Still working on stability

This commit is contained in:
Owen 2025-04-20 14:25:53 -04:00
parent e2efd0e65a
commit f6a19631dc
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
6 changed files with 128 additions and 86 deletions

View file

@ -10,7 +10,8 @@ import {
olms, olms,
clientSites, clientSites,
exitNodes, exitNodes,
orgs orgs,
sites
} from "@server/db/schema"; } from "@server/db/schema";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@ -115,13 +116,28 @@ export async function createClient(
const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`; // we want the block size of the whole org const updatedSubnet = `${subnet}/${org.subnet.split("/")[1]}`; // we want the block size of the whole org
// make sure the subnet is unique // make sure the subnet is unique
const subnetExists = await db const subnetExistsClients = await db
.select() .select()
.from(clients) .from(clients)
.where(eq(clients.subnet, updatedSubnet)) .where(eq(clients.subnet, updatedSubnet))
.limit(1); .limit(1);
if (subnetExists.length > 0) { if (subnetExistsClients.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
`Subnet ${subnet} already exists`
)
);
}
const subnetExistsSites = await db
.select()
.from(sites)
.where(eq(sites.address, updatedSubnet))
.limit(1);
if (subnetExistsSites.length > 0) {
return next( return next(
createHttpError( createHttpError(
HttpCode.CONFLICT, HttpCode.CONFLICT,

View file

@ -3,18 +3,9 @@ import { MessageHandler } from "../ws";
import logger from "@server/logger"; import logger from "@server/logger";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import db from "@server/db"; import db from "@server/db";
import { import { clients, clientSites, Newt, sites } from "@server/db/schema";
clients,
clientSites,
Newt,
Site,
sites,
olms
} from "@server/db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { getNextAvailableClientSubnet } from "@server/lib/ip"; import { updatePeer } from "../olm/peers";
import config from "@server/lib/config";
import { addPeer } from "../olm/peers";
const inputSchema = z.object({ const inputSchema = z.object({
publicKey: z.string(), publicKey: z.string(),
@ -27,7 +18,7 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
const { message, client, sendToClient } = context; const { message, client, sendToClient } = context;
const newt = client as Newt; const newt = client as Newt;
logger.debug(JSON.stringify(message.data)); const now = new Date().getTime() / 1000;
logger.debug("Handling Newt get config message!"); logger.debug("Handling Newt get config message!");
@ -63,6 +54,19 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
logger.warn("handleGetConfigMessage: Site not found"); logger.warn("handleGetConfigMessage: Site not found");
return; return;
} }
// todo check if the public key has changed
// we need to wait for hole punch success
if (!existingSite.endpoint) {
logger.warn(`Site ${existingSite.siteId} has no endpoint, skipping`);
return;
}
if (existingSite.lastHolePunch && now - existingSite.lastHolePunch > 6) {
logger.warn(
`Site ${existingSite.siteId} last hole punch is too old, skipping`
);
return;
}
// update the endpoint and the public key // update the endpoint and the public key
const [site] = await db const [site] = await db
@ -106,29 +110,30 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
return true; return true;
}) })
.map(async (client) => { .map(async (client) => {
const peerData = {
publicKey: client.clients.pubKey!,
allowedIps: [client.clients.subnet!],
endpoint: client.clientSites.isRelayed
? ""
: client.clients.endpoint! // if its relayed it should be localhost
};
// Add or update this peer on the olm if it is connected // Add or update this peer on the olm if it is connected
try { try {
await addPeer(client.clients.clientId, { if (site.endpoint && site.publicKey) {
...peerData, await updatePeer(client.clients.clientId, {
siteId: siteId, siteId: site.siteId,
serverIP: site.address, endpoint: site.endpoint,
serverPort: site.listenPort publicKey: site.publicKey,
}); serverIP: site.address,
serverPort: site.listenPort
});
}
} catch (error) { } catch (error) {
logger.error( logger.error(
`Failed to add/update peer ${client.clients.pubKey} to newt ${newt.newtId}: ${error}` `Failed to add/update peer ${client.clients.pubKey} to newt ${newt.newtId}: ${error}`
); );
} }
return peerData; return {
publicKey: client.clients.pubKey!,
allowedIps: [client.clients.subnet!],
endpoint: client.clientSites.isRelayed
? ""
: client.clients.endpoint! // if its relayed it should be localhost
};
}) })
); );

View file

@ -1,6 +1,6 @@
import db from "@server/db"; import db from "@server/db";
import { MessageHandler } from "../ws"; import { MessageHandler } from "../ws";
import { clients, clientSites, Olm, olms, sites } from "@server/db/schema"; import { clients, clientSites, Olm } from "@server/db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { updatePeer } from "../newt/peers"; import { updatePeer } from "../newt/peers";
import logger from "@server/logger"; import logger from "@server/logger";
@ -42,6 +42,13 @@ export const handleOlmRelayMessage: MessageHandler = async (context) => {
const { siteId } = message.data; const { siteId } = message.data;
await db
.update(clientSites)
.set({
isRelayed: true
})
.where(eq(clientSites.clientId, olm.clientId));
// update the peer on the exit node // update the peer on the exit node
await updatePeer(siteId, client.pubKey, { await updatePeer(siteId, client.pubKey, {
endpoint: "" // this removes the endpoint endpoint: "" // this removes the endpoint

View file

@ -7,7 +7,6 @@ import logger from '@server/logger';
export async function addPeer(clientId: number, peer: { export async function addPeer(clientId: number, peer: {
siteId: number, siteId: number,
publicKey: string; publicKey: string;
allowedIps: string[];
endpoint: string; endpoint: string;
serverIP: string | null; serverIP: string | null;
serverPort: number | null; serverPort: number | null;
@ -20,8 +19,8 @@ export async function addPeer(clientId: number, peer: {
sendToClient(olm.olmId, { sendToClient(olm.olmId, {
type: 'olm/wg/peer/add', type: 'olm/wg/peer/add',
data: { data: {
siteId: peer.siteId,
publicKey: peer.publicKey, publicKey: peer.publicKey,
allowedIps: peer.allowedIps,
endpoint: peer.endpoint, endpoint: peer.endpoint,
serverIP: peer.serverIP, serverIP: peer.serverIP,
serverPort: peer.serverPort serverPort: peer.serverPort
@ -47,11 +46,12 @@ export async function deletePeer(clientId: number, publicKey: string) {
logger.info(`Deleted peer ${publicKey} from olm ${olm.olmId}`); logger.info(`Deleted peer ${publicKey} from olm ${olm.olmId}`);
} }
export async function updatePeer(clientId: number, publicKey: string, peer: { export async function updatePeer(clientId: number, peer: {
allowedIps?: string[]; siteId: number,
endpoint?: string; publicKey: string;
serverIP?: string; endpoint: string;
serverPort?: number; serverIP: string | null;
serverPort: number | null;
}) { }) {
const [olm] = await db.select().from(olms).where(eq(olms.clientId, clientId)).limit(1); const [olm] = await db.select().from(olms).where(eq(olms.clientId, clientId)).limit(1);
if (!olm) { if (!olm) {
@ -59,12 +59,15 @@ export async function updatePeer(clientId: number, publicKey: string, peer: {
} }
sendToClient(olm.olmId, { sendToClient(olm.olmId, {
type: 'olm/wg/peer/update', type: 'olm/wg/peer/update',
data: { data: {
publicKey, siteId: peer.siteId,
...peer publicKey: peer.publicKey,
endpoint: peer.endpoint,
serverIP: peer.serverIP,
serverPort: peer.serverPort
} }
}); });
logger.info(`Updated peer ${publicKey} on olm ${olm.olmId}`); logger.info(`Added peer ${peer.publicKey} to olm ${olm.olmId}`);
} }

View file

@ -128,14 +128,16 @@ export async function createSite(
); );
} }
updatedAddress = `${address}/${org.subnet.split("/")[1]}`; // we want the block size of the whole org
// make sure the subnet is unique // make sure the subnet is unique
const addressExists = await db const addressExistsSites = await db
.select() .select()
.from(sites) .from(sites)
.where(eq(sites.address, address)) .where(eq(sites.address, updatedAddress))
.limit(1); .limit(1);
if (addressExists.length > 0) { if (addressExistsSites.length > 0) {
return next( return next(
createHttpError( createHttpError(
HttpCode.CONFLICT, HttpCode.CONFLICT,
@ -144,7 +146,19 @@ export async function createSite(
); );
} }
updatedAddress = `${address}/${org.subnet.split("/")[1]}`; // we want the block size of the whole org const addressExistsClients = await db
.select()
.from(sites)
.where(eq(sites.subnet, updatedAddress))
.limit(1);
if (addressExistsClients.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
`Subnet ${subnet} already exists`
)
);
}
} }
const niceId = await getUniqueSiteName(orgId); const niceId = await getUniqueSiteName(orgId);

View file

@ -544,6 +544,42 @@ PersistentKeepalive = 5`;
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="clientAddress"
render={({ field }) => (
<FormItem>
<FormLabel>
Client Address
</FormLabel>
<FormControl>
<Input
autoComplete="off"
value={
clientAddress
}
onChange={(
e
) => {
setClientAddress(
e.target
.value
);
field.onChange(
e.target
.value
);
}}
/>
</FormControl>
<FormMessage />
<FormDescription>
Specify the IP
address of the host.
</FormDescription>
</FormItem>
)}
/>
</form> </form>
</Form> </Form>
</SettingsSectionForm> </SettingsSectionForm>
@ -662,50 +698,11 @@ PersistentKeepalive = 5`;
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="clientAddress"
render={({ field }) => (
<FormItem>
<FormLabel>
Client Address
</FormLabel>
<FormControl>
<Input
autoComplete="off"
value={
clientAddress
}
onChange={(
e
) => {
setClientAddress(
e
.target
.value
);
field.onChange(
e
.target
.value
);
}}
/>
</FormControl>
<FormMessage />
<FormDescription>
Specify the IP
address of the
host.
</FormDescription>
</FormItem>
)}
/>
</form> </form>
</Form> </Form>
</SettingsSectionBody> </SettingsSectionBody>
</SettingsSection> </SettingsSection>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle> <SettingsSectionTitle>
Install Newt Install Newt