This commit is contained in:
Shahrad Elahi 2023-09-26 07:28:15 +03:30
parent 440a6189bd
commit c521faf7df
10 changed files with 57 additions and 36 deletions

View File

@ -4,9 +4,6 @@ services:
image: wireadmin image: wireadmin
volumes: volumes:
- ./src/:/app/ - ./src/:/app/
ports:
- "4040:4040/udp" # Direct
- "4050:4050/udp" # Tor
environment: environment:
- UI_PASSWORD=password - UI_PASSWORD=password
- WG_HOST=127.0.0.1 - WG_HOST=192.168.1.102

View File

@ -19,12 +19,10 @@ fi
screen -dmS redis bash -c "redis-server --port 6479 --daemonize no --dir /data --appendonly yes" screen -dmS redis bash -c "redis-server --port 6479 --daemonize no --dir /data --appendonly yes"
# Start Tor in the background # Start Tor in the background
screen -dmS tor bash -c "tor -f /etc/tor/torrc" screen -dmS tor bash -c "sleep 1; tor -f /etc/tor/torrc"
# If WG_HOST exists, again export it as NEXT_PUBLIC_WG_HOST # If WG_HOST exists, again export it as NEXT_PUBLIC_WG_HOST
if [ ! -z "$WG_HOST" ]; then export NEXT_PUBLIC_WG_HOST="$WG_HOST"
export NEXT_PUBLIC_WG_HOST=$WG_HOST
fi
# After 5 seconds, export the database to the WireGuard config file # After 5 seconds, export the database to the WireGuard config file
screen -dm bash -c "sleep 5; curl -s -o /dev/null http://127.0.0.1:3000/api/wireguard/regen" screen -dm bash -c "sleep 5; curl -s -o /dev/null http://127.0.0.1:3000/api/wireguard/regen"

View File

@ -29,7 +29,7 @@ export const PortSchema = z
message: 'Port must be a valid port number' message: 'Port must be a valid port number'
}) })
export const TypeSchema = z.enum([ 'default', 'tor' ]) export const TypeSchema = z.enum([ 'direct', 'tor' ])
export const DnsSchema = z export const DnsSchema = z
.string() .string()

View File

@ -30,8 +30,9 @@ export function getServerConf(server: WgServer): string {
type Peer = WgServer['peers'][0] type Peer = WgServer['peers'][0]
interface GenPeerConParams extends Peer { interface GenPeerConParams extends Peer, Pick<WgServer, 'dns'> {
serverAddress?: string serverAddress?: string
serverPublicKey: string
port: number port: number
} }
@ -40,12 +41,13 @@ export function getPeerConf(params: GenPeerConParams): string {
'# Autogenerated by WireGuard UI (WireAdmin)', '# Autogenerated by WireGuard UI (WireAdmin)',
'[Interface]', '[Interface]',
`PrivateKey = ${params.privateKey}`, `PrivateKey = ${params.privateKey}`,
`Address = ${params.allowedIps}/32`, `Address = ${params.allowedIps}/24`,
`${params.dns ? `DNS = ${params.dns}` : 'OMIT'}`,
'', '',
'[Peer]', '[Peer]',
`PublicKey = ${params.publicKey}`, `PublicKey = ${params.serverPublicKey}`,
`${params.preSharedKey ? `PresharedKey = ${params.preSharedKey}` : 'OMIT'}`, `${params.preSharedKey ? `PresharedKey = ${params.preSharedKey}` : 'OMIT'}`,
`AllowedIPs = ${params.allowedIps}/32`, `AllowedIPs = 0.0.0.0/0, ::/0`,
`PersistentKeepalive = ${params.persistentKeepalive}`, `PersistentKeepalive = ${params.persistentKeepalive}`,
`Endpoint = ${params.serverAddress || process.env.NEXT_PUBLIC_WG_HOST}:${params.port}`, `Endpoint = ${params.serverAddress || process.env.NEXT_PUBLIC_WG_HOST}:${params.port}`,
] ]

View File

