MakerFLOSS_Mikrotik/CLAUDE.md
sjat d96ec189d4 fix: repair stdout callback + refresh the access recipe
community.general 12 removed the `yaml` stdout callback, so every ansible
invocation died with a [DEPRECATED] error. Use the core default callback
with `callback_result_format = yaml` — identical output, no collection dep.

CLAUDE.md's access recipe still described the retired `-J kuku … 10.8.0.4`
hop; `mamba` is now an ssh alias over the wg overlay. Also record two things
that cost time on the 2026-09-01 bench session: the vaulted admin password
cannot authenticate over SSH at all (RouterOS refuses password auth for a
user holding an SSH key while always-allow-password-login=no), so each
operator seat needs its own key imported; and the checked-in .venv carries
shebangs from the original /home/sjat path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EyYJWABgSEHzbjsZGrkxVE
2026-09-01 20:20:46 +02:00

5.6 KiB

MakerFLOSS_Mikrotik

Ansible IaC for one MikroTik CRS310-8G+2S+IN switch (RouterOS 7) at the makerspace, managed over SSH with community.routeros. Sibling project to AnsibleBaobabV4 (whose conventions this repo copies); independent repo on forgejo.makerfloss.eu.

Tech stack

  • Ansible 10.x / ansible-core 2.17, community.routeros 3.x + ansible.netcommon
  • Connection: ansible.netcommon.network_cli, ansible_network_os: community.routeros.routeros, SSH key auth
  • Vault identity makerfloss (~/.ansible/vault-keys/makerfloss.txt)
  • Lint: ansible-lint (profile: production), yamllint

Structure

  • inventories/prod/hosts.yml — group mikrotik, host crs310-maker
  • group_vars/mikrotik.yml — connection vars + switch_*_enabled flags
  • group_vars/mikrotik.vault.yml — encrypted password (excluded from linters)
  • host_vars/crs310-maker.yml — device facts, real addressing, VLAN/port map
  • roles/makerfloss.mikrotik_switch/ — one role, per-domain task files gated by flags
  • play_switch.yml (day-2), play_bootstrap.yml (first contact), play_backup.yml
  • docs/ — field guide, design spec, implementation plan

Essential commands

yamllint . && ansible-lint && ansible-playbook play_switch.yml --syntax-check
ansible-playbook play_switch.yml                       # day-2 (key auth)
ansible-playbook play_switch.yml --tags vlans          # one domain
ansible-vault view group_vars/mikrotik.vault.yml       # read a secret

Access (on-site / bench)

The switch is reachable only via the makerspace laptop mamba. Ansible's network_cli uses paramiko, which ignores ProxyJump, so port-forward instead of double-hopping:

ssh -N -L 2222:192.168.88.1:22 mamba                            # tunnel to the switch
ansible-playbook play_switch.yml -e ansible_host=127.0.0.1 -e ansible_port=2222
ssh-keygen -R '[127.0.0.1]:2222'                                # if the tunnel host key changed
  • mamba is an ssh alias from boma's ssh_client drop-in (10.99.0.10:7576 over the wg overlay). It superseded the old -J kuku … sjat@10.8.0.4 hop; that path is dead.
  • ansible_user: sjat already comes from host_vars, so don't pass it on the CLI.
  • Every operator seat needs its own key on the device — key auth is the only way in (see Rules). ubongo's claude@ubongo key was added 2026-09-01; a new seat repeats: scp -O key.pub sjat@192.168.88.1: then /user/ssh-keys/import public-key-file=key.pub user=sjat, and delete the uploaded file.
  • mamba is the mgmt station on switch port 8 (MGMT VLAN); it must be on port 8 to reach 192.168.88.1. From a data port it gets 10.2.30.x and cannot reach mgmt.
  • NM profiles on mamba enp0s31f6: crs310-bench (static .2) and Wired connection 1 (DHCP). Moving the cable flaps the link and NM re-selects a profile — pin the intended one sticky (autoconnect yes + higher priority) and the other off, or it reverts. Either profile works now that the mgmt VLAN serves DHCP (.253 from the pool).
  • The .venv was built under /home/sjat/…; on a checkout at another path its console scripts fail with exit 126 (stale shebang). Fix the shebangs or rebuild the venv — the system ansible is not a substitute (no paramiko, newer community.general).

Rules

  • Idempotency: RouterOS tasks use community.routeros.command with :if [find] guards. Run every device-touching play twice; the second run must report no changes.
  • Lockout safety: keep an independent recovery channel (serial/WinBox-MAC) when touching mgmt/services/VLANs; enable vlan-filtering last. For lockout-prone changes over the network (vlan-filtering, moving the mgmt IP), run them as a detached self-reverting job — :execute { …; :delay 240s; :if ($mgmtok=false) do={ revert } }, then :global mgmtok true once verified. (Auto-healed a hard lockout during the cutover.)
  • RouterOS find ... address=<prefix> never matches an ip/address or dhcp-network value (returns 0 even on an exact string) — match by [find interface=X] or :foreach+/ip/address/get $a address. Bit the mgmt-IP move (duplicated the IP).
  • All real values go in host_vars; the role holds only mechanism + placeholders.
  • Secrets go to the makerfloss vault, never plaintext. Encrypt with ansible-vault encrypt --encrypt-vault-id makerfloss <file>.
  • vault_switch_admin_password cannot log in over SSH and is console/recovery-only. RouterOS refuses password auth for any user that has an SSH key while /ip/ssh always-allow-password-login=no (the default, and deliberately kept). So play_bootstrap.yml's password is a one-shot for user creation; after the key import the only SSH path is key auth. Never "fix" a failed login by flipping that flag.
  • New work: branch first, implement, verify (lint + syntax + run-twice), then merge.

Status / next

Live on the device (2026-06-09): flat L2 switch on 10.2.30.0/24 — DATA VLAN 30 (ether1 copper uplink + ether2-7 + SFP+), isolated MGMT VLAN 99 on ether8 (mgmt 192.168.88.1/24, no gateway/NTP/DNS), vlan-filtering on. The mgmt port also serves DHCP (192.168.88.10-.254) + the web UI as a makerspace experiment (flags switch_web_enabled, switch_mgmt_dhcp_enabled). Default admin disabled; login as sjat (key, or vaulted password). All task files + play_bootstrap/play_backup are idempotency-verified. Design + cutover runbook: docs/superpowers/specs/2026-06-09-crs310-flat-mgmtvlan-design.md.

Next: SFP+ 10G uplink and real VLAN segmentation once connectors + a VLAN plan are ready.