mirror of
https://github.com/wireadmin/wireadmin
synced 2025-02-26 05:48:44 +00:00
updates WireGuard
lib for creating peers
This commit is contained in:
parent
2b6083a692
commit
4e4449435d
@ -1,7 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { IPV4_REGEX } from "@lib/constants";
|
||||
import { isBetween, isPrivateIP } from "@lib/utils";
|
||||
import { ZodErrorMap } from "zod/lib/ZodError";
|
||||
|
||||
export const NameSchema = z
|
||||
.string()
|
||||
@ -50,6 +49,10 @@ export const ServerId = z
|
||||
.string()
|
||||
.uuid({ message: 'Server ID must be a valid UUID' })
|
||||
|
||||
export const ClientId = z
|
||||
.string()
|
||||
.uuid({ message: 'Client ID must be a valid UUID' })
|
||||
|
||||
export const ServerStatusSchema = z
|
||||
.enum([ 'up', 'down' ], {
|
||||
errorMap: issue => {
|
||||
|
@ -2,10 +2,12 @@ import { z } from "zod";
|
||||
import type React from "react";
|
||||
import { IPV4_REGEX } from "@lib/constants";
|
||||
import { NextApiRequest as TNextApiRequest } from "next/dist/shared/lib/utils";
|
||||
import { NameSchema } from "@lib/schemas/WireGuard";
|
||||
|
||||
export const WgKeySchema = z.object({
|
||||
privateKey: z.string(),
|
||||
publicKey: z.string(),
|
||||
preSharedKey: z.string(),
|
||||
})
|
||||
|
||||
export type WgKey = z.infer<typeof WgKeySchema>
|
||||
@ -32,7 +34,7 @@ export const WgServerSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
confId: z.number(),
|
||||
type: z.enum([ 'direct', 'bridge', 'tor' ]),
|
||||
name: z.string().regex(/^[A-Za-z\d\s]{3,32}$/),
|
||||
name: NameSchema,
|
||||
address: z.string().regex(IPV4_REGEX),
|
||||
listen: z.number(),
|
||||
preUp: z.string().nullable(),
|
||||
@ -40,17 +42,21 @@ export const WgServerSchema = z.object({
|
||||
preDown: z.string().nullable(),
|
||||
postDown: z.string().nullable(),
|
||||
dns: z.string().regex(IPV4_REGEX).nullable(),
|
||||
peers: z.array(z.object({
|
||||
publicKey: z.string(),
|
||||
preSharedKey: z.string().nullable(),
|
||||
allowedIps: z.string().regex(IPV4_REGEX),
|
||||
persistentKeepalive: z.number().nullable(),
|
||||
})),
|
||||
peers: z.array(
|
||||
z.object({
|
||||
id: z.string().uuid(),
|
||||
name: NameSchema,
|
||||
preSharedKey: z.string().nullable(),
|
||||
allowedIps: z.string().regex(IPV4_REGEX),
|
||||
persistentKeepalive: z.number().nullable(),
|
||||
})
|
||||
.merge(WgKeySchema)
|
||||
),
|
||||
createdAt: z.string().datetime(),
|
||||
updatedAt: z.string().datetime(),
|
||||
status: z.enum([ 'up', 'down' ]),
|
||||
})
|
||||
.merge(WgKeySchema)
|
||||
.merge(WgKeySchema.omit({ preSharedKey: true }))
|
||||
|
||||
export type WgServer = z.infer<typeof WgServerSchema>
|
||||
|
||||
|
55
src/lib/wireguard-utils.ts
Normal file
55
src/lib/wireguard-utils.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { WgServer } from "@lib/typings";
|
||||
|
||||
export function getServerConf(server: WgServer): string {
|
||||
const lines = [
|
||||
'# Autogenerated by WireGuard UI (WireAdmin)',
|
||||
'[Interface]',
|
||||
`PrivateKey = ${server.privateKey}`,
|
||||
`Address = ${server.address}/24`,
|
||||
`ListenPort = ${server.listen}`,
|
||||
`${server.dns ? `DNS = ${server.dns}` : 'OMIT'}`,
|
||||
'',
|
||||
`${server.preUp ? `PreUp = ${server.preUp}` : 'OMIT'}`,
|
||||
`${server.postUp ? `PostUp = ${server.postUp}` : 'OMIT'}`,
|
||||
`${server.preDown ? `PreDown = ${server.preDown}` : 'OMIT'}`,
|
||||
`${server.postDown ? `PostDown = ${server.postDown}` : 'OMIT'}`,
|
||||
...server.peers.map((peer, index) => ([
|
||||
'',
|
||||
`## ${peer.name || `Peer #${index + 1}`}`,
|
||||
'[Peer]',
|
||||
`PublicKey = ${peer.publicKey}`,
|
||||
`${peer.preSharedKey ? `PresharedKey = ${peer.preSharedKey}` : 'OMIT'}`,
|
||||
`AllowedIPs = ${peer.allowedIps}/32`,
|
||||
`${peer.persistentKeepalive ? `PersistentKeepalive = ${peer.persistentKeepalive}` : 'OMIT'}`
|
||||
]))
|
||||
]
|
||||
return lines
|
||||
.filter((l) => l !== 'OMIT')
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
type Peer = WgServer['peers'][0]
|
||||
|
||||
interface GenPeerConParams extends Peer {
|
||||
serverAddress?: string
|
||||
port: number
|
||||
}
|
||||
|
||||
export function getPeerConf(params: GenPeerConParams): string {
|
||||
const lines = [
|
||||
'# Autogenerated by WireGuard UI (WireAdmin)',
|
||||
'[Interface]',
|
||||
`PrivateKey = ${params.privateKey}`,
|
||||
`Address = ${params.allowedIps}/32`,
|
||||
'',
|
||||
'[Peer]',
|
||||
`PublicKey = ${params.publicKey}`,
|
||||
`${params.preSharedKey ? `PresharedKey = ${params.preSharedKey}` : 'OMIT'}`,
|
||||
`AllowedIPs = ${params.allowedIps}/32`,
|
||||
`PersistentKeepalive = ${params.persistentKeepalive}`,
|
||||
`Endpoint = ${params.serverAddress || process.env.NEXT_PUBLIC_WG_HOST}:${params.port}`,
|
||||
]
|
||||
return lines
|
||||
.filter((l) => l !== 'OMIT')
|
||||
.join('\n')
|
||||
}
|
@ -6,6 +6,7 @@ import { WgKey, WgServer } from "@lib/typings";
|
||||
import { client, WG_SEVER_PATH } from "@lib/redis";
|
||||
import { isJson } from "@lib/utils";
|
||||
import deepmerge from "deepmerge";
|
||||
import { getPeerConf, getServerConf } from "@lib/wireguard-utils";
|
||||
|
||||
export class WGServer {
|
||||
|
||||
@ -63,7 +64,10 @@ export class WGServer {
|
||||
console.warn('findServerIndex: index not found')
|
||||
return true
|
||||
}
|
||||
const res = await client.lset(WG_SEVER_PATH, index, JSON.stringify(deepmerge(server, update)))
|
||||
const res = await client.lset(WG_SEVER_PATH, index, JSON.stringify({
|
||||
...deepmerge(server, update),
|
||||
updatedAt: new Date().toISOString()
|
||||
}))
|
||||
return res === 'OK'
|
||||
}
|
||||
|
||||
@ -109,14 +113,23 @@ export class WGServer {
|
||||
return false
|
||||
}
|
||||
const peers = await wgPeersStr(server.confId)
|
||||
const peerIndex = peers.findIndex((p) => p
|
||||
.replace(/\s/g, '')
|
||||
.includes(`PublicKey=${publicKey}`)
|
||||
)
|
||||
|
||||
const index = await findServerIndex(id)
|
||||
if (typeof index !== 'number') {
|
||||
console.warn('findServerIndex: index not found')
|
||||
return true
|
||||
}
|
||||
await client.lset(WG_SEVER_PATH, index, JSON.stringify({
|
||||
...server,
|
||||
peers: server.peers.filter((p) => p.publicKey !== publicKey)
|
||||
}))
|
||||
|
||||
const peerIndex = peers.findIndex((p) => p.includes(`PublicKey = ${publicKey}`))
|
||||
if (peerIndex === -1) {
|
||||
console.warn('no peer found')
|
||||
console.warn('removePeer: no peer found')
|
||||
return false
|
||||
}
|
||||
|
||||
const confPath = path.join(WG_PATH, `wg${server.confId}.conf`)
|
||||
const conf = await fs.readFile(confPath, 'utf-8')
|
||||
const serverConfStr = conf.includes('[Peer]') ?
|
||||
@ -124,14 +137,46 @@ export class WGServer {
|
||||
conf
|
||||
const peersStr = peers.filter((_, i) => i !== peerIndex).join('\n')
|
||||
await fs.writeFile(confPath, `${serverConfStr}\n${peersStr}`)
|
||||
await WGServer.update(server.id, {
|
||||
peers: server.peers.filter((_, i) => i !== peerIndex)
|
||||
})
|
||||
|
||||
await WGServer.stop(server.id)
|
||||
await WGServer.start(server.id)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static async getFreePeerIp(id: string): Promise<string | undefined> {
|
||||
const server = await findServer(id)
|
||||
if (!server) {
|
||||
console.error('getFreePeerIp: server not found')
|
||||
return undefined
|
||||
}
|
||||
const reservedIps = server.peers.map((p) => p.allowedIps)
|
||||
const ips = reservedIps.map((ip) => ip.split('/')[0])
|
||||
const net = server.address.split('/')[0].split('.')
|
||||
for (let i = 1; i < 255; i++) {
|
||||
const ip = `${net[0]}.${net[1]}.${net[2]}.${i}`
|
||||
if (!ips.includes(ip) && ip !== server.address.split('/')[0]) {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
console.error('getFreePeerIp: no free ip found')
|
||||
return undefined
|
||||
}
|
||||
|
||||
static async generatePeerConfig(id: string, peerId: string): Promise<string | undefined> {
|
||||
const server = await findServer(id)
|
||||
if (!server) {
|
||||
console.error('generatePeerConfig: server not found')
|
||||
return undefined
|
||||
}
|
||||
const peer = server.peers.find((p) => p.id === peerId)
|
||||
if (!peer) {
|
||||
console.error('generatePeerConfig: peer not found')
|
||||
return undefined
|
||||
}
|
||||
return getPeerConf({ ...peer, port: server.listen })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,8 +216,11 @@ export async function readWgConf(configId: number): Promise<WgServer> {
|
||||
if (reachedPeers) {
|
||||
if (key === '[Peer]') {
|
||||
server.peers.push({
|
||||
id: crypto.randomUUID(),
|
||||
name: `Unknown #${server.peers.length + 1}`,
|
||||
publicKey: '',
|
||||
preSharedKey: null,
|
||||
privateKey: '', // it's okay to be empty because, we not using it on server
|
||||
preSharedKey: '',
|
||||
allowedIps: '',
|
||||
persistentKeepalive: null
|
||||
})
|
||||
@ -268,7 +316,8 @@ async function wgPeersStr(configId: number): Promise<string[]> {
|
||||
export async function generateWgKey(): Promise<WgKey> {
|
||||
const privateKey = await Shell.exec('wg genkey');
|
||||
const publicKey = await Shell.exec(`echo ${privateKey} | wg pubkey`);
|
||||
return { privateKey, publicKey }
|
||||
const preSharedKey = await Shell.exec('wg genkey');
|
||||
return { privateKey, publicKey, preSharedKey }
|
||||
}
|
||||
|
||||
export async function generateWgServer(config: {
|
||||
@ -386,34 +435,6 @@ export async function dropInterface(configId: number) {
|
||||
await Shell.exec(`ip link delete dev wg${configId}`, true)
|
||||
}
|
||||
|
||||
export function getServerConf(server: WgServer): string {
|
||||
return `
|
||||
# Autogenerated by WireGuard UI (WireAdmin)
|
||||
[Interface]
|
||||
PrivateKey = ${server.privateKey}
|
||||
Address = ${server.address}/24
|
||||
ListenPort = ${server.listen}
|
||||
${server.dns ? `DNS = ${server.dns}` : ''}
|
||||
|
||||
PreUp = ${server.preUp}
|
||||
PostUp = ${server.postUp}
|
||||
PreDown = ${server.preDown}
|
||||
PostDown = ${server.postDown}
|
||||
|
||||
${server.peers.map(getPeerConf).join('\n')}
|
||||
`
|
||||
}
|
||||
|
||||
export function getPeerConf(peer: WgServer['peers'][0]): string {
|
||||
return `
|
||||
[Peer]
|
||||
PublicKey = ${peer.publicKey}
|
||||
${peer.preSharedKey ? `PresharedKey = ${peer.preSharedKey}` : ''}
|
||||
AllowedIPs = ${peer.allowedIps}/32
|
||||
${peer.persistentKeepalive ? `PersistentKeepalive = ${peer.persistentKeepalive}` : ''}
|
||||
`
|
||||
}
|
||||
|
||||
export async function maxConfId(): Promise<number> {
|
||||
// get files in /etc/wireguard
|
||||
const files = await fs.readdir(WG_PATH)
|
||||
|
Loading…
Reference in New Issue
Block a user