Implement wireguard client setup with auto key generation and host discovery
This commit is contained in:
@@ -1,9 +1,24 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to grab the host's network interface
|
* Function to grab the host's network interface
|
||||||
* eg `eth0`
|
* eg `eth0`
|
||||||
* @param param0
|
* @param param0
|
||||||
*/
|
*/
|
||||||
export default async function grabHostNetworkInterface() {
|
export default async function grabHostNetworkInterface() {
|
||||||
// Placeholder
|
try {
|
||||||
|
const route = execSync(`ip -4 route show default`, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
})
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/);
|
||||||
|
|
||||||
|
const deviceIndex = route.indexOf(`dev`);
|
||||||
|
|
||||||
|
if (deviceIndex > -1 && route[deviceIndex + 1]) {
|
||||||
|
return route[deviceIndex + 1];
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
return `eth0`;
|
return `eth0`;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,33 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to grab the host's public
|
* Function to grab the host's public
|
||||||
* IP address
|
* IP address
|
||||||
* @param param0
|
* @param param0
|
||||||
*/
|
*/
|
||||||
export default async function grabHostPublicIPAddress() {
|
export default async function grabHostPublicIPAddress() {
|
||||||
// Placeholder
|
try {
|
||||||
return `102.34.765.43`;
|
const public_ip = execSync(`curl -sS --max-time 15 https://api.ipify.org`, {
|
||||||
}
|
encoding: "utf-8",
|
||||||
|
})
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(public_ip)) {
|
||||||
|
return public_ip;
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const local_ips = execSync(`hostname -I`, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
})
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/);
|
||||||
|
|
||||||
|
if (local_ips[0]) {
|
||||||
|
return local_ips[0];
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
return ``;
|
||||||
|
}
|
||||||
@@ -7,14 +7,19 @@ import { AppData } from "@/src/data/app-data";
|
|||||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import grabHostNetworkInterface from "./grab-host-network-interface";
|
import grabHostPublicIPAddress from "./grab-host-public-ip-address";
|
||||||
|
import setupWireguardHost from "./setup-wireguard-host";
|
||||||
import type { APIResponseObject } from "@moduletrace/bunext/types";
|
import type { APIResponseObject } from "@moduletrace/bunext/types";
|
||||||
|
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||||
|
import type { TableType } from "@/src/types";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
WGUI_LIB_IP_TABLES_DIR,
|
WGUI_LIB_CLIENTS_CONFIGS_DIR,
|
||||||
WIREGUARD_HOST_CONFIG_DIR,
|
WIREGUARD_HOST_CONFIG_DIR,
|
||||||
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
||||||
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
|
WGUI_LIB_KEYS_DIR,
|
||||||
|
WIREGUARD_CLIENT_CONFIG_FILE_NAME,
|
||||||
} = grabDirNames();
|
} = grabDirNames();
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
@@ -35,7 +40,145 @@ export default async function setupWireguardClient({
|
|||||||
host,
|
host,
|
||||||
variables,
|
variables,
|
||||||
}: Params): Promise<APIResponseObject> {
|
}: Params): Promise<APIResponseObject> {
|
||||||
return {
|
const host_id = host?.id || AppData["WireguardHostID"];
|
||||||
success: true,
|
|
||||||
};
|
const CLIENT_WG_IP = client?.wg_ip_address;
|
||||||
|
|
||||||
|
if (!CLIENT_WG_IP) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `No Client Private IP address provided`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const HOST_WG_IP =
|
||||||
|
host?.wg_ip_address ||
|
||||||
|
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value;
|
||||||
|
|
||||||
|
if (!HOST_WG_IP) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `No Host Private IP address provided`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLIENT_DIR = path.join(WGUI_LIB_CLIENTS_CONFIGS_DIR, `${client.id}`);
|
||||||
|
|
||||||
|
const CLIENT_PRIVATE_KEY_FILE = path.join(
|
||||||
|
CLIENT_DIR,
|
||||||
|
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
||||||
|
);
|
||||||
|
const CLIENT_PUBLIC_KEY_FILE = path.join(
|
||||||
|
CLIENT_DIR,
|
||||||
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
|
);
|
||||||
|
const CLIENT_CONFIG_FILE = path.join(
|
||||||
|
CLIENT_DIR,
|
||||||
|
WIREGUARD_CLIENT_CONFIG_FILE_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pre_sh = ``;
|
||||||
|
|
||||||
|
pre_sh += `mkdir -p ${CLIENT_DIR}\n`;
|
||||||
|
pre_sh += `if [ ! -f ${CLIENT_PRIVATE_KEY_FILE} ]; then\n`;
|
||||||
|
pre_sh += ` wg genkey | tee ${CLIENT_PRIVATE_KEY_FILE} | wg pubkey > ${CLIENT_PUBLIC_KEY_FILE}\n`;
|
||||||
|
pre_sh += `fi\n`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const exec_pre_setup = execSync(pre_sh, { encoding: "utf-8" });
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const CLIENT_PRIVATE_KEY =
|
||||||
|
client?.private_key ||
|
||||||
|
execSync(`cat ${CLIENT_PRIVATE_KEY_FILE}`, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
if (CLIENT_PRIVATE_KEY && !client.private_key && client.id) {
|
||||||
|
await BunSQLite.update<BUN_SQLITE_WGUI_CLIENTS, TableType>({
|
||||||
|
data: { private_key: CLIENT_PRIVATE_KEY },
|
||||||
|
table: "clients",
|
||||||
|
targetId: client.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLIENT_PUBLIC_KEY =
|
||||||
|
client?.public_key ||
|
||||||
|
execSync(`cat ${CLIENT_PUBLIC_KEY_FILE}`, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
if (CLIENT_PUBLIC_KEY && !client.public_key && client.id) {
|
||||||
|
await BunSQLite.update<BUN_SQLITE_WGUI_CLIENTS, TableType>({
|
||||||
|
data: { public_key: CLIENT_PUBLIC_KEY },
|
||||||
|
table: "clients",
|
||||||
|
targetId: client.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const HOST_PUBLIC_KEY =
|
||||||
|
host?.public_key ||
|
||||||
|
execSync(
|
||||||
|
`cat ${path.join(
|
||||||
|
WIREGUARD_HOST_CONFIG_DIR,
|
||||||
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
|
)}`,
|
||||||
|
{ encoding: "utf-8" },
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
const ALLOWED_IPS = client?.allowed_ips || `0.0.0.0/0`;
|
||||||
|
|
||||||
|
const PUBLIC_IP_ADDRESS =
|
||||||
|
(await grabHostPublicIPAddress()) ||
|
||||||
|
client?.public_ip_address ||
|
||||||
|
HOST_WG_IP;
|
||||||
|
|
||||||
|
const sh = `
|
||||||
|
cd ${CLIENT_DIR}
|
||||||
|
|
||||||
|
cat > ${WIREGUARD_CLIENT_CONFIG_FILE_NAME} << EOF
|
||||||
|
[Interface]
|
||||||
|
Address = ${CLIENT_WG_IP}/32
|
||||||
|
PrivateKey = ${CLIENT_PRIVATE_KEY}
|
||||||
|
DNS = 1.1.1.1
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = ${HOST_PUBLIC_KEY}
|
||||||
|
Endpoint = ${PUBLIC_IP_ADDRESS}:51820
|
||||||
|
AllowedIPs = ${ALLOWED_IPS}
|
||||||
|
EOF
|
||||||
|
`;
|
||||||
|
|
||||||
|
const exec = execSync(sh, { encoding: "utf-8" });
|
||||||
|
|
||||||
|
const CLIENT_CONFIG = execSync(`cat ${CLIENT_CONFIG_FILE}`, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
const host_setup_res = await setupWireguardHost({ host, variables });
|
||||||
|
|
||||||
|
if (!host_setup_res.success) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `Client config created but Host update failed: ${host_setup_res.msg}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
msg: `Client ${client.name} setup complete`,
|
||||||
|
stringRes: CLIENT_CONFIG,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
REQUIRED_TOOLS=(wg wg-quick)
|
REQUIRED_TOOLS=(wg wg-quick ip curl)
|
||||||
HAVE_ALL_TOOLS=true
|
HAVE_ALL_TOOLS=true
|
||||||
|
|
||||||
for tool in "${REQUIRED_TOOLS[@]}"; do
|
for tool in "${REQUIRED_TOOLS[@]}"; do
|
||||||
@@ -49,19 +49,19 @@ case "$DISTRO" in
|
|||||||
echo "detected Debian-family distro: $DISTRO"
|
echo "detected Debian-family distro: $DISTRO"
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
apt-get update -y
|
apt-get update -y
|
||||||
apt-get install -y wireguard
|
apt-get install -y wireguard iproute2 curl
|
||||||
;;
|
;;
|
||||||
fedora | rhel | centos | rocky | almalinux)
|
fedora | rhel | centos | rocky | almalinux)
|
||||||
echo "detected Fedora-family distro: $DISTRO"
|
echo "detected Fedora-family distro: $DISTRO"
|
||||||
dnf install -y wireguard-tools
|
dnf install -y wireguard-tools iproute2 curl
|
||||||
;;
|
;;
|
||||||
arch | manjaro | endeavouros)
|
arch | manjaro | endeavouros)
|
||||||
echo "detected Arch-family distro: $DISTRO"
|
echo "detected Arch-family distro: $DISTRO"
|
||||||
pacman -Syu --noconfirm --needed wireguard-tools
|
pacman -Syu --noconfirm --needed wireguard-tools iproute2 curl
|
||||||
;;
|
;;
|
||||||
alpine)
|
alpine)
|
||||||
echo "detected Alpine distro: $DISTRO"
|
echo "detected Alpine distro: $DISTRO"
|
||||||
apk add --no-cache wireguard-tools
|
apk add --no-cache wireguard-tools curl
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "error: unsupported distro: $DISTRO" >&2
|
echo "error: unsupported distro: $DISTRO" >&2
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default function grabDirNames(params?: Params) {
|
|||||||
const WIREGUARD_HOST_CONFIG_DIR = `/etc/wireguard`;
|
const WIREGUARD_HOST_CONFIG_DIR = `/etc/wireguard`;
|
||||||
const WIREGUARD_PRIVATE_KEY_FILE_NAME = `private.key`;
|
const WIREGUARD_PRIVATE_KEY_FILE_NAME = `private.key`;
|
||||||
const WIREGUARD_PUBLIC_KEY_FILE_NAME = `public.key`;
|
const WIREGUARD_PUBLIC_KEY_FILE_NAME = `public.key`;
|
||||||
|
const WIREGUARD_CLIENT_CONFIG_FILE_NAME = `wg.conf`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ROOT_DIR,
|
ROOT_DIR,
|
||||||
@@ -55,5 +56,6 @@ export default function grabDirNames(params?: Params) {
|
|||||||
WIREGUARD_HOST_CONFIG_DIR,
|
WIREGUARD_HOST_CONFIG_DIR,
|
||||||
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
||||||
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
|
WIREGUARD_CLIENT_CONFIG_FILE_NAME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user