routeros — MikroTik RouterOS over ssh

Blocks on a MikroTik router by driving its CLI over ssh: two address-lists (IPv4 and IPv6) plus the input-chain filter rules referencing them, all created by the kur itself. For the REST flavor that manages only list membership, see routeros_api; this ssh one also works on RouterOS 6, which has no REST API.

[kur.sshd]
backend = "routeros"

[kur.sshd.options]
host     = "192.0.2.1"
user     = "blocker"
identity = "/usr/local/etc/ereshkigal/routeros_key"

What it creates

At init, one filter rule per family:

/ip firewall filter add chain=input src-address-list=kur_sshd action=drop
/ipv6 firewall filter add chain=input src-address-list=kur_sshd action=drop

Bans then add to the matching list:

/ip firewall address-list add list=kur_sshd address=1.2.3.4

Every statement is delivered as ssh [-p <port>] [-i <identity>] <user>@<host> '<statement>'.

Requirements

Settings

Options

| option | default | what | |------------|-------------------|---------------------------------------------------| | host | (required) | router hostname or IP | | user | admin | ssh user | | ssh_cmd | ssh | ssh binary plus any base arguments | | ssh_port | (unset) | adds -p <port> when set | | identity | (unset) | adds -i <identity> when set | | list4 | <prefix>_<name> | IPv4 address-list name | | list6 | <prefix>_<name> | IPv6 address-list name | | action | drop | filter rule action, e.g. drop or reject |

What each operation runs

All via ssh; [find where ...] is RouterOS's query-then-act idiom and quietly matches nothing when the entry is already gone:

| operation | statements | |------------|---------------------------------------------------------------------------| | init | /ip firewall filter add chain=input src-address-list=<list4> action=<action> and the /ipv6 twin | | ban | /ip[v6] firewall address-list add list=<listN> address=<ip> | | unban | /ip[v6] firewall address-list remove [find where list=<listN> address=<ip>] | | list | no command — the kur's own ban book | | check | /ip firewall filter print where src-address-list=<list4> exits 0 | | flush | /ip[v6] firewall address-list remove [find where list=<listN>] | | re_init | teardown (best effort), init, re-add every banned IP | | teardown | remove both filter rules, then both lists' entries (ban book kept) |

self_heal

check only confirms the IPv4 filter-rule query succeeds — and RouterOS's print where exits 0 even when nothing matches, so in practice it is an ssh-reachability probe. Only an ssh or auth failure triggers self-heal; rules and list entries removed on the router by hand stay gone until an explicit re_init (or a kur restart).

Gotchas