@usex/mikrotik-mcp 4.12.0 → 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usex/mikrotik-mcp",
3
- "version": "4.12.0",
3
+ "version": "4.14.0",
4
4
  "description": "MCP server for MikroTik RouterOS — 780+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
5
5
  "keywords": [
6
6
  "ai",
@@ -113,6 +113,7 @@
113
113
  "react-dom": "^19.2.7",
114
114
  "recharts": "^3.9.2",
115
115
  "release-it": "^20.2.1",
116
+ "sonner": "^2.0.3",
116
117
  "tailwind-merge": "^3.6.0",
117
118
  "tailwindcss": "^4.3.2",
118
119
  "tw-animate-css": "^1.4.0",
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: harden-against-bruteforce
3
+ title: Harden a router under SSH/Telnet brute-force attack
4
+ description: Stop an active SSH/Telnet/Winbox brute-force flood on a MikroTik — block the attacking IPs, install an auto-ban ladder + management lock-down, disable telnet — WITHOUT locking the operator out (trusted-IP gate, snapshot, Safe Mode, preview-then-confirm), then check for a compromised admin account.
5
+ arguments:
6
+ - name: device
7
+ description: Which configured device is under attack (config key or label from list_mikrotik_devices). Omit to discover and choose.
8
+ required: false
9
+ - name: management_ip
10
+ description: The IP or CIDR you manage this router from (e.g. 203.0.113.5 or 203.0.113.0/24). This is the anti-lockout gate — it is accepted before any drop. Omit and you will be asked; never proceed without it.
11
+ required: false
12
+ - name: attacker_ips
13
+ description: Comma-separated source IPs already known to be attacking (optional). Leave empty — this workflow reads the device's own error/critical/security logs and extracts the attacking IPs itself; anything you pass here is added on top.
14
+ required: false
15
+ - name: trusted_list_name
16
+ description: Name of the existing trusted/management address-list on this device (e.g. Trust-IP). Omit to use/create a list named "trusted".
17
+ required: false
18
+ ---
19
+
20
+ You have MCP access to a MikroTik RouterOS device that is under an **active
21
+ SSH/Telnet/Winbox brute-force attack**. Harden it against the attack **without
22
+ locking the operator out**. Anti-lockout is the top priority: the operator's own
23
+ management access must be accepted BEFORE any drop, every write goes through a
24
+ snapshot + Safe Mode, and you PREVIEW then wait for explicit approval before
25
+ applying anything.
26
+
27
+ Target device: {{device}}
28
+ Management IP/CIDR (anti-lockout gate): {{management_ip}}
29
+ Trusted address-list: {{trusted_list_name}}
30
+ Extra attacker IPs (optional): {{attacker_ips}}
31
+
32
+ ## 0. Discover the device (do this FIRST)
33
+
34
+ - Call `list_mikrotik_devices`; resolve `{{device}}` against it — never substitute
35
+ a similar name. If it was omitted, present the inventory and ask which router is
36
+ under attack. Pass that `device` on every subsequent call.
37
+
38
+ ## 1. Read and PRINT the attack logs, then extract the attacking IPs
39
+
40
+ - Pull the device's own logs and show them to the operator:
41
+ - `get_security_logs` — login failures and auth events, and
42
+ - `get_logs_by_severity` with severity `critical` and again with `error` — the
43
+ critical/error lines (e.g. `login failure for user … from <IP> via ssh/telnet`,
44
+ `ssh-cmd:admin@<IP> …`, service malfunctions).
45
+ - **Print those error + critical/security lines back to the operator** so they can
46
+ see the attack, then **extract every distinct source IP** from them (the address
47
+ after `from ` on login-failure lines and after `ssh-cmd:<user>@` on script lines).
48
+ Add any IPs given in `{{attacker_ips}}`. Present the deduplicated candidate
49
+ blocklist to the operator with a one-line reason per IP (which log lines it came
50
+ from). Do not write anything yet.
51
+
52
+ ## 2. Anti-lockout gate — make sure YOU are trusted (before any drop)
53
+
54
+ - Read the trusted list: `list_address_lists` filtered to `{{trusted_list_name}}`
55
+ (default `trusted`). Determine whether it exists and which IPs/CIDRs it contains.
56
+ - Decide the operator's whitelist:
57
+ - If `{{management_ip}}` was provided, `add_address_list_entry` it into the trusted
58
+ list (clear comment), then re-read and confirm it is present.
59
+ - If `{{management_ip}}` is empty AND the trusted list is missing or empty,
60
+ **STOP and ASK the operator**: "Which IP/CIDR do you manage this router from? I
61
+ must whitelist it before dropping anything, or you could be locked out." Do NOT
62
+ proceed to any drop until a trusted entry exists. If their IP is dynamic, ask for
63
+ the ISP CIDR block, or advise connecting from a fixed IP / VPN first.
64
+ - Confirm the trusted list now contains at least the operator's own address.
65
+
66
+ ## 3. Block the attacking IPs — but NEVER an entry that is trusted
67
+
68
+ - Cross-check the candidate blocklist from step 1 against the trusted list from
69
+ step 2. **Remove from the blocklist any IP that is in — or is covered by a CIDR in —
70
+ the trusted list**, plus the operator's `{{management_ip}}` and any RFC1918/LAN
71
+ address. NEVER blacklist a trusted or management IP (that is a self-inflicted
72
+ lockout). Show the operator the final block list and what was excluded and why.
73
+ - For each remaining IP, `add_address_list_entry` into the `security-shield-blacklist`
74
+ list (the list `harden_firewall`'s drop rule matches) with a long timeout (e.g. `4w`).
75
+
76
+ ## 4. Snapshot (rollback point)
77
+
78
+ - `capture_config_snapshot` and report the returned **snapshot_id**. Everything
79
+ after this is reversible via `diff_config_snapshots from=<snapshot_id> to=live`.
80
+
81
+ ## 5. PREVIEW the hardening (no writes) — then wait for approval
82
+
83
+ - Call `harden_firewall` with **`apply: false`** and:
84
+ - `preset: "standard"` (accept established/related, drop invalid, SSH/Winbox
85
+ brute-force auto-ban ladder, DDoS connection cap, SYN-flood + ICMP rate limits,
86
+ port-scan detection, anti-spoof),
87
+ - `protections: { raw_drops: true, mgmt_lockdown: true }` (drop blacklisted
88
+ sources before connection tracking, and restrict management ports to trusted),
89
+ - `trusted_sources: "{{trusted_list_name}}"` (the trusted address-list name — or
90
+ the operator's `{{management_ip}}` directly),
91
+ - `ssh_ports: "22"`, `mgmt_ports: "22,8291,8728"`,
92
+ - `safe_mode: true`.
93
+ - Show the operator the exact rules it will add, and confirm the safety accepts
94
+ (established/related + trusted) come BEFORE any drop. Do not proceed until the
95
+ operator approves.
96
+
97
+ ## 6. Apply (only after explicit approval)
98
+
99
+ - Re-issue the same `harden_firewall` call with **`apply: true`, `safe_mode: true`**.
100
+ Safe Mode auto-reverts if a rule cuts the operator's own access. Report what was
101
+ applied and on which device.
102
+
103
+ ## 7. Kill telnet and harden SSH
104
+
105
+ - `disable_ip_service` name=`telnet` — telnet is cleartext and a top brute-force
106
+ target; disable it outright (check `list_ip_services` first).
107
+ - Enable strong SSH crypto: `harden_ssh_service` (or set `strong-crypto=yes` on
108
+ `/ip ssh`; read the current state with `get_ssh_settings` first).
109
+
110
+ ## 8. Verify — and tell the operator to reconnect fresh
111
+
112
+ - `audit_firewall_hardening` (or `run_security_hardening_audit`) — confirm the
113
+ brute-force ladder, drop-blacklisted, raw drops and management lock-down are
114
+ present, and that the trusted/established accepts sit ABOVE the drops.
115
+ - **Instruct the operator to reconnect from a SEPARATE, FRESH session now** to
116
+ confirm they are not locked out. If anything is wrong, roll back with
117
+ `diff_config_snapshots from=<snapshot_id> to=live` and the snapshot tools.
118
+
119
+ ## 9. Check for a compromised admin (if the log showed logins, not just failures)
120
+
121
+ Brute-force logs that show a source actually _executing commands_ as a user (e.g.
122
+ `ssh-cmd:admin@<ip>` running an unexpected command) can mean the account was
123
+ guessed — a known RouterOS botnet pattern. If so:
124
+
125
+ - `list_users` — flag any account the operator doesn't recognise.
126
+ - `list_schedulers` and `list_scripts` — flag anything they didn't create (botnet
127
+ backdoors hide here). Do NOT delete anything without the operator's explicit say-so.
128
+ - Recommend rotating the admin password to a long random one and renaming the
129
+ `admin` user. Consider enabling management-from-trusted-only (already covered by
130
+ `mgmt_lockdown` in step 5) so brute-force can't reach SSH/Winbox at all.
131
+
132
+ ---
133
+
134
+ Hard rules: read and show the logs before deciding anything; **never blacklist an IP
135
+ that is in the trusted list or is the operator's own management IP** — cross-check
136
+ every candidate against the trusted list first; never apply a firewall drop unless
137
+ the operator's IP is in the trusted list (ask for it if unknown, don't guess); always
138
+ snapshot + Safe Mode before writes; preview then wait for explicit approval; state
139
+ plainly which device each change ran on; never delete accounts, schedulers or scripts
140
+ without the operator's confirmation.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "version": "4.12.0",
3
+ "version": "4.14.0",
4
4
  "generated": "by scripts/gen-schemas.ts — do not edit by hand",
5
5
  "toolCount": 821,
6
6
  "tools": [
@@ -12737,18 +12737,14 @@
12737
12737
  "destructiveHint": false,
12738
12738
  "openWorldHint": false
12739
12739
  },
12740
- "description": "Creates an IPv4 firewall FILTER rule (`/ip firewall filter`) — the accept/drop/reject decision table for traffic TO the router (chain=input), THROUGH it (forward) or FROM it (output). Use this to allow or block traffic. For address translation use the NAT tools, for packet marking use mangle, for pre-connection-tracking drops use raw, and for IPv6 use create_ipv6_filter_rule. Rule order matters (first match wins) — use place_before or move_filter_rule to position it. Returns the created rule's detail including its `.id`. connection_state: comma-separated e.g. \"established,related,new,invalid\". limit: RouterOS rate/burst string e.g. \"10,5:packet\" or \"10/1s:packet\". tcp_flags: RouterOS flag expression e.g. \"syn,!ack\". place_before: rule number or ID (*N) to insert before e.g. \"0\" or \"*3\".",
12740
+ "description": "Creates an IPv4 firewall FILTER rule (`/ip firewall filter`) — the accept/drop/reject decision table for traffic TO the router (chain=input), THROUGH it (forward) or FROM it (output). Use this to allow or block traffic. For address translation use the NAT tools, for packet marking use mangle, for pre-connection-tracking drops use raw, and for IPv6 use create_ipv6_filter_rule. `chain` also accepts a CUSTOM sub-chain name (e.g. detect-portscan) reached by an action=jump rule, and `action` supports add-src-to-address-list / add-dst-to-address-list (with `address_list` + `address_list_timeout`) for tagging/detection rules. Rule order matters (first match wins) — use place_before or move_filter_rule to position it. Returns the created rule's detail including its `.id`. connection_state: comma-separated e.g. \"established,related,new,invalid\". limit: RouterOS rate/burst string e.g. \"10,5:packet\" or \"10/1s:packet\". tcp_flags: RouterOS flag expression e.g. \"syn,!ack\". place_before: rule number or ID (*N) to insert before e.g. \"0\" or \"*3\".",
12741
12741
  "inputSchema": {
12742
12742
  "$schema": "https://json-schema.org/draft/2020-12/schema",
12743
12743
  "type": "object",
12744
12744
  "properties": {
12745
12745
  "chain": {
12746
12746
  "type": "string",
12747
- "enum": [
12748
- "input",
12749
- "forward",
12750
- "output"
12751
- ]
12747
+ "description": "The built-in `input`, `forward`, or `output` chain — OR a custom sub-chain name (e.g. `detect-portscan`). A custom chain runs only when a rule with action=jump and jump-target=<that chain> sends traffic into it; use this to build tagging/detection sub-chains."
12752
12748
  },
12753
12749
  "action": {
12754
12750
  "type": "string",
@@ -12761,7 +12757,9 @@
12761
12757
  "passthrough",
12762
12758
  "return",
12763
12759
  "tarpit",
12764
- "fasttrack-connection"
12760
+ "fasttrack-connection",
12761
+ "add-src-to-address-list",
12762
+ "add-dst-to-address-list"
12765
12763
  ]
12766
12764
  },
12767
12765
  "src_address": {
@@ -16044,7 +16042,7 @@
16044
16042
  "destructiveHint": true,
16045
16043
  "openWorldHint": false
16046
16044
  },
16047
- "description": "Installs the user-selected port-scan detection signatures into a dedicated `detect-portscan` sub-chain, gated by a single input jump that EXCLUDES a trusted address-list (so trusted sources are never tagged). These rules only add the source to an address list — they NEVER drop or block. NEVER call this with a guessed or default set of rule_types: it must only be called with signature IDs the user explicitly chose after seeing list_port_scan_detection_signatures — there is no select-all. Requires `trusted_list_name` (must already exist and be non-empty on the device) and the human acknowledgement `confirmed_trusted_list_includes_my_ip=true`. Captures a config snapshot and applies every write inside Safe Mode (auto-revert on session drop). Idempotent — a second identical run adds nothing. Enforcement/blocking of the tagged list is intentionally out of scope.",
16045
+ "description": "Installs the user-selected port-scan detection signatures into a dedicated `detect-portscan` sub-chain, gated by a single input jump that EXCLUDES a trusted address-list (so trusted sources are never tagged). These rules only add the source to an address list — they NEVER drop or block. NEVER call this with a guessed or default set of rule_types: it must only be called with signature IDs the user explicitly chose after seeing list_port_scan_detection_signatures — there is no select-all. Requires `trusted_list_name` (must already exist and be non-empty on the device) and the human acknowledgement `confirmed_trusted_list_includes_my_ip=true`. Captures a config snapshot, then applies every write inside Safe Mode (auto-revert on session drop) — or, if Safe Mode is unavailable or goes silent on the device (a known flaky case on some RouterOS SSH builds), applies the writes DIRECTLY instead of aborting, since these rules only tag and cannot lock you out (the snapshot is the rollback point). Idempotent — a second identical run adds nothing. Enforcement/blocking of the tagged list is intentionally out of scope.",
16048
16046
  "inputSchema": {
16049
16047
  "$schema": "https://json-schema.org/draft/2020-12/schema",
16050
16048
  "type": "object",
@@ -5,11 +5,7 @@
5
5
  "properties": {
6
6
  "chain": {
7
7
  "type": "string",
8
- "enum": [
9
- "input",
10
- "forward",
11
- "output"
12
- ]
8
+ "description": "The built-in `input`, `forward`, or `output` chain — OR a custom sub-chain name (e.g. `detect-portscan`). A custom chain runs only when a rule with action=jump and jump-target=<that chain> sends traffic into it; use this to build tagging/detection sub-chains."
13
9
  },
14
10
  "action": {
15
11
  "type": "string",
@@ -22,7 +18,9 @@
22
18
  "passthrough",
23
19
  "return",
24
20
  "tarpit",
25
- "fasttrack-connection"
21
+ "fasttrack-connection",
22
+ "add-src-to-address-list",
23
+ "add-dst-to-address-list"
26
24
  ]
27
25
  },
28
26
  "src_address": {