Apply client changes to the running tunnel without a restart
Saving a client or rule restarted the whole host tunnel (wg-quick down/up) and re-ran the firewall manager setup, which was slow and briefly dropped every client on the host. Client and rule changes now sync peers with `wg syncconf` and re-run the PostUp script, skipping the firewalld/ufw step. Host creation and edits still do a full restart. PostUp now loads the per-host chains with `iptables-restore --noflush`, so re-running it on a live tunnel swaps the rules atomically instead of leaving the chains empty between flush and re-add. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import type { APIResponseObject } from "@moduletrace/bunext/types";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import grabHostDirnames from "./grab-host-dir-names";
|
||||
import manageWireguardHost from "./manage-wireguard-host";
|
||||
|
||||
type Params = {
|
||||
host_id: number;
|
||||
};
|
||||
|
||||
function errorOutput({ error }: { error: any }) {
|
||||
const stderr = error?.stderr ? String(error.stderr).trim() : "";
|
||||
const stdout = error?.stdout ? String(error.stdout).trim() : "";
|
||||
|
||||
return stderr || stdout || error?.message || "unknown error";
|
||||
}
|
||||
|
||||
function isInterfaceUp({ interface_name }: { interface_name: string }) {
|
||||
try {
|
||||
execFileSync("ip", ["link", "show", "dev", interface_name], {
|
||||
stdio: "pipe",
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an already-written host config and PostUp script to the running
|
||||
* tunnel without restarting it: peers are synced with `wg syncconf` and the
|
||||
* per-host iptables chains are reloaded atomically by re-running PostUp.
|
||||
* Connected clients stay connected. Falls back to bringing the tunnel up if
|
||||
* it isn't running.
|
||||
*
|
||||
* Only for changes that leave the interface itself alone (peers and client
|
||||
* rules). Address or listen-port changes need a full restart.
|
||||
*/
|
||||
export default function applyWireguardHostLive({
|
||||
host_id,
|
||||
}: Params): APIResponseObject {
|
||||
const { HOST_CONFIG_FILE, INTERFACE_NAME, POST_UP_PATH } =
|
||||
grabHostDirnames({ host: { id: host_id } });
|
||||
|
||||
const IS_ROOT =
|
||||
typeof process.getuid === "function" && process.getuid() === 0;
|
||||
|
||||
if (!IS_ROOT) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "wg-ui must run as root to manage wireguard tunnels",
|
||||
};
|
||||
}
|
||||
|
||||
if (!isInterfaceUp({ interface_name: INTERFACE_NAME })) {
|
||||
return manageWireguardHost({ host_id, action: "up" });
|
||||
}
|
||||
|
||||
try {
|
||||
const stripped = execFileSync("wg-quick", ["strip", HOST_CONFIG_FILE], {
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
});
|
||||
|
||||
execFileSync("wg", ["syncconf", INTERFACE_NAME, "/dev/stdin"], {
|
||||
input: stripped,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Could not sync peers on ${INTERFACE_NAME}: ${errorOutput({ error })}`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
execFileSync(POST_UP_PATH, [], {
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
timeout: 30000,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Peers synced on ${INTERFACE_NAME}, but the firewall rules could not be applied: ${errorOutput({ error })}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
msg: `Applied changes to ${INTERFACE_NAME} without a restart`,
|
||||
};
|
||||
}
|
||||
@@ -14,9 +14,9 @@ describe("buildHostIptablesScripts", () => {
|
||||
});
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain("iptables -N WGUI0FWD");
|
||||
expect(res.post_up).toContain("iptables -A WGUI0FWD -j DROP");
|
||||
expect(res.post_up).toContain("iptables -A WGUI0IN -j DROP");
|
||||
expect(res.post_up).toContain(":WGUI0FWD - [0:0]");
|
||||
expect(res.post_up).toContain("-A WGUI0FWD -j DROP");
|
||||
expect(res.post_up).toContain("-A WGUI0IN -j DROP");
|
||||
expect(res.post_up).not.toContain("WGUI0FWD -s");
|
||||
expect(res.post_down).toContain("iptables -X WGUI0FWD");
|
||||
expect(res.post_down).toContain(
|
||||
@@ -38,10 +38,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.2/32 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.2/32 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -66,10 +66,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.3/32 -d 192.168.1.10/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -95,13 +95,13 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.5.0.2/32 -d 65.108.81.79/32 -p tcp --dport 443 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).not.toContain(
|
||||
"iptables -A WGUI0IN -s 10.5.0.2/32 -d 10.5.0.0/24 -p tcp --dport 443 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.5.0.2/32 -d 10.5.0.0/24 -p tcp --dport 443 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -125,16 +125,16 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -229,10 +229,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.7/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.7/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.7/32 -d 10.6.0.0/24 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.7/32 -d 10.6.0.0/24 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -256,10 +256,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -278,10 +278,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.2/32 -d 10.0.0.0/24 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.0.0.2/32 -d 10.0.0.0/24 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -307,10 +307,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
expect(res.success).toBe(true);
|
||||
const post_up = res.post_up ?? "";
|
||||
|
||||
const sibling_drop = "iptables -A WGUI0FWD -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
|
||||
const sibling_drop_2 = "iptables -A WGUI0FWD -s 10.0.0.2/32 -d 10.2.0.0/24 -j DROP";
|
||||
const sibling_input_drop = "iptables -A WGUI0IN -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
|
||||
const accept = "iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT";
|
||||
const sibling_drop = "-A WGUI0FWD -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
|
||||
const sibling_drop_2 = "-A WGUI0FWD -s 10.0.0.2/32 -d 10.2.0.0/24 -j DROP";
|
||||
const sibling_input_drop = "-A WGUI0IN -s 10.0.0.2/32 -d 10.1.0.0/24 -j DROP";
|
||||
const accept = "-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT";
|
||||
|
||||
expect(post_up).toContain(sibling_drop);
|
||||
expect(post_up).toContain(sibling_drop_2);
|
||||
@@ -337,7 +337,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).not.toContain("-d 10.1.0.0/24 -j DROP");
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -356,10 +356,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.7.0.3/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.7.0.3/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.7.0.3/32 -j ACCEPT",
|
||||
"-A WGUI0IN -s 10.7.0.3/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).not.toContain("-d 10.7.0.0/24");
|
||||
});
|
||||
@@ -382,8 +382,8 @@ describe("buildHostIptablesScripts", () => {
|
||||
const post_up = res.post_up ?? "";
|
||||
|
||||
const sibling_drop =
|
||||
"iptables -A WGUI0IN -s 10.7.0.4/32 -d 10.3.0.0/24 -j DROP";
|
||||
const input_accept = "iptables -A WGUI0IN -s 10.7.0.4/32 -j ACCEPT";
|
||||
"-A WGUI0IN -s 10.7.0.4/32 -d 10.3.0.0/24 -j DROP";
|
||||
const input_accept = "-A WGUI0IN -s 10.7.0.4/32 -j ACCEPT";
|
||||
|
||||
expect(post_up).toContain(sibling_drop);
|
||||
expect(post_up).toContain(input_accept);
|
||||
@@ -414,7 +414,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.7.0.5/32 -j ACCEPT",
|
||||
"-A WGUI0FWD -s 10.7.0.5/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).not.toContain("192.0.2.1");
|
||||
});
|
||||
@@ -444,9 +444,9 @@ describe("buildHostIptablesScripts", () => {
|
||||
const post_up = res.post_up ?? "";
|
||||
|
||||
const sibling_drop =
|
||||
"iptables -A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.0/24 -j DROP";
|
||||
"-A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.0/24 -j DROP";
|
||||
const accept =
|
||||
"iptables -A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.25/32 -p tcp --dport 80 -j ACCEPT";
|
||||
"-A WGUI0IN -s 10.0.0.6/32 -d 10.1.0.25/32 -p tcp --dport 80 -j ACCEPT";
|
||||
|
||||
expect(post_up).toContain(sibling_drop);
|
||||
expect(post_up).toContain(accept);
|
||||
@@ -464,7 +464,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -i wgui0 -p tcp --dport 8443 -j ACCEPT",
|
||||
"-A WGUI0IN -i wgui0 -p tcp --dport 8443 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -489,4 +489,27 @@ describe("buildHostIptablesScripts", () => {
|
||||
expect(res.success).toBe(false);
|
||||
expect(res.msg).toBe("Invalid host id");
|
||||
});
|
||||
test("loads the host chains atomically with iptables-restore", () => {
|
||||
const res = buildHostIptablesScripts({
|
||||
...base,
|
||||
clients: [
|
||||
{ id: 1, wg_ip_address: "10.0.0.2", allow_all_ips: true },
|
||||
],
|
||||
});
|
||||
|
||||
const post_up = res.post_up ?? "";
|
||||
const start = post_up.indexOf(
|
||||
"iptables-restore --noflush <<'WGUI_RULES' || exit 1",
|
||||
);
|
||||
const end = post_up.indexOf("\nWGUI_RULES\n");
|
||||
const block = post_up.slice(start, end);
|
||||
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
expect(block).toContain("*filter\n:WGUI0FWD - [0:0]\n:WGUI0IN - [0:0]");
|
||||
expect(block).toContain("-A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT");
|
||||
expect(block.trimEnd().endsWith("-A WGUI0IN -j DROP\nCOMMIT")).toBe(true);
|
||||
expect(post_up).not.toContain("iptables -F WGUI0FWD");
|
||||
expect(post_up.indexOf("iptables -I FORWARD 1 -i wgui0 -j WGUI0FWD")).toBeGreaterThan(end);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ function acceptLines({
|
||||
|
||||
lines.push(
|
||||
joinArgs([
|
||||
`iptables -A ${forward_chain}`,
|
||||
`-A ${forward_chain}`,
|
||||
`-s ${source}`,
|
||||
direction,
|
||||
proto ? `-p ${proto}` : undefined,
|
||||
@@ -130,7 +130,7 @@ function acceptLines({
|
||||
|
||||
lines.push(
|
||||
joinArgs([
|
||||
`iptables -A ${input_chain}`,
|
||||
`-A ${input_chain}`,
|
||||
`-s ${source}`,
|
||||
input_destination ? `-d ${input_destination}` : undefined,
|
||||
proto ? `-p ${proto}` : undefined,
|
||||
@@ -198,7 +198,7 @@ export default function buildHostIptablesScripts({
|
||||
}
|
||||
|
||||
accept_lines.push(
|
||||
`iptables -A ${input} -i ${interface_name} -p tcp --dport ${port} -j ACCEPT`,
|
||||
`-A ${input} -i ${interface_name} -p tcp --dport ${port} -j ACCEPT`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -231,15 +231,15 @@ export default function buildHostIptablesScripts({
|
||||
for (let s = 0; s < sibling_host_subnets.length; s++) {
|
||||
const sibling_host_subnet = sibling_host_subnets[s];
|
||||
accept_lines.push(
|
||||
`iptables -A ${forward} -s ${source} -d ${sibling_host_subnet} -j DROP`,
|
||||
`iptables -A ${input} -s ${source} -d ${sibling_host_subnet} -j DROP`,
|
||||
`-A ${forward} -s ${source} -d ${sibling_host_subnet} -j DROP`,
|
||||
`-A ${input} -s ${source} -d ${sibling_host_subnet} -j DROP`,
|
||||
);
|
||||
}
|
||||
|
||||
if (client.allow_all_ips) {
|
||||
accept_lines.push(
|
||||
`iptables -A ${forward} -s ${source} -j ACCEPT`,
|
||||
`iptables -A ${input} -s ${source} -j ACCEPT`,
|
||||
`-A ${forward} -s ${source} -j ACCEPT`,
|
||||
`-A ${input} -s ${source} -j ACCEPT`,
|
||||
);
|
||||
|
||||
continue;
|
||||
@@ -264,12 +264,12 @@ export default function buildHostIptablesScripts({
|
||||
if (rule.rule_type == "all") {
|
||||
accept_lines.push(
|
||||
joinArgs([
|
||||
`iptables -A ${forward}`,
|
||||
`-A ${forward}`,
|
||||
`-s ${source}`,
|
||||
`-j ACCEPT`,
|
||||
]),
|
||||
joinArgs([
|
||||
`iptables -A ${input}`,
|
||||
`-A ${input}`,
|
||||
`-s ${source}`,
|
||||
scoped_subnet ? `-d ${scoped_subnet}` : undefined,
|
||||
`-j ACCEPT`,
|
||||
@@ -314,21 +314,22 @@ export default function buildHostIptablesScripts({
|
||||
`iptables -D FORWARD -i ${interface_name} -j ACCEPT 2>/dev/null || true`,
|
||||
`iptables -D FORWARD -o ${interface_name} -j ACCEPT 2>/dev/null || true`,
|
||||
``,
|
||||
`iptables -N ${forward} 2>/dev/null || true`,
|
||||
`iptables -F ${forward}`,
|
||||
`iptables -N ${input} 2>/dev/null || true`,
|
||||
`iptables -F ${input}`,
|
||||
``,
|
||||
`iptables -A ${forward} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
|
||||
`iptables -A ${input} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
|
||||
``,
|
||||
`iptables -A ${input} -i ${interface_name} -p udp --dport 53 -j ACCEPT`,
|
||||
`iptables -A ${input} -i ${interface_name} -p tcp --dport 53 -j ACCEPT`,
|
||||
``,
|
||||
// Declaring a chain under --noflush creates or flushes it, and the
|
||||
// whole table commits at once — so re-running this on a live tunnel
|
||||
// swaps the rules without a window where the chains are empty.
|
||||
`iptables-restore --noflush <<'WGUI_RULES' || exit 1`,
|
||||
`*filter`,
|
||||
`:${forward} - [0:0]`,
|
||||
`:${input} - [0:0]`,
|
||||
`-A ${forward} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
|
||||
`-A ${input} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
|
||||
`-A ${input} -i ${interface_name} -p udp --dport 53 -j ACCEPT`,
|
||||
`-A ${input} -i ${interface_name} -p tcp --dport 53 -j ACCEPT`,
|
||||
...accept_lines,
|
||||
``,
|
||||
`iptables -A ${forward} -j DROP`,
|
||||
`iptables -A ${input} -j DROP`,
|
||||
`-A ${forward} -j DROP`,
|
||||
`-A ${input} -j DROP`,
|
||||
`COMMIT`,
|
||||
`WGUI_RULES`,
|
||||
``,
|
||||
`iptables -C FORWARD -i ${interface_name} -j ${forward} 2>/dev/null || iptables -I FORWARD 1 -i ${interface_name} -j ${forward}`,
|
||||
`iptables -C FORWARD -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || iptables -I FORWARD 1 -o ${interface_name} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT`,
|
||||
|
||||
@@ -184,6 +184,7 @@ export default async function setupWireguardClient({
|
||||
host: resolved_host,
|
||||
user,
|
||||
is_update_after_client_setup: true,
|
||||
apply_live: true,
|
||||
});
|
||||
|
||||
if (!host_setup_res.success) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { TableType, User } from "@/src/types";
|
||||
import checkPrivateIPAvailability from "./check-private-ip-availability";
|
||||
import manageWireguardHost from "./manage-wireguard-host";
|
||||
import applyWireguardHostLive from "./apply-wireguard-host-live";
|
||||
import grabHostDirnames from "./grab-host-dir-names";
|
||||
import grabClientDirnames from "./grab-client-dir-names";
|
||||
import buildHostIptablesScripts from "./build-host-iptables-scripts";
|
||||
@@ -27,6 +28,13 @@ type Params = {
|
||||
wg_subnet_ip?: string;
|
||||
user: User;
|
||||
is_update_after_client_setup?: boolean;
|
||||
/**
|
||||
* Apply the new config to the running tunnel (`wg syncconf` + atomic
|
||||
* iptables reload) instead of restarting it, and skip the firewall
|
||||
* manager rules. Only for peer/rule changes — the interface address and
|
||||
* listen port must be unchanged.
|
||||
*/
|
||||
apply_live?: boolean;
|
||||
};
|
||||
|
||||
export default async function setupWireguardHost({
|
||||
@@ -34,6 +42,7 @@ export default async function setupWireguardHost({
|
||||
wg_subnet_ip,
|
||||
user,
|
||||
is_update_after_client_setup,
|
||||
apply_live,
|
||||
}: Params): Promise<APIResponseObject> {
|
||||
const {
|
||||
HOST_CLIENTS_DIR,
|
||||
@@ -296,6 +305,10 @@ export default async function setupWireguardHost({
|
||||
);
|
||||
}
|
||||
|
||||
if (apply_live) {
|
||||
return applyWireguardHostLive({ host_id: Number(HOST_ID) });
|
||||
}
|
||||
|
||||
const manage_res = manageWireguardHost({
|
||||
host_id: HOST_ID,
|
||||
action: "restart",
|
||||
|
||||
@@ -125,6 +125,7 @@ export default async function updateWireguardHostPublicIP({
|
||||
host,
|
||||
user,
|
||||
is_update_after_client_setup: true,
|
||||
apply_live: true,
|
||||
});
|
||||
|
||||
if (!host_setup_res.success) {
|
||||
|
||||
@@ -116,6 +116,7 @@ export default async function (
|
||||
host,
|
||||
user,
|
||||
is_update_after_client_setup: true,
|
||||
apply_live: true,
|
||||
});
|
||||
|
||||
if (!host_setup_res.success) {
|
||||
|
||||
Reference in New Issue
Block a user