- bookshelf: Servarr v1 fork on the radarr template (4 tools). - unifi: portainer-shaped tooled app (7 tools, 9 ports), settings-merged config. - fail2ban: host-level security module mirroring firewall (service + restart-on, no container); ban actions preserved as source ufw/iptables and FLAGGED to be rewritten nftables-native before it actually bans. - marrytts: manifest-only plain container (no tools), like resolv-conf. All typecheck against the built @novox/mesh-sdk; service images digest-pinned. Held from merge pending the hq initialization reconciliation. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
// fail2ban's tools — reading and steering the live ban state. The jails themselves are declared
|
|
// resources (module.json); these three touch what the running daemon holds: what is banned now,
|
|
// and the manual ban/unban an operator reaches for. The daemon's state is fail2ban's own, so this
|
|
// is the only way to see or change it — the mesh reconciles the config, not the bans.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { Fail2banClient } from "../client.js";
|
|
|
|
export function getFail2banTools(fail2ban: Fail2banClient): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "fail2ban_status",
|
|
description:
|
|
"fail2ban status on this node — the jails and their live bans. Omit `jail` for every jail, or name one for its detail.",
|
|
input: {
|
|
type: "object",
|
|
properties: {
|
|
jail: {
|
|
type: "string",
|
|
description: "A specific jail (e.g. sshd, recidive); omit for the overview of all jails.",
|
|
},
|
|
},
|
|
},
|
|
run: async (args) => ({ status: await fail2ban.status(args.jail as string | undefined) }),
|
|
},
|
|
{
|
|
name: "fail2ban_ban",
|
|
description: "Manually ban an IP address in a jail — a live change to the running daemon, not a mesh-managed file.",
|
|
input: {
|
|
type: "object",
|
|
properties: {
|
|
jail: { type: "string", description: "Jail name (e.g. sshd, recidive)." },
|
|
ip: { type: "string", description: "IP address to ban." },
|
|
},
|
|
required: ["jail", "ip"],
|
|
},
|
|
run: async (args) => ({ result: await fail2ban.ban(args.jail as string, args.ip as string) }),
|
|
},
|
|
{
|
|
name: "fail2ban_unban",
|
|
description: "Unban an IP address from one jail, or from every jail when `jail` is omitted.",
|
|
input: {
|
|
type: "object",
|
|
properties: {
|
|
ip: { type: "string", description: "IP address to unban." },
|
|
jail: { type: "string", description: "A specific jail; omit to unban from all jails." },
|
|
},
|
|
required: ["ip"],
|
|
},
|
|
run: async (args) => ({ result: await fail2ban.unban(args.ip as string, args.jail as string | undefined) }),
|
|
},
|
|
];
|
|
}
|
|
|
|
registerModuleTools("fail2ban", () => getFail2banTools(Fail2banClient.fromEnv()));
|