Iptables generator funciton bugfix

This commit is contained in:
2026-09-28 20:31:17 +01:00
parent 8492ae1a2a
commit 19486bbfa0
2 changed files with 129 additions and 21 deletions
@@ -39,7 +39,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.2/32 -o eth0 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -o wgui0 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.2/32 -j ACCEPT", "iptables -A WGUI0IN -s 10.0.0.2/32 -j ACCEPT",
@@ -67,7 +70,10 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.3/32 -p tcp -m multiport --dports 80,443 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.3/32 -o eth0 -p tcp -m multiport --dports 80,443 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.3/32 -o wgui0 -p tcp -m multiport --dports 80,443 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.3/32 -p tcp -m multiport --dports 80,443 -j ACCEPT", "iptables -A WGUI0IN -s 10.0.0.3/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
@@ -95,10 +101,16 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.4/32 -o eth0 -p tcp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -p udp --dport 53 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.4/32 -o eth0 -p udp --dport 53 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -o wgui0 -p tcp --dport 53 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.4/32 -o wgui0 -p udp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT", "iptables -A WGUI0IN -s 10.0.0.4/32 -p tcp --dport 53 -j ACCEPT",
@@ -184,7 +196,7 @@ describe("buildHostIptablesScripts", () => {
); );
}); });
test("all access accepts the client on forward and input regardless of host_subnet", () => { test("all access scopes same-host forward traffic to the host's own subnet", () => {
const res = buildHostIptablesScripts({ const res = buildHostIptablesScripts({
...base, ...base,
host_subnet: "10.6.0.0/24", host_subnet: "10.6.0.0/24",
@@ -199,13 +211,14 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.7/32 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.7/32 -o eth0 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.7/32 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.7/32 -o wgui0 -d 10.6.0.0/24 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.7/32 -d 10.6.0.0/24 -j ACCEPT",
); );
expect(res.post_up).not.toContain("-d 10.6.0.0/24");
expect(res.post_up).not.toContain("-o eth0");
}); });
test("port-only rules are mirrored onto both forward and input chains", () => { test("port-only rules are mirrored onto both forward and input chains", () => {
@@ -228,13 +241,76 @@ describe("buildHostIptablesScripts", () => {
expect(res.success).toBe(true); expect(res.success).toBe(true);
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT", "iptables -A WGUI0FWD -s 10.0.0.8/32 -o eth0 -p tcp --dport 53 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.8/32 -o wgui0 -p tcp --dport 53 -j ACCEPT",
); );
expect(res.post_up).toContain( expect(res.post_up).toContain(
"iptables -A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT", "iptables -A WGUI0IN -s 10.0.0.8/32 -p tcp --dport 53 -j ACCEPT",
); );
}); });
test("client-to-client traffic on the same host is accepted via the scoped forward rule", () => {
const res = buildHostIptablesScripts({
...base,
host_subnet: "10.0.0.0/24",
clients: [
{
id: 1,
wg_ip_address: "10.0.0.2",
rules: [{ rule_type: "all" }],
},
],
});
expect(res.success).toBe(true);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -o wgui0 -d 10.0.0.0/24 -j ACCEPT",
);
expect(res.post_up).toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -o eth0 -j ACCEPT",
);
});
test("clients cannot jump to another host on the same machine", () => {
const res = buildHostIptablesScripts({
...base,
host_subnet: "10.0.0.0/24",
clients: [
{
id: 1,
wg_ip_address: "10.0.0.2",
rules: [{ rule_type: "all" }],
},
{
id: 2,
wg_ip_address: "10.0.0.3",
rules: [{ rule_type: "all" }],
},
],
});
expect(res.success).toBe(true);
const post_up = res.post_up ?? "";
expect(post_up).not.toContain(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
);
expect(post_up).not.toContain(
"iptables -A WGUI0FWD -s 10.0.0.3/32 -j ACCEPT",
);
const scoped_accept_index = post_up.indexOf(
"iptables -A WGUI0FWD -s 10.0.0.2/32 -o wgui0 -d 10.0.0.0/24 -j ACCEPT",
);
const catch_all_drop_index = post_up.indexOf(
"iptables -A WGUI0FWD -j DROP",
);
expect(scoped_accept_index).toBeGreaterThan(-1);
expect(catch_all_drop_index).toBeGreaterThan(scoped_accept_index);
});
test("rejects oversized host id", () => { test("rejects oversized host id", () => {
const res = buildHostIptablesScripts({ const res = buildHostIptablesScripts({
...base, ...base,
@@ -85,14 +85,18 @@ function acceptLines({
source, source,
protocol, protocol,
ports, ports,
host_wg_ip, wg_interface,
target_interface,
host_subnet,
}: { }: {
forward_chain: string; forward_chain: string;
input_chain: string; input_chain: string;
source: string; source: string;
protocol?: string; protocol?: string;
ports: string[]; ports: string[];
host_wg_ip?: string; wg_interface: string;
target_interface: string;
host_subnet?: string;
}) { }) {
const lines: string[] = []; const lines: string[] = [];
const protocols = protocolsForRule({ const protocols = protocolsForRule({
@@ -114,6 +118,19 @@ function acceptLines({
joinArgs([ joinArgs([
`iptables -A ${forward_chain}`, `iptables -A ${forward_chain}`,
`-s ${source}`, `-s ${source}`,
`-o ${target_interface}`,
proto ? `-p ${proto}` : undefined,
port_match_str,
`-j ACCEPT`,
]),
);
lines.push(
joinArgs([
`iptables -A ${forward_chain}`,
`-s ${source}`,
`-o ${wg_interface}`,
host_subnet ? `-d ${host_subnet}` : undefined,
proto ? `-p ${proto}` : undefined, proto ? `-p ${proto}` : undefined,
port_match_str, port_match_str,
`-j ACCEPT`, `-j ACCEPT`,
@@ -124,7 +141,7 @@ function acceptLines({
joinArgs([ joinArgs([
`iptables -A ${input_chain}`, `iptables -A ${input_chain}`,
`-s ${source}`, `-s ${source}`,
host_wg_ip ? `-d ${host_wg_ip}` : undefined, host_subnet ? `-d ${host_subnet}` : undefined,
proto ? `-p ${proto}` : undefined, proto ? `-p ${proto}` : undefined,
port_match_str, port_match_str,
`-j ACCEPT`, `-j ACCEPT`,
@@ -176,9 +193,7 @@ export default function buildHostIptablesScripts({
const { forward, input } = deriveIptablesChainNames({ host_id }); const { forward, input } = deriveIptablesChainNames({ host_id });
const masquerade_match = scoped_subnet ? `-s ${scoped_subnet} ` : ``; const masquerade_match = scoped_subnet ? `-s ${scoped_subnet} ` : ``;
const accept_lines: string[] = [ const accept_lines: string[] = [];
`iptables -A ${forward} -o ${interface_name} -j DROP`,
];
const host_clients = clients || []; const host_clients = clients || [];
for (let i = 0; i < host_clients.length; i++) { for (let i = 0; i < host_clients.length; i++) {
@@ -225,10 +240,25 @@ export default function buildHostIptablesScripts({
if (rule.rule_type == "all") { if (rule.rule_type == "all") {
accept_lines.push( accept_lines.push(
`iptables -A ${forward} -s ${source} -j ACCEPT`, joinArgs([
scoped_subnet `iptables -A ${forward}`,
? `iptables -A ${input} -s ${source} -d ${scoped_subnet} -j ACCEPT` `-s ${source}`,
: `iptables -A ${input} -s ${source} -j ACCEPT`, `-o ${target_interface}`,
`-j ACCEPT`,
]),
joinArgs([
`iptables -A ${forward}`,
`-s ${source}`,
`-o ${interface_name}`,
scoped_subnet ? `-d ${scoped_subnet}` : undefined,
`-j ACCEPT`,
]),
joinArgs([
`iptables -A ${input}`,
`-s ${source}`,
scoped_subnet ? `-d ${scoped_subnet}` : undefined,
`-j ACCEPT`,
]),
); );
continue; continue;
@@ -250,7 +280,9 @@ export default function buildHostIptablesScripts({
source, source,
protocol: rule.protocol || "any", protocol: rule.protocol || "any",
ports: parsed_ports.ports, ports: parsed_ports.ports,
host_wg_ip: scoped_subnet, wg_interface: interface_name,
target_interface,
host_subnet: scoped_subnet,
}), }),
); );
} }