fix: allow client-to-client traffic when isolation is disabled

The wg+ wildcard in the server isolation DROP rule was also matching
same-interface traffic (wg0->wg0), blocking clients from pinging each
other even with Client Isolation turned off.

Fix: always insert an explicit same-interface ACCEPT (or REJECT if
isolated) rule AFTER the wg+ DROP, so it lands at position 1 in the
chain and is evaluated before the DROP.

Also update syncIptablesRules to apply the ACCEPT rule (not just remove
the REJECT) when isolation is toggled off at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
xtcnet 2026-03-17 20:14:33 +07:00
parent fd8baf878c
commit 5d65eafc65

View file

@ -65,17 +65,22 @@ const internalWireguard = {
basePostUp.push("iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE");
basePostDown.push("iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE");
// Client Isolation: Prevent clients on this interface from communicating with each other
// Server Isolation: drop cross-server traffic by default.
// Uses -I to insert at position 1, before the ACCEPT rules above.
basePostUp.push("iptables -I FORWARD -i %i -o wg+ -j DROP");
basePostDown.push("iptables -D FORWARD -i %i -o wg+ -j DROP");
// Same-interface rule: inserted AFTER the DROP above so it lands at position 1,
// placing it BEFORE the wg+ DROP in the chain.
// This ensures client-to-client traffic on this interface is evaluated first.
if (iface.isolate_clients) {
basePostUp.push("iptables -I FORWARD -i %i -o %i -j REJECT");
basePostDown.push("iptables -D FORWARD -i %i -o %i -j REJECT");
} else {
basePostUp.push("iptables -I FORWARD -i %i -o %i -j ACCEPT");
basePostDown.push("iptables -D FORWARD -i %i -o %i -j ACCEPT");
}
// Server Isolation (Default DROP) & Server Peering
// 1. By default, prevent this interface from talking to ANY OTHER wg+ interfaces
basePostUp.push("iptables -I FORWARD -i %i -o wg+ -j DROP");
basePostDown.push("iptables -D FORWARD -i %i -o wg+ -j DROP");
// 2. Fetch linked servers to punch holes in the DROP rule
// wg_server_link has interface_id_1 and interface_id_2
const links = await knex("wg_server_link")
@ -646,12 +651,15 @@ const internalWireguard = {
*/
async syncIptablesRules(iface) {
const name = iface.name;
// Remove existing rule first (idempotent — suppress error if rule doesn't exist)
// Remove both possible same-interface rules first (idempotent)
await execAsync(`iptables -D FORWARD -i ${name} -o ${name} -j REJECT 2>/dev/null || true`);
await execAsync(`iptables -D FORWARD -i ${name} -o ${name} -j ACCEPT 2>/dev/null || true`);
// Re-insert at position 1 so it appears before the wg+ DROP rule in the chain
if (iface.isolate_clients) {
await execAsync(`iptables -I FORWARD -i ${name} -o ${name} -j REJECT`);
logger.info(`Client isolation enabled for ${name}`);
} else {
await execAsync(`iptables -I FORWARD -i ${name} -o ${name} -j ACCEPT`);
logger.info(`Client isolation disabled for ${name}`);
}
},