generated from atu/saiop-infrastructure
Add saiop-mikrotik-mcp skill: TLS/cert debugging notes, verified read-only setup
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
name: saiop-mikrotik-mcp
|
||||||
|
description: "MikroTik (RouterOS) read-only MCP access for rt-alpha — TLS debugging notes and the read-only verification method."
|
||||||
|
version: 1.0.0
|
||||||
|
author: Claude Code (SAIOP ops session)
|
||||||
|
license: MIT
|
||||||
|
platforms: [linux]
|
||||||
|
prerequisites:
|
||||||
|
env_vars: [MIKROTIK_PASSWORD]
|
||||||
|
commands: [uv]
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
tags: [SAIOP, MCP, MikroTik, RouterOS, network, security]
|
||||||
|
---
|
||||||
|
|
||||||
|
# MikroTik (rt-alpha) read-only MCP access
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
Querying the ATU network's MikroTik router (`rt-alpha`,
|
||||||
|
`169.239.248.166`) for read-only diagnostics (interfaces, routes, DHCP
|
||||||
|
leases, firewall rules) via Hermes.
|
||||||
|
|
||||||
|
## What's wired up
|
||||||
|
|
||||||
|
A custom single-tool MCP server (`/opt/ai-stack/hermes/scripts/mikrotik_mcp.py`,
|
||||||
|
run via `uv run --with mcp --with librouteros`) connects via the RouterOS
|
||||||
|
API over TLS (port 8729) as a dedicated `claudeuser` account.
|
||||||
|
|
||||||
|
**Two independent layers, same pattern as postgres-mcp/proxmox-mcp:**
|
||||||
|
1. The `claudeuser` RouterOS account itself is read-only — **independently
|
||||||
|
verified**, not assumed: a real write attempt (`/interface/set` to
|
||||||
|
change a comment) returned `TrapError: not enough permissions (9)`
|
||||||
|
directly from the device's permission system.
|
||||||
|
2. The MCP tool also only accepts paths ending in `/print`, `/getall`, or
|
||||||
|
`/export` (rejects anything else before even attempting the call) —
|
||||||
|
defense-in-depth on top of #1, not the primary boundary.
|
||||||
|
|
||||||
|
## TLS gotchas hit getting here (useful if this recurs for other network gear)
|
||||||
|
|
||||||
|
- Initial connection attempts failed with a TLS handshake failure. Root
|
||||||
|
cause: RouterOS's `api-ssl` service had **no certificate assigned**, so
|
||||||
|
it fell back to **anonymous Diffie-Hellman ciphers** (`ADH-AES256-SHA256`)
|
||||||
|
— meaning no server authentication at all. Confirmed via raw
|
||||||
|
`openssl s_client -cipher "ALL:@SECLEVEL=0" -tls1_2`, which negotiated
|
||||||
|
successfully where every normal client library failed (modern TLS
|
||||||
|
stacks don't enable anonymous ciphers the same permissive way the CLI
|
||||||
|
does, even with matching settings — spent real effort confirming this
|
||||||
|
was the device's config, not a bug in our approach, before escalating
|
||||||
|
to ask for a router-side fix).
|
||||||
|
- **The actual fix was on the device, not the client**: assigning a real
|
||||||
|
certificate to the `api-ssl` service
|
||||||
|
(`/certificate add` + `/certificate sign` + `/ip service set api-ssl
|
||||||
|
certificate=...`). Once that landed, standard TLS negotiated cleanly —
|
||||||
|
no special cipher/version pinning needed in the client at all.
|
||||||
|
- Even with a real cert, it had its own separate issues (expired,
|
||||||
|
CN mismatch vs. the hostname used to connect) — handled with
|
||||||
|
`verify_mode = ssl.CERT_NONE` in the client, since we already have
|
||||||
|
independent assurance of which device we're talking to (the ~1ms ping
|
||||||
|
latency confirms it's on a directly-attached local segment, not
|
||||||
|
reachable for interception the way a public-internet path would be).
|
||||||
|
Worth a routine cert renewal on the network side regardless.
|
||||||
|
- `librouteros`'s `Api.__call__` takes the RouterOS path **positionally**,
|
||||||
|
not as a `cmd=` keyword (`api("/interface/print")`, not
|
||||||
|
`api(cmd="/interface/print")`) — easy mistake, produces a confusing
|
||||||
|
`TypeError: missing 1 required positional argument`.
|
||||||
|
|
||||||
|
## Why a custom server instead of `mcp-server-mikrotik`
|
||||||
|
|
||||||
|
That package exists and is actively maintained, but it's the wrong shape
|
||||||
|
for this need: it connects via **SSH and an interactive shell**, uses
|
||||||
|
RouterOS's own "Safe Mode" (a revert-on-disconnect safety net for *live
|
||||||
|
config changes*, not a read-only restriction), and exposes broad
|
||||||
|
read-write scope across firewall/DHCP/DNS/NAT/backup management. Writing
|
||||||
|
~70 lines against the already-verified API connection was less work and
|
||||||
|
matches the actual need (read-only diagnostics) far more precisely — same
|
||||||
|
reasoning as the `proxmox-mcp` custom server earlier in this project.
|
||||||
Reference in New Issue
Block a user