mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-28 21:58:15 +02:00
Still working on stability
This commit is contained in:
parent
e2efd0e65a
commit
f6a19631dc
6 changed files with 128 additions and 86 deletions
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
endpoint: site.endpoint,
|
||||||
|
publicKey: site.publicKey,
|
||||||
serverIP: site.address,
|
serverIP: site.address,
|
||||||
serverPort: site.listenPort
|
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
|
||||||
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -61,10 +61,13 @@ 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}`);
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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,45 +698,6 @@ 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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue