The module gains a runtime carrying only the fail2ban client with the daemon's socket shared in, serving status/banned/ban/unban and its own fail2ban_settings. It declares jailing, so the controller's composition lands in jail.d/mesh.conf and filter.d; mailu, route-proxy and gitea log to the journal and declare a jail reading it by container name. The base is strict: three in a day for a day, twice banned in two weeks for four; the mesh's range stays never banned.
63 lines
3.0 KiB
TypeScript
63 lines
3.0 KiB
TypeScript
// The intrusion prevention's tools: the node-intrusion-prevention seat's four verbs — who is banned,
|
|
// the jails' state, ban one, let one go — and the module's own reading of a jail's settings
|
|
// (novox/hq to-be 31, ADR 0179). The jails themselves are composed by the mesh from the modules a
|
|
// machine runs and written as declared resources; these touch only what the running daemon holds.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { Fail2banClient } from "../client.js";
|
|
|
|
export function getSeatVerbs(fail2ban: Fail2banClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "status",
|
|
description:
|
|
"Every jail on this machine with what it watches, how many addresses it is counting failures against and holding now, and the totals since it started; one jail's detail when named.",
|
|
input: { jail: { type: "string", description: "one jail (optional)" } },
|
|
run: async (args) => fail2ban.status(args.jail ? String(args.jail) : undefined),
|
|
},
|
|
{
|
|
name: "banned",
|
|
description: "Every address banned on this machine right now, with the jail that holds it, when it was banned and when the ban ends.",
|
|
input: { jail: { type: "string", description: "one jail (optional)" } },
|
|
run: async (args) => fail2ban.banned(args.jail ? String(args.jail) : undefined),
|
|
},
|
|
{
|
|
name: "ban",
|
|
description:
|
|
"Ban one address in one jail now, for the jail's ban time — an operator's act on the live ban list, which the mesh never writes itself.",
|
|
input: {
|
|
ip: { type: "string", description: "the address" },
|
|
jail: { type: "string", description: "the jail to hold it (recidive for the long ban)" },
|
|
},
|
|
run: async (args) => fail2ban.ban(String(args.ip ?? ""), String(args.jail ?? "")),
|
|
},
|
|
{
|
|
name: "unban",
|
|
description: "Let one address go, from one jail or from every jail when none is named.",
|
|
input: {
|
|
ip: { type: "string", description: "the address" },
|
|
jail: { type: "string", description: "one jail (optional)" },
|
|
},
|
|
run: async (args) => fail2ban.unban(String(args.ip ?? ""), args.jail ? String(args.jail) : undefined),
|
|
},
|
|
];
|
|
}
|
|
|
|
export function getFail2banTools(fail2ban: Fail2banClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "fail2ban_settings",
|
|
description:
|
|
"One jail's effective settings on this machine: ban time, window, tries, the addresses it never bans, its actions and what it reads.",
|
|
input: { jail: { type: "string", description: "the jail" } },
|
|
run: async (args) => fail2ban.settings(String(args.jail ?? "")),
|
|
},
|
|
];
|
|
}
|
|
|
|
const fail2ban = Fail2banClient.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-intrusion-prevention", () => getSeatVerbs(fail2ban));
|
|
registerModuleTools("fail2ban", () => getFail2banTools(fail2ban));
|