Go is the default for module code. One binary, fail2ban-tools, serving the node-intrusion-prevention seat's four verbs and fail2ban_settings over the SDK, with the same parsing and the same tests; read back against the control node's live daemon.
65 lines
3.1 KiB
Go
65 lines
3.1 KiB
Go
// fail2ban-tools (novox/hq to-be 31, ADR 0179): the intrusion prevention's tools. One binary, launched
|
|
// by the machine's tool runtime and speaking MCP to it over stdio through the Go SDK (ADR 0193, ADR
|
|
// 0198): 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. 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.
|
|
//
|
|
// stdout is the MCP channel; everything this module says, it says on stderr.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// Seat is the role this module holds.
|
|
const Seat = "node-intrusion-prevention"
|
|
|
|
func main() {
|
|
if err := stdio.Serve("", tools(Fail2ban{Run: execRunner})); err != nil {
|
|
fmt.Fprintf(os.Stderr, "[fail2ban] %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func str(description string) map[string]any {
|
|
return map[string]any{"type": "string", "description": description}
|
|
}
|
|
|
|
func arg(a map[string]any, k string) string {
|
|
v, _ := a[k].(string)
|
|
return strings.TrimSpace(v)
|
|
}
|
|
|
|
// verb is one of the seat's verbs: listed as `<seat>.<verb>`, so the runtime serves it on the seat's
|
|
// subject. The module's own tools keep their bare names.
|
|
func verb(name, description string, input map[string]any, run func(a map[string]any) (any, error)) stdio.Tool {
|
|
return stdio.Tool{Name: Seat + "." + name, Description: description, Input: input, Run: run}
|
|
}
|
|
|
|
func tools(f Fail2ban) []stdio.Tool {
|
|
ctx := context.Background()
|
|
oneJail := map[string]any{"jail": str("one jail (optional)")}
|
|
return []stdio.Tool{
|
|
verb("status", "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.",
|
|
oneJail, func(a map[string]any) (any, error) { return f.Status(ctx, arg(a, "jail")) }),
|
|
verb("banned", "Every address banned on this machine right now, with the jail that holds it, when it was banned and when the ban ends.",
|
|
oneJail, func(a map[string]any) (any, error) { return f.Banned(ctx, arg(a, "jail")) }),
|
|
verb("ban", "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.",
|
|
map[string]any{"ip": str("the address"), "jail": str("the jail to hold it (recidive for the long ban)")},
|
|
func(a map[string]any) (any, error) { return f.Ban(ctx, arg(a, "ip"), arg(a, "jail")) }),
|
|
verb("unban", "Let one address go, from one jail or from every jail when none is named.",
|
|
map[string]any{"ip": str("the address"), "jail": str("one jail (optional)")},
|
|
func(a map[string]any) (any, error) { return f.Unban(ctx, arg(a, "ip"), arg(a, "jail")) }),
|
|
{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: map[string]any{"jail": str("the jail")},
|
|
Run: func(a map[string]any) (any, error) { return f.Settings(ctx, arg(a, "jail")) }},
|
|
}
|
|
}
|