Files
mesh-catalog/modules/nftables/tools/index.ts
T
jschoubben 663e8143d4 nftables holds the node-packet-filter seat: rules, reload and remove, from a runtime with NET_ADMIN (hq ADR 0169)
The seat's three verbs over the machine's own tools: the filter as enforced
(nftables and the legacy filter), the mesh's own table reloaded from its file,
and one rule set the mesh did not write removed by the name the host reports
it under (ADR 0168) — a predecessor's chain loses its jumps and goes, the
runtime's user chain is emptied back to its return, a table of the machine's
own goes whole; the mesh's tables, the runtime's chains, a built-in chain and
an active found firewall's chains are refused. Tested over the shapes two
machines of the first mesh reported live. The module's own tool stays.
2026-10-02 13:28:33 +02:00

52 lines
2.5 KiB
TypeScript

// The packet filter's tools: the node-packet-filter seat's three verbs — what the machine enforces,
// reload the mesh's own, remove one thing the mesh did not write — and the module's own reading of
// the mesh's table (novox/hq ADR 0045, ADR 0168, ADR 0169).
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
import { FirewallClient } from "../client.js";
export function getSeatVerbs(firewall: FirewallClient): ToolDefinition[] {
return [
{
name: "rules",
description:
"The packet filter as this machine enforces it now: the nftables ruleset and, where the tool exists, the legacy filter's listings. Narrowed to one table or chain when asked.",
input: {
table: { type: "string", description: "one nftables table, as `family name` (optional)" },
chain: { type: "string", description: "one chain of that table (optional)" },
},
run: async (args) => firewall.rules(args.table ? String(args.table) : undefined, args.chain ? String(args.chain) : undefined),
},
{
name: "reload",
description: "Load the mesh's own filter again from the file the mesh writes, and answer with the mesh's table as loaded.",
input: {},
run: async () => firewall.reload(),
},
{
name: "remove",
description:
"Remove one rule set the mesh did not write, named exactly as `node show` lists it: `chain X (iptables-legacy)` or `table ip6 filter, chain DOCKER-USER`. " +
"Refuses the mesh's tables, the runtime's own chains, a built-in chain and an active found firewall's chains. An operator's act, by name, never a flush.",
input: { where: { type: "string", description: "the rule set, as `node show` lists it" } },
run: async (args) => firewall.remove(String(args.where ?? "")),
},
];
}
export function getFirewallTools(firewall: FirewallClient): ToolDefinition[] {
return [
{
name: "firewall_rules",
description: "The mesh's live nftables table on this node — what the mesh's own filter is accepting and dropping.",
input: {},
run: async () => ({ ruleset: await firewall.ruleset() }),
},
];
}
const firewall = FirewallClient.fromEnv();
// The seat's verbs under the seat's name: the runtime serves them on the seat's subjects where this
// module holds it (ADR 0159, 0160). The module's own under its own.
registerModuleTools("node-packet-filter", () => getSeatVerbs(firewall));
registerModuleTools("nftables", () => getFirewallTools(firewall));