Iptables generator funciton bugfix followup: allow host network ports
This commit is contained in:
@@ -4,7 +4,6 @@ import buildHostIptablesScripts from "./build-host-iptables-scripts";
|
||||
const base = {
|
||||
host_id: 0,
|
||||
interface_name: "wgui0",
|
||||
target_interface: "eth0",
|
||||
};
|
||||
|
||||
describe("buildHostIptablesScripts", () => {
|
||||
@@ -39,10 +38,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"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",
|
||||
"iptables -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",
|
||||
@@ -70,10 +66,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"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",
|
||||
"iptables -A WGUI0FWD -s 10.0.0.3/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0IN -s 10.0.0.3/32 -p tcp -m multiport --dports 80,443 -j ACCEPT",
|
||||
@@ -101,16 +94,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.4/32 -o eth0 -p tcp --dport 53 -j ACCEPT",
|
||||
"iptables -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 -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",
|
||||
"iptables -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",
|
||||
@@ -196,7 +183,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("all access scopes same-host forward traffic to the host's own subnet", () => {
|
||||
test("all access scopes input traffic to the host's own subnet", () => {
|
||||
const res = buildHostIptablesScripts({
|
||||
...base,
|
||||
host_subnet: "10.6.0.0/24",
|
||||
@@ -211,10 +198,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.7/32 -o eth0 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.7/32 -o wgui0 -d 10.6.0.0/24 -j ACCEPT",
|
||||
"iptables -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",
|
||||
@@ -241,17 +225,14 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
expect(res.success).toBe(true);
|
||||
expect(res.post_up).toContain(
|
||||
"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",
|
||||
"iptables -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",
|
||||
);
|
||||
});
|
||||
|
||||
test("client-to-client traffic on the same host is accepted via the scoped forward rule", () => {
|
||||
test("client-to-client traffic on the same host is accepted on forward and input", () => {
|
||||
const res = buildHostIptablesScripts({
|
||||
...base,
|
||||
host_subnet: "10.0.0.0/24",
|
||||
@@ -266,10 +247,10 @@ describe("buildHostIptablesScripts", () => {
|
||||
|
||||
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",
|
||||
"iptables -A WGUI0FWD -s 10.0.0.2/32 -j ACCEPT",
|
||||
);
|
||||
expect(res.post_up).toContain(
|
||||
"iptables -A WGUI0FWD -s 10.0.0.2/32 -o eth0 -j ACCEPT",
|
||||
"iptables -A WGUI0IN -s 10.0.0.2/32 -d 10.0.0.0/24 -j ACCEPT",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -277,6 +258,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
const res = buildHostIptablesScripts({
|
||||
...base,
|
||||
host_subnet: "10.0.0.0/24",
|
||||
sibling_subnets: ["10.1.0.0/24", "10.2.0.0/24"],
|
||||
clients: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -286,7 +268,7 @@ describe("buildHostIptablesScripts", () => {
|
||||
{
|
||||
id: 2,
|
||||
wg_ip_address: "10.0.0.3",
|
||||
rules: [{ rule_type: "all" }],
|
||||
rules: [{ rule_type: "destination", ports: "80", protocol: "tcp" }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -294,21 +276,38 @@ describe("buildHostIptablesScripts", () => {
|
||||
expect(res.success).toBe(true);
|
||||
const post_up = res.post_up ?? "";
|
||||
|
||||
expect(post_up).not.toContain(
|
||||
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";
|
||||
|
||||
expect(post_up).toContain(sibling_drop);
|
||||
expect(post_up).toContain(sibling_drop_2);
|
||||
expect(post_up).toContain(sibling_input_drop);
|
||||
expect(post_up.indexOf(accept)).toBeGreaterThan(
|
||||
post_up.indexOf(sibling_drop_2),
|
||||
);
|
||||
});
|
||||
|
||||
test("a sibling subnet equal to the host's own subnet is ignored", () => {
|
||||
const res = buildHostIptablesScripts({
|
||||
...base,
|
||||
host_subnet: "10.1.0.0/24",
|
||||
sibling_subnets: ["10.1.0.0/24", "10.1.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).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",
|
||||
);
|
||||
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", () => {
|
||||
|
||||
@@ -15,8 +15,8 @@ type HostIptablesClient = {
|
||||
type Params = {
|
||||
host_id: number;
|
||||
interface_name: string;
|
||||
target_interface: string;
|
||||
host_subnet?: string;
|
||||
sibling_subnets?: string[];
|
||||
clients?: HostIptablesClient[];
|
||||
};
|
||||
|
||||
@@ -85,8 +85,6 @@ function acceptLines({
|
||||
source,
|
||||
protocol,
|
||||
ports,
|
||||
wg_interface,
|
||||
target_interface,
|
||||
host_subnet,
|
||||
}: {
|
||||
forward_chain: string;
|
||||
@@ -94,8 +92,6 @@ function acceptLines({
|
||||
source: string;
|
||||
protocol?: string;
|
||||
ports: string[];
|
||||
wg_interface: string;
|
||||
target_interface: string;
|
||||
host_subnet?: string;
|
||||
}) {
|
||||
const lines: string[] = [];
|
||||
@@ -118,19 +114,6 @@ function acceptLines({
|
||||
joinArgs([
|
||||
`iptables -A ${forward_chain}`,
|
||||
`-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,
|
||||
port_match_str,
|
||||
`-j ACCEPT`,
|
||||
@@ -156,8 +139,8 @@ function acceptLines({
|
||||
export default function buildHostIptablesScripts({
|
||||
host_id,
|
||||
interface_name,
|
||||
target_interface,
|
||||
host_subnet,
|
||||
sibling_subnets,
|
||||
clients,
|
||||
}: Params) {
|
||||
if (!Number.isSafeInteger(host_id) || host_id < 0 || host_id > 999999999) {
|
||||
@@ -174,13 +157,6 @@ export default function buildHostIptablesScripts({
|
||||
};
|
||||
}
|
||||
|
||||
if (!SAFE_IFACE.test(target_interface)) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `Invalid target interface`,
|
||||
};
|
||||
}
|
||||
|
||||
const scoped_subnet = host_subnet?.trim();
|
||||
|
||||
if (scoped_subnet && !validateIpv4Cidr({ value: scoped_subnet })) {
|
||||
@@ -190,6 +166,12 @@ export default function buildHostIptablesScripts({
|
||||
};
|
||||
}
|
||||
|
||||
const sibling_host_subnets = (sibling_subnets || []).filter(
|
||||
(subnet) =>
|
||||
subnet.trim() !== scoped_subnet &&
|
||||
validateIpv4Cidr({ value: subnet.trim() }),
|
||||
);
|
||||
|
||||
const { forward, input } = deriveIptablesChainNames({ host_id });
|
||||
|
||||
const masquerade_match = scoped_subnet ? `-s ${scoped_subnet} ` : ``;
|
||||
@@ -222,6 +204,14 @@ export default function buildHostIptablesScripts({
|
||||
const source = `${source_ip}/32`;
|
||||
const rules = client.rules || [];
|
||||
|
||||
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`,
|
||||
);
|
||||
}
|
||||
|
||||
for (let r = 0; r < rules.length; r++) {
|
||||
const rule = rules[r];
|
||||
|
||||
@@ -243,14 +233,6 @@ export default function buildHostIptablesScripts({
|
||||
joinArgs([
|
||||
`iptables -A ${forward}`,
|
||||
`-s ${source}`,
|
||||
`-o ${target_interface}`,
|
||||
`-j ACCEPT`,
|
||||
]),
|
||||
joinArgs([
|
||||
`iptables -A ${forward}`,
|
||||
`-s ${source}`,
|
||||
`-o ${interface_name}`,
|
||||
scoped_subnet ? `-d ${scoped_subnet}` : undefined,
|
||||
`-j ACCEPT`,
|
||||
]),
|
||||
joinArgs([
|
||||
@@ -280,8 +262,6 @@ export default function buildHostIptablesScripts({
|
||||
source,
|
||||
protocol: rule.protocol || "any",
|
||||
ports: parsed_ports.ports,
|
||||
wg_interface: interface_name,
|
||||
target_interface,
|
||||
host_subnet: scoped_subnet,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
} from "@/db/types/db";
|
||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||
import { execSync } from "node:child_process";
|
||||
import grabHostNetworkInterface from "./grab-host-network-interface";
|
||||
import type { APIResponseObject } from "@moduletrace/bunext/types";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { TableType, User } from "@/src/types";
|
||||
@@ -92,8 +91,31 @@ export default async function setupWireguardHost({
|
||||
rules_by_client_id.set(rule.client_id, existing_rules);
|
||||
}
|
||||
|
||||
const TARGET_INTERFACE =
|
||||
host?.interface || (await grabHostNetworkInterface());
|
||||
const sibling_hosts_res = await BunSQLite.select<
|
||||
BUN_SQLITE_WGUI_HOSTS,
|
||||
TableType
|
||||
>({
|
||||
table: "hosts",
|
||||
});
|
||||
|
||||
const sibling_subnets = [
|
||||
...new Set(
|
||||
(sibling_hosts_res.payload || [])
|
||||
.filter(
|
||||
(host_row) =>
|
||||
host_row.id != HOST_ID &&
|
||||
typeof host_row.wg_ip_address == "string" &&
|
||||
host_row.wg_ip_address.trim() != "",
|
||||
)
|
||||
.map((host_row) => {
|
||||
const parts = String(host_row.wg_ip_address)
|
||||
.trim()
|
||||
.split(".");
|
||||
return `${parts.slice(0, 3).join(".")}.0/24`;
|
||||
}),
|
||||
),
|
||||
];
|
||||
|
||||
const HOST_WG_IP = host?.wg_ip_address || wg_subnet_ip;
|
||||
|
||||
if (!HOST_WG_IP) {
|
||||
@@ -178,8 +200,8 @@ export default async function setupWireguardHost({
|
||||
const iptables_scripts = buildHostIptablesScripts({
|
||||
host_id: Number(HOST_ID),
|
||||
interface_name: INTERFACE_NAME,
|
||||
target_interface: TARGET_INTERFACE || "eth0",
|
||||
host_subnet: HOST_SUBNET,
|
||||
sibling_subnets,
|
||||
clients: clients.map((client) => ({
|
||||
id: client.id,
|
||||
wg_ip_address: client.wg_ip_address,
|
||||
|
||||
Reference in New Issue
Block a user