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.
52 lines
2.5 KiB
TypeScript
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));
|