Refactor wireguard to use a different config dir instead of /etc/wireguard
This commit is contained in:
@@ -15,7 +15,7 @@ import type { TableType } from "@/src/types";
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
WGUI_LIB_CLIENTS_CONFIGS_DIR,
|
WGUI_LIB_CLIENTS_CONFIGS_DIR,
|
||||||
WIREGUARD_HOST_CONFIG_DIR,
|
WGUI_LIB_HOSTS_CONFIGS_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,
|
WGUI_LIB_KEYS_DIR,
|
||||||
@@ -133,7 +133,7 @@ export default async function setupWireguardClient({
|
|||||||
host?.public_key ||
|
host?.public_key ||
|
||||||
execSync(
|
execSync(
|
||||||
`cat ${path.join(
|
`cat ${path.join(
|
||||||
WIREGUARD_HOST_CONFIG_DIR,
|
WGUI_LIB_HOSTS_CONFIGS_DIR,
|
||||||
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
)}`,
|
)}`,
|
||||||
{ encoding: "utf-8" },
|
{ encoding: "utf-8" },
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
} from "@/db/types/db";
|
} from "@/db/types/db";
|
||||||
import { AppData } from "@/src/data/app-data";
|
import { AppData } from "@/src/data/app-data";
|
||||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||||
|
import deriveWireguardInterfaceName from "@/src/utils/derive-wireguard-interface-name";
|
||||||
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 grabHostNetworkInterface from "./grab-host-network-interface";
|
||||||
@@ -15,7 +16,8 @@ import checkPrivateIPAvailability from "./check-private-ip-availability";
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
WGUI_LIB_IP_TABLES_DIR,
|
WGUI_LIB_IP_TABLES_DIR,
|
||||||
WIREGUARD_HOST_CONFIG_DIR,
|
WGUI_LIB_HOSTS_CONFIGS_DIR,
|
||||||
|
WGUI_WG_QUICK_SYSTEMD_SCRIPT,
|
||||||
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
WIREGUARD_PRIVATE_KEY_FILE_NAME,
|
||||||
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
WIREGUARD_PUBLIC_KEY_FILE_NAME,
|
||||||
} = grabDirNames();
|
} = grabDirNames();
|
||||||
@@ -31,6 +33,12 @@ export default async function setupWireguardHost({
|
|||||||
}: Params): Promise<APIResponseObject> {
|
}: Params): Promise<APIResponseObject> {
|
||||||
const host_id = host?.id || AppData["WireguardHostID"];
|
const host_id = host?.id || AppData["WireguardHostID"];
|
||||||
|
|
||||||
|
const INTERFACE_NAME = deriveWireguardInterfaceName({ host_id });
|
||||||
|
const HOST_CONFIG_PATH = path.join(
|
||||||
|
WGUI_LIB_HOSTS_CONFIGS_DIR,
|
||||||
|
`${INTERFACE_NAME}.conf`,
|
||||||
|
);
|
||||||
|
|
||||||
const variables_res = await BunSQLite.select<
|
const variables_res = await BunSQLite.select<
|
||||||
BUN_SQLITE_WGUI_VARIABLES,
|
BUN_SQLITE_WGUI_VARIABLES,
|
||||||
TableType
|
TableType
|
||||||
@@ -83,7 +91,8 @@ export default async function setupWireguardHost({
|
|||||||
let pre_sh = ``;
|
let pre_sh = ``;
|
||||||
|
|
||||||
pre_sh += `set -e\n`;
|
pre_sh += `set -e\n`;
|
||||||
pre_sh += `cd ${WIREGUARD_HOST_CONFIG_DIR}\n`;
|
pre_sh += `mkdir -p ${WGUI_LIB_HOSTS_CONFIGS_DIR}\n`;
|
||||||
|
pre_sh += `cd ${WGUI_LIB_HOSTS_CONFIGS_DIR}\n`;
|
||||||
pre_sh += `if [ ! -f ${WIREGUARD_PRIVATE_KEY_FILE_NAME} ]; then\n`;
|
pre_sh += `if [ ! -f ${WIREGUARD_PRIVATE_KEY_FILE_NAME} ]; then\n`;
|
||||||
pre_sh += ` wg genkey | tee ${WIREGUARD_PRIVATE_KEY_FILE_NAME} | wg pubkey > ${WIREGUARD_PUBLIC_KEY_FILE_NAME}\n`;
|
pre_sh += ` wg genkey | tee ${WIREGUARD_PRIVATE_KEY_FILE_NAME} | wg pubkey > ${WIREGUARD_PUBLIC_KEY_FILE_NAME}\n`;
|
||||||
pre_sh += `fi\n`;
|
pre_sh += `fi\n`;
|
||||||
@@ -103,12 +112,12 @@ export default async function setupWireguardHost({
|
|||||||
const HOST_PUBLIC_KEY = host?.id
|
const HOST_PUBLIC_KEY = host?.id
|
||||||
? host.public_key
|
? host.public_key
|
||||||
: execSync(
|
: execSync(
|
||||||
`cat ${path.join(WIREGUARD_HOST_CONFIG_DIR, WIREGUARD_PUBLIC_KEY_FILE_NAME)}`,
|
`cat ${path.join(WGUI_LIB_HOSTS_CONFIGS_DIR, WIREGUARD_PUBLIC_KEY_FILE_NAME)}`,
|
||||||
);
|
);
|
||||||
const HOST_PRIVATE_KEY = host?.private_key
|
const HOST_PRIVATE_KEY = host?.private_key
|
||||||
? host.private_key
|
? host.private_key
|
||||||
: execSync(
|
: execSync(
|
||||||
`cat ${path.join(WIREGUARD_HOST_CONFIG_DIR, WIREGUARD_PRIVATE_KEY_FILE_NAME)}`,
|
`cat ${path.join(WGUI_LIB_HOSTS_CONFIGS_DIR, WIREGUARD_PRIVATE_KEY_FILE_NAME)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
let sh = ``;
|
let sh = ``;
|
||||||
@@ -124,17 +133,17 @@ export default async function setupWireguardHost({
|
|||||||
`${host_id}-down.sh`,
|
`${host_id}-down.sh`,
|
||||||
);
|
);
|
||||||
|
|
||||||
sh += `cd ${WIREGUARD_HOST_CONFIG_DIR}\n`;
|
sh += `cd ${WGUI_LIB_HOSTS_CONFIGS_DIR}\n`;
|
||||||
|
|
||||||
sh += `cat > ${POST_UP_PATH} << EOF\n`;
|
sh += `cat > ${POST_UP_PATH} << EOF\n`;
|
||||||
sh += `#!/bin/bash\n\n`;
|
sh += `#!/bin/bash\n\n`;
|
||||||
sh += `# Allow WireGuard traffic to/from the server itself\n`;
|
sh += `# Allow WireGuard traffic to/from the server itself\n`;
|
||||||
sh += `iptables -I INPUT 1 -i wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -I INPUT 1 -i ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `iptables -I OUTPUT 1 -o wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -I OUTPUT 1 -o ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `\n`;
|
sh += `\n`;
|
||||||
sh += `# Allow WireGuard traffic to be forwarded (insert above Docker rules)\n`;
|
sh += `# Allow WireGuard traffic to be forwarded (insert above Docker rules)\n`;
|
||||||
sh += `iptables -I FORWARD 1 -i wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -I FORWARD 1 -i ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `iptables -I FORWARD 1 -o wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -I FORWARD 1 -o ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `\n`;
|
sh += `\n`;
|
||||||
sh += `iptables -t nat -A POSTROUTING -o ${TARGET_INTERFACE} -j MASQUERADE\n`;
|
sh += `iptables -t nat -A POSTROUTING -o ${TARGET_INTERFACE} -j MASQUERADE\n`;
|
||||||
sh += `EOF\n`;
|
sh += `EOF\n`;
|
||||||
@@ -144,19 +153,19 @@ export default async function setupWireguardHost({
|
|||||||
sh += `cat > ${POST_DOWN_PATH} << EOF\n`;
|
sh += `cat > ${POST_DOWN_PATH} << EOF\n`;
|
||||||
sh += `#!/bin/bash\n\n`;
|
sh += `#!/bin/bash\n\n`;
|
||||||
sh += `# Remove WireGuard INPUT/OUTPUT rules\n`;
|
sh += `# Remove WireGuard INPUT/OUTPUT rules\n`;
|
||||||
sh += `iptables -D INPUT -i wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -D INPUT -i ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `iptables -D OUTPUT -o wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -D OUTPUT -o ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `\n`;
|
sh += `\n`;
|
||||||
sh += `# Remove FORWARD rules\n`;
|
sh += `# Remove FORWARD rules\n`;
|
||||||
sh += `iptables -D FORWARD -i wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -D FORWARD -i ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `iptables -D FORWARD -o wg${host_id} -j ACCEPT\n`;
|
sh += `iptables -D FORWARD -o ${INTERFACE_NAME} -j ACCEPT\n`;
|
||||||
sh += `\n`;
|
sh += `\n`;
|
||||||
sh += `iptables -t nat -D POSTROUTING -o ${TARGET_INTERFACE} -j MASQUERADE\n`;
|
sh += `iptables -t nat -D POSTROUTING -o ${TARGET_INTERFACE} -j MASQUERADE\n`;
|
||||||
sh += `EOF\n`;
|
sh += `EOF\n`;
|
||||||
|
|
||||||
sh += `\n`;
|
sh += `\n`;
|
||||||
|
|
||||||
sh += `cat > wg${host_id}.conf << EOF\n`;
|
sh += `cat > ${INTERFACE_NAME}.conf << EOF\n`;
|
||||||
sh += `[Interface]\n`;
|
sh += `[Interface]\n`;
|
||||||
sh += `Address = ${HOST_WG_IP}/24\n`;
|
sh += `Address = ${HOST_WG_IP}/24\n`;
|
||||||
sh += `ListenPort = 51820\n`;
|
sh += `ListenPort = 51820\n`;
|
||||||
@@ -183,10 +192,28 @@ export default async function setupWireguardHost({
|
|||||||
|
|
||||||
const exec = execSync(sh, { encoding: "utf-8" });
|
const exec = execSync(sh, { encoding: "utf-8" });
|
||||||
|
|
||||||
return {
|
const IS_ROOT =
|
||||||
success: true,
|
typeof process.getuid === "function" && process.getuid() === 0;
|
||||||
msg: exec,
|
const SUDO_PREFIX = IS_ROOT ? "" : "sudo -n ";
|
||||||
};
|
const MANAGE_WG_QUICK_CMD = `${SUDO_PREFIX}${WGUI_WG_QUICK_SYSTEMD_SCRIPT} ${INTERFACE_NAME} ${HOST_CONFIG_PATH}`;
|
||||||
|
|
||||||
|
let exec_systemd = ``;
|
||||||
|
|
||||||
|
try {
|
||||||
|
exec_systemd = execSync(MANAGE_WG_QUICK_CMD, {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
msg: [exec.trim(), exec_systemd.trim()].join("\n\n"),
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `Host config written to ${HOST_CONFIG_PATH}, but could not manage the tunnel via systemd (wg-quick@${INTERFACE_NAME}.service): ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AppData } from "@/src/data/app-data";
|
import { AppData } from "@/src/data/app-data";
|
||||||
|
import deriveWireguardInterfaceName from "@/src/utils/derive-wireguard-interface-name";
|
||||||
import type {
|
import type {
|
||||||
BUN_SQLITE_WGUI_CLIENTS,
|
BUN_SQLITE_WGUI_CLIENTS,
|
||||||
BUN_SQLITE_WGUI_HOSTS,
|
BUN_SQLITE_WGUI_HOSTS,
|
||||||
@@ -7,6 +8,7 @@ import type {
|
|||||||
|
|
||||||
const LISTEN_PORT = 51820;
|
const LISTEN_PORT = 51820;
|
||||||
const IP_TABLES_DIR = `/var/lib/wgui/iptables`;
|
const IP_TABLES_DIR = `/var/lib/wgui/iptables`;
|
||||||
|
const HOSTS_CONFIG_DIR = `/var/lib/wgui/hosts`;
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
host?: BUN_SQLITE_WGUI_HOSTS;
|
host?: BUN_SQLITE_WGUI_HOSTS;
|
||||||
@@ -25,10 +27,14 @@ export default function deriveHostConfig({
|
|||||||
host?.wg_ip_address ||
|
host?.wg_ip_address ||
|
||||||
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value;
|
variables?.find((v) => v.key == "main_host_wg_ip_address")?.value;
|
||||||
|
|
||||||
|
const interface_name = deriveWireguardInterfaceName({ host_id });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
host_id,
|
host_id,
|
||||||
interface_name: `wg${host_id}`,
|
interface_name,
|
||||||
config_path: `/etc/wireguard/wg${host_id}.conf`,
|
config_path: `${HOSTS_CONFIG_DIR}/${interface_name}.conf`,
|
||||||
|
systemd_unit_name: `wg-quick@${interface_name}.service`,
|
||||||
|
|
||||||
address: wg_ip_address ? `${wg_ip_address}/24` : undefined,
|
address: wg_ip_address ? `${wg_ip_address}/24` : undefined,
|
||||||
listen_port: LISTEN_PORT,
|
listen_port: LISTEN_PORT,
|
||||||
post_up: `${IP_TABLES_DIR}/${host_id}-up.sh`,
|
post_up: `${IP_TABLES_DIR}/${host_id}-up.sh`,
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ export default function InterfaceConfigSection({
|
|||||||
<P noMargin className="font-mono text-[11.5px] text-foreground-light/40 dark:text-foreground-dark/40">
|
<P noMargin className="font-mono text-[11.5px] text-foreground-light/40 dark:text-foreground-dark/40">
|
||||||
{config.config_path}
|
{config.config_path}
|
||||||
</P>
|
</P>
|
||||||
|
<P noMargin className="font-mono text-[11.5px] text-foreground-light/40 dark:text-foreground-dark/40">
|
||||||
|
{config.systemd_unit_name}
|
||||||
|
</P>
|
||||||
</Stack>
|
</Stack>
|
||||||
<AdminButton
|
<AdminButton
|
||||||
Icon={Copy}
|
Icon={Copy}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ log() { echo "==> $*"; }
|
|||||||
fail() { echo "error: $*" >&2; exit 1; }
|
fail() { echo "error: $*" >&2; exit 1; }
|
||||||
|
|
||||||
WGUI_LIB_DIR="/var/lib/wgui"
|
WGUI_LIB_DIR="/var/lib/wgui"
|
||||||
|
WG_QUICK_HELPER="$WGUI_LIB_DIR/scripts/wg-quick-systemd.sh"
|
||||||
INSTALL_DIR="${INSTALL_DIR:-}"
|
INSTALL_DIR="${INSTALL_DIR:-}"
|
||||||
SERVICE_USER="${SERVICE_USER:-}"
|
SERVICE_USER="${SERVICE_USER:-}"
|
||||||
SERVICE_GROUP="${SERVICE_GROUP:-}"
|
SERVICE_GROUP="${SERVICE_GROUP:-}"
|
||||||
@@ -125,8 +126,9 @@ install_bun() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_service_user() {
|
setup_service_user() {
|
||||||
mkdir -p "$WGUI_LIB_DIR/iptables" "$WGUI_LIB_DIR/keys" "$WGUI_LIB_DIR/clients"
|
mkdir -p "$WGUI_LIB_DIR/iptables" "$WGUI_LIB_DIR/keys" "$WGUI_LIB_DIR/clients" "$WGUI_LIB_DIR/hosts" "$WGUI_LIB_DIR/scripts"
|
||||||
chown -R "$SERVICE_USER:$SERVICE_GROUP" "$WGUI_LIB_DIR"
|
chown -R "$SERVICE_USER:$SERVICE_GROUP" "$WGUI_LIB_DIR"
|
||||||
|
chmod 770 "$WGUI_LIB_DIR/hosts"
|
||||||
}
|
}
|
||||||
|
|
||||||
clone_or_update() {
|
clone_or_update() {
|
||||||
@@ -187,11 +189,30 @@ setup_wireguard() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
grant_runtime_access() {
|
install_wg_quick_helper() {
|
||||||
log "granting $SERVICE_USER access to /etc/wireguard ..."
|
local helper_src="$INSTALL_DIR/src/scripts/wg-quick-systemd.sh"
|
||||||
mkdir -p /etc/wireguard
|
if [ ! -f "$helper_src" ]; then
|
||||||
chown "root:$SERVICE_GROUP" /etc/wireguard
|
fail "missing $helper_src — cannot install the wg-quick systemd helper"
|
||||||
chmod 770 /etc/wireguard
|
fi
|
||||||
|
log "installing wg-quick systemd helper to $WG_QUICK_HELPER ..."
|
||||||
|
install -m 755 -o root -g root "$helper_src" "$WG_QUICK_HELPER"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_wg_quick_sudoers() {
|
||||||
|
if [ "$SERVICE_USER" = "root" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! command -v visudo >/dev/null 2>&1; then
|
||||||
|
log "visudo not found — skipping sudoers entry for $SERVICE_USER (tunnel management will require root)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local sudoers_file="/etc/sudoers.d/wgui-wg-quick"
|
||||||
|
log "granting $SERVICE_USER passwordless access to $WG_QUICK_HELPER ..."
|
||||||
|
cat > "$sudoers_file" <<EOF
|
||||||
|
$SERVICE_USER ALL=(root) NOPASSWD: $WG_QUICK_HELPER *
|
||||||
|
EOF
|
||||||
|
chmod 440 "$sudoers_file"
|
||||||
|
visudo -cf "$sudoers_file" >/dev/null 2>&1 || fail "invalid sudoers file $sudoers_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_systemd_unit() {
|
install_systemd_unit() {
|
||||||
@@ -274,7 +295,8 @@ fi
|
|||||||
install_dependencies
|
install_dependencies
|
||||||
ensure_env_file
|
ensure_env_file
|
||||||
setup_wireguard
|
setup_wireguard
|
||||||
grant_runtime_access
|
install_wg_quick_helper
|
||||||
|
install_wg_quick_sudoers
|
||||||
|
|
||||||
if [ "$DEV_MODE" = false ]; then
|
if [ "$DEV_MODE" = false ]; then
|
||||||
case "$INIT_SYSTEM" in
|
case "$INIT_SYSTEM" in
|
||||||
@@ -295,7 +317,8 @@ fi
|
|||||||
PORT="$(grab_port)"
|
PORT="$(grab_port)"
|
||||||
log "wg-ui install complete."
|
log "wg-ui install complete."
|
||||||
log "webapp: $INSTALL_DIR"
|
log "webapp: $INSTALL_DIR"
|
||||||
log "runtime: $WGUI_LIB_DIR (keys, iptables, client configs)"
|
log "runtime: $WGUI_LIB_DIR (host configs, keys, iptables, client configs)"
|
||||||
|
log "tunnels: systemd units wg-quick@wgui<host_id>.service (configs in $WGUI_LIB_DIR/hosts — /etc/wireguard is never touched)"
|
||||||
if [ "$DEV_MODE" = true ]; then
|
if [ "$DEV_MODE" = true ]; then
|
||||||
log "process: development mode — no system service installed"
|
log "process: development mode — no system service installed"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -77,8 +77,12 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
mkdir -p /etc/wireguard
|
# Configs are project-scoped under /var/lib/wgui (hosts + helper scripts),
|
||||||
chmod 700 /etc/wireguard
|
# never /etc/wireguard, so stock wg-quick setups on this machine are untouched.
|
||||||
|
mkdir -p /var/lib/wgui/hosts /var/lib/wgui/scripts
|
||||||
|
chown root:root /var/lib/wgui/hosts /var/lib/wgui/scripts
|
||||||
|
chmod 700 /var/lib/wgui/hosts
|
||||||
|
chmod 755 /var/lib/wgui/scripts
|
||||||
|
|
||||||
echo "wireguard setup complete."
|
echo "wireguard setup complete."
|
||||||
wg --version
|
wg --version
|
||||||
Executable
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Manages the wg-quick systemd unit for a single wireguard-ui host tunnel.
|
||||||
|
#
|
||||||
|
# This project deliberately avoids /etc/wireguard entirely so it can never
|
||||||
|
# collide with an existing WireGuard setup. Host configs live under
|
||||||
|
# /var/lib/wgui/hosts and are wired into the stock wg-quick@ template unit
|
||||||
|
# via a per-instance drop-in that points ExecStart/ExecStop at the project
|
||||||
|
# config path. Interfaces follow the project convention wgui<host_id>
|
||||||
|
# (e.g. wgui0), never stock names like wg0.
|
||||||
|
#
|
||||||
|
# Usage: wg-quick-systemd.sh <interface> <config-path>
|
||||||
|
# interface e.g. wgui0 — must match wgui[0-9]* (project convention)
|
||||||
|
# config-path absolute path to the host config file
|
||||||
|
#
|
||||||
|
# Designed to run as root (directly or via a NOPASSWD sudoers entry).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
INTERFACE="${1:-}"
|
||||||
|
CONFIG_PATH="${2:-}"
|
||||||
|
|
||||||
|
if [ -z "$INTERFACE" ] || [ -z "$CONFIG_PATH" ]; then
|
||||||
|
echo "usage: $0 <interface> <config-path>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$INTERFACE" in
|
||||||
|
wgui[0-9]*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "error: refusing to manage non-project interface '$INTERFACE' (expected wgui[0-9]*)" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
WG_QUICK_BIN="$(command -v wg-quick)"
|
||||||
|
if [ -z "$WG_QUICK_BIN" ]; then
|
||||||
|
echo "error: wg-quick not found — run setup-wireguard.sh first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DROP_IN_DIR="/etc/systemd/system/wg-quick@${INTERFACE}.service.d"
|
||||||
|
mkdir -p "$DROP_IN_DIR"
|
||||||
|
|
||||||
|
cat > "$DROP_IN_DIR/override.conf" <<EOF
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=$WG_QUICK_BIN up $CONFIG_PATH
|
||||||
|
ExecReload=
|
||||||
|
ExecReload=$WG_QUICK_BIN strip $CONFIG_PATH
|
||||||
|
ExecStop=
|
||||||
|
ExecStop=$WG_QUICK_BIN down $CONFIG_PATH
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "wg-quick@${INTERFACE}.service"
|
||||||
|
systemctl restart "wg-quick@${INTERFACE}.service"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import deriveWireguardInterfaceName from "./derive-wireguard-interface-name";
|
||||||
|
|
||||||
|
describe("deriveWireguardInterfaceName", () => {
|
||||||
|
test("derives interface name from host id", () => {
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 0 })).toBe("wgui0");
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 1 })).toBe("wgui1");
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 7 })).toBe("wgui7");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("uses a project-scoped prefix, never stock wgN", () => {
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 0 })).not.toBe("wg0");
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 1 })).not.toBe("wg1");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("prefix matches wg-quick interface name requirements (<= 15 chars)", () => {
|
||||||
|
expect(deriveWireguardInterfaceName({ host_id: 0 }).length).toBeLessThanOrEqual(
|
||||||
|
15,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const WIREGUARD_INTERFACE_NAME_PREFIX = `wgui`;
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
host_id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function deriveWireguardInterfaceName({ host_id }: Params) {
|
||||||
|
return `${WIREGUARD_INTERFACE_NAME_PREFIX}${host_id}`;
|
||||||
|
}
|
||||||
@@ -33,8 +33,13 @@ export default function grabDirNames(params?: Params) {
|
|||||||
const WGUI_LIB_IP_TABLES_DIR = path.join(WGUI_LIB_DIR, `iptables`);
|
const WGUI_LIB_IP_TABLES_DIR = path.join(WGUI_LIB_DIR, `iptables`);
|
||||||
const WGUI_LIB_KEYS_DIR = path.join(WGUI_LIB_DIR, `keys`);
|
const WGUI_LIB_KEYS_DIR = path.join(WGUI_LIB_DIR, `keys`);
|
||||||
const WGUI_LIB_CLIENTS_CONFIGS_DIR = path.join(WGUI_LIB_DIR, `clients`);
|
const WGUI_LIB_CLIENTS_CONFIGS_DIR = path.join(WGUI_LIB_DIR, `clients`);
|
||||||
|
const WGUI_LIB_HOSTS_CONFIGS_DIR = path.join(WGUI_LIB_DIR, `hosts`);
|
||||||
|
const WGUI_LIB_SCRIPTS_DIR = path.join(WGUI_LIB_DIR, `scripts`);
|
||||||
|
const WGUI_WG_QUICK_SYSTEMD_SCRIPT = path.join(
|
||||||
|
WGUI_LIB_SCRIPTS_DIR,
|
||||||
|
`wg-quick-systemd.sh`,
|
||||||
|
);
|
||||||
|
|
||||||
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`;
|
const WIREGUARD_CLIENT_CONFIG_FILE_NAME = `wg.conf`;
|
||||||
@@ -52,8 +57,10 @@ export default function grabDirNames(params?: Params) {
|
|||||||
WGUI_LIB_IP_TABLES_DIR,
|
WGUI_LIB_IP_TABLES_DIR,
|
||||||
WGUI_LIB_KEYS_DIR,
|
WGUI_LIB_KEYS_DIR,
|
||||||
WGUI_LIB_CLIENTS_CONFIGS_DIR,
|
WGUI_LIB_CLIENTS_CONFIGS_DIR,
|
||||||
|
WGUI_LIB_HOSTS_CONFIGS_DIR,
|
||||||
|
WGUI_LIB_SCRIPTS_DIR,
|
||||||
|
WGUI_WG_QUICK_SYSTEMD_SCRIPT,
|
||||||
|
|
||||||
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,
|
WIREGUARD_CLIENT_CONFIG_FILE_NAME,
|
||||||
|
|||||||
Reference in New Issue
Block a user