@ -16,7 +16,10 @@ export class WGServer {
console.error('server could not be updated (reason: not exists)') console.error('server could not be updated (reason: not exists)')
return false return false
} }
await Shell.exec(`ip link set down dev wg${server.confId}`, true)
await Shell.exec(`wg-quick down wg${server.confId}`, true)
await dropInterface(server.confId)
await this.update(id, { status: 'down' }) await this.update(id, { status: 'down' })
return true return true
} }
@ -27,8 +30,10 @@ export class WGServer {
console.error('server could not be updated (reason: not exists)') console.error('server could not be updated (reason: not exists)')
return false return false
} }
await createInterface(server.confId, server.address)
await Shell.exec(`ip link set up dev wg${server.confId}`) await Shell.exec(`wg-quick down wg${server.confId}`, true)
await Shell.exec(`wg-quick up wg${server.confId}`)
await this.update(id, { status: 'up' }) await this.update(id, { status: 'up' })
return true return true
} }
@ -189,7 +194,12 @@ export class WGServer {
console.error('generatePeerConfig: peer not found') console.error('generatePeerConfig: peer not found')
return undefined return undefined
} }
return getPeerConf({ ...peer, port: server.listen }) return getPeerConf({
...peer,
serverPublicKey: server.publicKey,
port: server.listen,
dns: server.dns
})
} }
} }
@ -402,16 +412,14 @@ export async function generateWgServer(config: {
}) })
// to ensure interface does not exists // to ensure interface does not exists
await Shell.exec(`wg-quick down wg${confId}`, true)
await dropInterface(confId) await dropInterface(confId)
await Shell.exec(`ip link set down dev wg${confId}`, true)
// create a interface // create a interface
await createInterface(confId, config.address) // await createInterface(confId, config.address)
// restart WireGuard // restart WireGuard
await Shell.exec(`wg setconf wg${confId} ${CONFIG_PATH}`) await Shell.exec(`wg-quick up wg${confId}`)
await Shell.exec(`ip link set up dev wg${confId}`)
// return server id // return server id
return uuid return uuid
@ -502,7 +510,7 @@ async function makeWgIptables(s: WgServer): Promise<{
up: string up: string
down: string down: string
}> { }> {
const inet = Shell.exec('ip route | grep default | grep -oP "(?<=dev )[^ ]+"') const inet = await Shell.exec('ip route list default | awk \'{print $5}\'')
const wgAddress = `${s.address}/24` const wgAddress = `${s.address}/24`
const wgInet = `wg${s.confId}` const wgInet = `wg${s.confId}`

View File

@ -9,7 +9,9 @@ function publicENV(ex = {}) {
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
transpilePackages: [], transpilePackages: [],
env: publicENV() env: publicENV({
NEXT_PUBLIC_WG_HOST: process.env.WG_HOST
})
} }
module.exports = nextConfig; module.exports = nextConfig;

View File

