Files
harbor-net/src/server/tproxySourceBypass.js

125 lines
2.7 KiB
JavaScript

import { spawnSync } from "node:child_process";
import { settings } from "./config.js";
import { deviceCidrs, normalizeCidr } from "./devices.js";
const DEFAULT_NAT_BYPASS_CIDRS =
"0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4";
function splitCidrs(value) {
return String(value || "")
.split(/[\s,]+/)
.map((item) => normalizeCidr(item))
.filter(Boolean);
}
function unique(list) {
return [...new Set(list)];
}
export function sourceBypassCidrs(
profiles,
envCidrs = process.env.TPROXY_BYPASS_SOURCE_CIDRS || "",
) {
return unique([
...splitCidrs(envCidrs),
...deviceCidrs(profiles?.devices || [], "bypass"),
]);
}
export function buildSourceBypassIptablesCommands(
cidrs,
{
chain = settings.tproxySourceBypassChain,
forwardChain = settings.tproxySourceForwardChain,
natChain = settings.tproxySourceNatChain,
natBypassCidrs = splitCidrs(
process.env.BYPASS_CIDRS || DEFAULT_NAT_BYPASS_CIDRS,
),
} = {},
) {
return [
["-w", "-t", "mangle", "-F", chain],
["-w", "-F", forwardChain],
["-w", "-t", "nat", "-F", natChain],
...cidrs.map((cidr) => [
"-w",
"-t",
"mangle",
"-A",
chain,
"-s",
cidr,
"-j",
"ACCEPT",
]),
...cidrs.flatMap((cidr) => [
["-w", "-A", forwardChain, "-s", cidr, "-j", "ACCEPT"],
[
"-w",
"-A",
forwardChain,
"-d",
cidr,
"-m",
"conntrack",
"--ctstate",
"RELATED,ESTABLISHED",
"-j",
"ACCEPT",
],
]),
...natBypassCidrs.map((cidr) => [
"-w",
"-t",
"nat",
"-A",
natChain,
"-d",
cidr,
"-j",
"RETURN",
]),
...cidrs.map((cidr) => [
"-w",
"-t",
"nat",
"-A",
natChain,
"-s",
cidr,
"-j",
"MASQUERADE",
]),
];
}
export function syncTproxySourceBypass(profiles, options = {}) {
if (settings.appMode !== "gateway") {
return { success: true, skipped: true, cidrs: [] };
}
const cidrs = sourceBypassCidrs(
profiles,
options.envCidrs ?? process.env.TPROXY_BYPASS_SOURCE_CIDRS,
);
const commands = buildSourceBypassIptablesCommands(cidrs, options);
for (const args of commands) {
const result = spawnSync("iptables", args, {
encoding: "utf8",
timeout: 1000,
});
if (result.error || result.status !== 0) {
return {
success: false,
cidrs,
error:
result.error?.message ||
(result.stderr || result.stdout || "iptables command failed").trim(),
};
}
}
return { success: true, cidrs };
}