@ -190,6 +190,8 @@ export default function ServerPage(props: PageProps) {
key={s.id} key={s.id}
{...s} {...s}
serverId={props.serverId} serverId={props.serverId}
serverPublicKey={data?.publicKey}
dns={data?.dns}
listenPort={data?.listen} listenPort={data?.listen}
refreshTrigger={() => refresh()} refreshTrigger={() => refresh()}
/> />
@ -214,8 +216,9 @@ export default function ServerPage(props: PageProps) {
type Peer = WgServer['peers'][0] type Peer = WgServer['peers'][0]
interface ClientProps extends Peer { interface ClientProps extends Peer, Pick<WgServer, 'dns'> {
serverId: string serverId: string
serverPublicKey: string
listenPort: number listenPort: number
refreshTrigger: () => void refreshTrigger: () => void
} }
@ -229,7 +232,9 @@ function Client(props: ClientProps) {
React.useEffect(() => { React.useEffect(() => {
setConf(getPeerConf({ setConf(getPeerConf({
...props, ...props,
port: props.listenPort serverPublicKey: props.serverPublicKey,
port: props.listenPort,
dns: props.dns,
})) }))
console.log('conf', conf) console.log('conf', conf)
}, [ props ]) }, [ props ])
@ -254,12 +259,18 @@ function Client(props: ClientProps) {
return ( return (
<List.Item key={props.id} className={'flex items-center justify-between p-4'}> <List.Item key={props.id} className={'flex items-center justify-between p-4'}>
<QRCodeModal ref={qrcodeRef} content={conf?.trim() || 'null'} /> <QRCodeModal ref={qrcodeRef} content={conf?.trim() || 'null'} />
<div className={'w-full grid grid-cols-12 items-center gap-x-2'}> <div className={'w-full flex flex-row items-center gap-x-2'}>
<div className={'col-span-12 md:col-span-4'}> <div className={'w-12 aspect-square flex items-center justify-center mr-4 rounded-full bg-gray-200'}>
<span className={'inline-block font-medium'}> {props.name} </span> {/* User Icon */}
<i className={'fas fa-user text-gray-400 text-lg'} />
</div> </div>
<div className={'col-span-12 md:col-span-4'}> <div className={'col-span-12 md:col-span-4'}>
<span className={'font-mono text-gray-400 text-sm'}> {props.allowedIps} </span> <div className={'col-span-12 md:col-span-4'}>
<span className={'inline-block font-medium'}> {props.name} </span>
</div>
<div className={'col-span-12 md:col-span-4'}>
<span className={'font-mono text-gray-400 text-xs'}> {props.allowedIps} </span>
</div>
</div> </div>
</div> </div>
<div className={'flex items-center justify-center gap-x-3'}> <div className={'flex items-center justify-center gap-x-3'}>
@ -310,7 +321,7 @@ function ClientBaseButton(props: {
<div <div
className={twMerge( className={twMerge(
'group flex items-center justify-center w-10 aspect-square rounded-md', 'group flex items-center justify-center w-10 aspect-square rounded-md',
'bg-gray-200/80 hover:bg-gray-100', 'bg-gray-200/80 hover:bg-gray-100/50',
'border border-transparent hover:border-primary', 'border border-transparent hover:border-primary',
'transition-colors duration-200 ease-in-out', 'transition-colors duration-200 ease-in-out',
'cursor-pointer', 'cursor-pointer',

View File

@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
publicKey: peerKeys.publicKey, publicKey: peerKeys.publicKey,
privateKey: peerKeys.privateKey, privateKey: peerKeys.privateKey,
preSharedKey: peerKeys.preSharedKey, preSharedKey: peerKeys.preSharedKey,
persistentKeepalive: 25, persistentKeepalive: 0,
}) })
if (!addedPeer) { if (!addedPeer) {
return res return res

View File

@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next' import type { NextApiRequest, NextApiResponse } from 'next'
import safeServe from "@lib/safe-serve"; import safeServe from "@lib/safe-serve";
import { getServers } from "@lib/wireguard"; import { getServers, WGServer } from "@lib/wireguard";
import { getServerConf } from "@lib/wireguard-utils"; import { getServerConf } from "@lib/wireguard-utils";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
@ -17,10 +17,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const servers = await getServers() const servers = await getServers()
servers.forEach((s) => { for (const s of servers) {
const CONFIG_PATH = path.join(WG_PATH, `wg${s.confId}.conf`) const CONFIG_PATH = path.join(WG_PATH, `wg${s.confId}.conf`)
fs.writeFileSync(CONFIG_PATH, getServerConf(s), { mode: 0o600 }) fs.writeFileSync(CONFIG_PATH, getServerConf(s), { mode: 0o600 })
}) await WGServer.start(s.id)
}
return res return res
.status(200) .status(200)

View File

@ -24,7 +24,7 @@ const CreateServerModal = React.forwardRef<
const innerRef = React.useRef<SmartModalRef | null>(null) const innerRef = React.useRef<SmartModalRef | null>(null)
const [ form ] = Form.useForm() const [ form ] = Form.useForm()
const [ type, setType ] = React.useState<'default' | 'tor'>('default') const [ type, setType ] = React.useState<ServerType>('direct')
React.useImperativeHandle(ref, () => innerRef.current as SmartModalRef) React.useImperativeHandle(ref, () => innerRef.current as SmartModalRef)
@ -206,4 +206,6 @@ const FormSchema = z.object({
mtu: MtuSchema mtu: MtuSchema
}) })
type ServerType = z.infer<typeof TypeSchema>
type FormValues = z.infer<typeof FormSchema> type FormValues = z.infer<typeof FormSchema>