TP30 angelegt: Credential-Rotation als Sammel-TP, gebuendelt gegen Projektende

Vier Rotationsposten (MikroTik-admin, IoT-PSK, Shelly-Geraetepasswort,
CF-Access-Token) lagen als verstreute Follow-up-Zeilen herum. Der teure
Teil jeder Rotation sind die Konsumenten (HA-Re-Auth x4, Nautobot-Stack-
Redeploy), nicht das Setzen -- einzeln gemacht zahlt man den mehrfach.
Deshalb ein Sammel-TP mit Ausloesekriterium statt Termin.

- 100 Zeilen Radius-/Etappe-1-Detail aus den Follow-ups nach TP30 verschoben
  (nicht dupliziert), Follow-ups tragen jetzt einen Zeiger
- Etappe 1 (Service-Accounts ha/nautobot) ist durch; admin ist NICHT rotiert
- Stale: "nach TP17.1 sind es weniger Geraete" -- TP17.1 ist verworfen,
  die Geraete bleiben WLAN-Clients, die Rechnung gilt unveraendert
- Handoff 08-24 + Naechster-Schritt-Block als ueberholt markiert

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 06:26:22 +02:00
parent 0e0de74cb2
commit baf97e2d85
2 changed files with 179 additions and 107 deletions

View File

@@ -20,6 +20,12 @@ Fünf Commits: `d245d10`, `e8f7f80`, `0a7f2e2`, `2f18319`, `e9a5b36`.
## 🔴 Womit die nächste Session anfangen sollte
> ⚠️ **ÜBERHOLT am 2026-08-24 (User-Entscheid):** die Rotation ist **nicht** der Sessionstart.
> Sie ist zu **[TP30 — Credential-Rotation](teilprojekte.md)** geworden, einem Sammel-TP, der
> **gegen Projektende gebündelt** abgearbeitet wird — Begründung: es kommen weitere Passwörter
> hinzu, und der teure Teil sind die Konsumenten (HA-Re-Auth, Stack-Redeploy), den man nicht
> mehrfach zahlen will. **Der Inhalt unten bleibt sachlich gültig** und steht jetzt in TP30.
**Passwort-Rotation, Etappe 2.** Die Vorarbeit steht komplett, der Rest ist die einzige Sache mit
echter Dringlichkeit — das `admin`-Passwort ist zweimal in ein Transkript geraten (22.08. und, durch
meinen eigenen Enumerationsfehler, nochmal am 23.08.; **derselbe Wert**, da alle vier Geräte ihn teilen).

View File

@@ -91,10 +91,13 @@ ab jetzt bei jeder Änderung mitgezogen, nicht nur die TP-Abschnitte.)*
> bleibt WLAN-Client; `s9` zurückgebaut) · ✅ **TP17.3 ERLEDIGT** (Ursache **`eco_mode`** bewiesen;
> `s23` + `s26` repariert; **`eco_mode` flottenweit aus**, 22/22 außer `s24`) ·
> 🔜 **TP17.2 aktiviert** (Shelly-Gerätepasswort, an die PSK-Rotation gekoppelt) ·
> 🔑 **Rotation des MikroTik-Admin-Passworts** — seit 08-23 bewusst **hinter** TP17.1 eingereiht.
> 🔑 **Alle Passwort-/Token-Rotationen sind seit 2026-08-24 TP30** (Sammel-TP) — **bewusst
> gebündelt gegen Projektende**, User-Entscheid. Damit ist die ältere Einreihung „hinter TP17.1"
> gegenstandslos (TP17.1 ist verworfen). **Etappe 1 — die Service-Accounts — ist durch;**
> das `admin`-Passwort selbst ist **nicht rotiert** und bleibt es bis dahin bewusst.
> 📌 **Einstieg für die nächste Session: [`handoff-20260824-session.md`](handoff-20260824-session.md)**
> — Start bei der **Passwort-Rotation Etappe 2** (Nautobot + HA auf die neuen Service-Accounts
> umhängen, dann `admin` rotieren). Ältere Sitzungsvorschläge:
> — ⚠️ dessen Empfehlung „Start bei der Passwort-Rotation Etappe 2" ist durch den TP30-Entscheid
> **überholt**; der Rest der Übergabe gilt. Ältere Sitzungsvorschläge:
> [`handoff-20260822-naechste-schritte.md`](handoff-20260822-naechste-schritte.md).
> ✅ **`crs326-core` ist transaktionsfrei** (seit 2026-08-22, geprüft): ein gemeldeter Drift ist ab
> jetzt **echt**, nicht beabsichtigt — `python3 nautobot/gc-transaktion.py status`.
@@ -925,6 +928,167 @@ die Redundanzfrage oben.
- **Abh.:** keine harten. Profitiert von TP25 (Abdeckung/AP-Zuordnung) und TP11 (Monitoring), wenn es
dahin kommt. **Kein Blocker für irgendetwas.**
<a id="tp30"></a>
### TP30 — Credential-Rotation (Sammel-TP) ▸ 🔜 **ANGELEGT 2026-08-24, bewusst GEBÜNDELT gegen Projektende**
> **User-Entscheid 2026-08-24:** „ich würde gerne daraus einen eigenen punkt machen. ich bin mir
> sicher, dass es noch öfters passwörter zu rotieren geben wird. das machen wir dann alles gegen
> projektende."
>
> **Warum ein eigener TP und nicht vier verstreute Follow-up-Zeilen:** die Rotationen teilen sich Werkzeug,
> Fallen und vor allem den **teuren Teil — den Konsumenten** (HA, Nautobot, MikroMCP, `~/.secrets`).
> Wer sie einzeln macht, zahlt den Re-Auth-Aufwand mehrfach. Gebündelt ist es **ein** Durchgang je
> Gerät. Deshalb steht hier die Liste, nicht der Termin.
- **Auslösekriterium (statt Datum):** wenn die offenen TPs abgearbeitet sind bzw. Christoph das
Projekt schließt. 🔴 **Harte Ausnahme, die den Termin vorzieht:** ein *neuer* Leak, ein
fremdzugängliches Gerät oder ein Personenwechsel — dann sofort und einzeln, nicht gebündelt.
| # | Credential | Umfang | Anlass | Vorarbeit | Preis der Rotation |
|---|---|---|---|---|---|
| 1 | **MikroTik `admin`** | core · branch · ap1 · ap2 (**ein** geteilter Wert) | 🔴 **zweimal in ein Transkript geraten** (2026-08-22 via `sshpass -p`; 2026-08-23 durch einen Enumerationsfehler) | ✅ **Etappe 1 durch** (s.u.) | nach Etappe 2 nur noch `~/.secrets/credentials.env` + `~/.claude.json` + MikroMCP-Neustart |
| 2 | **IoT-WLAN-PSK** (`sec-iot`) | `dirtyvader-iot`, 26 Clients | steht **im Klartext** im TP17-Text ⇒ auch in der **Git-Historie**; Redigieren holt ihn dort nicht raus | Hebel `multi-passphrase` am Core verifiziert vorhanden, Tabelle leer | 23 Shellys per Skript + **3 von Hand** (`.249` NSPanel Pro, `.244` Nuki Bridge, `.227` SwitchBot) |
| 3 | **Shelly-Gerätepasswort** | 23 Shellys | Härtung (bisher **keine** Auth auf den Geräten) | ✅ Werkzeug fertig (`migrate.py cmd_auth` + Digest-Fallback) | **je Gerät ein HA-Re-Auth-Flow** — das ist der Aufwand, nicht `SetAuth` |
| 4 | **CF-Access-Service-Token** (`CF_API_TOKEN`) | Cloudflare-Zugang, s. TP14.4 | ⏳ **läuft am 2027-01-09 ohnehin ab** — harter Termin, unabhängig vom Projektende | Runbook liegt (`tp14.4-cf-access-token-runbook.md`) | Token neu ausstellen + `~/.secrets` nachziehen; **account-owned**, anderer verify-Endpoint |
> ℹ️ **#3 ist TP17.2 und bleibt dort inhaltlich zu Hause** — hier steht es, weil es denselben
> Geräte-Durchgang wie #2 benutzt (ein Reboot statt zwei), nicht als Duplikat.
#### Detail — Radius, Etappe 1 und Etappe 2 (am 2026-08-24 aus den Follow-ups hierher gezogen)
**Vorgeschichte des Entscheids** (Stand bis 2026-08-23, User: „machen wir, aber erst später"):
⏭️ ~~**Nachgelagert seit 2026-08-23: erst TP17.1, dann rotieren.**~~
**Revidiert noch am selben Tag (User): beides läuft PARALLEL.** Der Passphrase-Override je MAC
(s.u.) macht den Flag-Day überflüssig, damit war die Reihenfolge nie technisch nötig — nur
ökonomisch. Regel für die Parallelität: **ein Gerät, das ohnehin auf Thread/Zigbee wandert,
bekommt den neuen PSK gar nicht**, sondern nur einen Übergangs-Eintrag in der `access-list`.
⚠️ Der Entscheid betrifft **beide** Passwörter: der **IoT-WLAN-PSK** steht im Klartext im
TP17-Text ([Archiv](teilprojekte-archiv.md#tp17)) und damit auch in der Git-Historie — **bleibt
auf User-Entscheid 2026-08-23 vorerst unverändert**, weil Redigieren ihn nicht aus der Historie
holt und Rotieren heute ~22 Shellys kostet. ~~Nach TP17.1 sind es deutlich weniger.~~
🔴 **Hinfällig seit 2026-08-23: TP17.1 ist verworfen** — die Geräte bleiben WLAN-Clients, es
werden also *nicht* weniger. Die Rechnung „~23 Shellys + 3 Fremdgeräte" gilt unverändert. Anlass: das Core-Passwort ist bei einer Debug-Ausgabe im Klartext in
ein Sitzungstranskript geraten. Ursache war `sshpass -p`, das es in die **Kommandozeile**
schreibt — sichtbar in `ps` und in jedem argv-Trace.
#### 🔑 Radius-Befund 2026-08-23: das Admin-Konto teilen sich FÜNF Konsumenten
Vor dem Rotieren erhoben — das ist deutlich mehr als „vier Geräte + eine Datei":
| Halter | Nachziehen |
|---|---|
| `~/.secrets/credentials.env` | Datei |
| `~/.claude.json` (MikroMCP-Env, `ROUTER_*`/`CRS326_*`) | JSON + MCP-Neustart |
| **Nautobot-Stack** `NAUTOBOT_ROUTEROS_USERNAME/PASSWORD` | Stack-Datei + **Portainer-Redeploy auf der DS923** |
| **HA: VIER `mikrotik`-Integrationen** (`.99.1/.2/.11/.12`, 40 `device_tracker`) | 🔴 **`supports_reconfigure: false` — NICHT skriptbar**, 4× Re-Auth von Hand |
| Repo-Skripte (`routeros/*.py`, `nautobot/*.py`, `docs/tp*.sh`) | ziehen aus `~/.secrets` → automatisch |
⇒ **User-Entscheid 2026-08-23: Konten trennen statt ein Passwort teilen.** Nicht das Rotieren ist
das Problem, sondern dass ein einziges `admin` von fünf Stellen benutzt wird — jede Rotation kostet
sonst wieder vier Re-Auths und einen Stack-Redeploy.
#### ✅ Etappe 1 DURCH (2026-08-23): dedizierte Service-Accounts auf allen vier Geräten
Rein **additiv**, kein laufender Dienst berührt. Vorher existierte **nur** `admin` (Gruppe `full`).
| Gruppe | Policy | User | Zweck |
|---|---|---|---|
| `grp-ha` | `read,api,rest-api,test` | `ha` | HA-Mikrotik-Integration, **lesend** über API 8728 |
| `grp-nautobot` | `ssh,read,write,test,sensitive` | `nautobot` | Golden Config über SSH/Netmiko (Backup **und** Deploy) |
Passwörter: `MIKROTIK_HA_PASS` / `MIKROTIK_NAUTOBOT_PASS` in `~/.secrets/credentials.env`
(32 Zeichen, je Dienst eigen, geräteübergreifend gleich).
🔒 **Angelegt über ein Skript, das die Werte aus der Datei liest** — bewusst *nicht* über
`manage_user`, dessen `password`-Parameter im Sitzungstranskript landen würde.
**Abnahme, auf allen vier Geräten geprüft:**
- `ha` liest (`identity`, `dhcp-lease` — am Core 59 Einträge, genau was das Device-Tracking braucht),
**Schreibversuch wird abgewiesen**.
- `nautobot` meldet sich per SSH an und exportiert; **kein REST** (bewusst kein `rest-api` in der Policy).
- 🎯 **Der Export von `nautobot` ist byte-identisch zu dem von `admin`** (391/56/87/87 Zeilen je Gerät)
⇒ **die GC-Compliance driftet beim Umstellen nicht.** Das war das Hauptrisiko dieser Etappe.
- `/user` wird von **keiner** ComplianceRule abgedeckt ⇒ die neuen Konten erzeugen selbst keinen Drift.
#### ⏭️ Etappe 2 offen — die Konsumenten umhängen, DANN `admin` rotieren
1. **Nautobot** auf `nautobot` umstellen (Stack-Env + Portainer-Redeploy), GC-Backup-Lauf als Abnahme.
2. **HA**: die vier `mikrotik`-Einträge auf den `ha`-User bringen. ⚠️ Reconfigure ist nicht skriptbar;
Entfernen-und-neu-Anlegen **wäre** skriptbar, riskiert aber die 40 `device_tracker`-Entity-IDs
(sie leiten sich aus der MAC ab, kämen also gleich zurück — **aber erst nachdem** die alten
Registry-Einträge weg sind, sonst hängt HA ein `_2` an; dieselbe Falle wie beim Shelly-Rename).
3. **Erst danach** `admin` rotieren — dann berührt es nur noch `~/.secrets` + `~/.claude.json`.
🔴 **Eigener Fehler, protokolliert:** beim Erheben des Radius hat ein `json.dumps`-Aufruf über
`~/.claude.json` das **ap1-Passwort im Klartext ins Transkript geschrieben**. Da alle vier Geräte
dasselbe `admin`-Passwort teilen, ist es **derselbe Wert, der am 2026-08-22 bereits verbrannt ist** —
keine neue Expositionsklasse, aber ein zweiter Beleg dafür, dass die Rotation fällig ist.
**Merkregel: Secrets-Enumeration gibt nur Schlüsselnamen aus, nie Werte** — auch nicht abgeschnitten.
✅ **Vorbedingung erfüllt, geprüft 2026-08-23:** alle vier
Skripte laufen auf `sshpass -e` (`tp6-marker/server.py`, `tp6-monitor-core.sh`,
`tp25-walk-messung.sh`, `tp25-pin-s22.sh`) — die frühere Notiz, zwei davon hätten die Schwäche
noch, war veraltet. Übrig blieb nur **`CLAUDE.md`, das `sshpass -p` weiterhin aktiv als Reboot-Weg
empfahl** — am 2026-08-23 auf `sshpass -e` korrigiert. **Damit ist die Rotation ohne Rückfallgefahr
fahrbar.**
Weg: `manage_user set-password` (⚠️ `/user set*` ist per Deny-Policy blockiert), danach
`~/.secrets/credentials.env` (`ROUTER_*_PASS`/`CRS326_*_PASS`) und `~/.claude.json` nachziehen.
Es ist **ein** Passwort für alle vier Geräte — die „Überlebensversicherung" aus CLAUDE.md.
- 🔎 **Nachtrag 2026-08-23 (Recherche-Session, read-only — kein Eingriff): der PSK in der Doku ist
noch der LIVE konfigurierte.** Gegengeprüft am Manager `crs326-core` (`/interface wifi security`,
Profil `sec-iot` → `cfg-iot` → SSID `dirtyvader-iot`, `datapath.vlan-id=30`,
`wpa2-psk,wpa3-psk`) — und zwar **per Fingerprint statt Klartext** (Länge + drei Zeichenpaare),
damit der Wert nicht ein zweites Mal in ein Transkript wandert. **Diese Methode ist die Merkregel
daraus:** einen Verdacht auf Übereinstimmung prüft man mit Stichproben, nicht durch Ausgeben.
- ✅ **User-Entscheid 2026-08-23 zur Git-Historie: belassen, im Nachtrag dokumentieren** — **kein**
`git filter-repo`. Begründung trägt: genau **eine** getrackte Fundstelle
([Archiv](teilprojekte-archiv.md#tp17)), drei Commits (`e76289c` Ersteintrag 2026-07-11,
`f0d1ff9` 08-08, `7ee64b5` Doku-Split 08-23), Remote **ausschließlich** `gitea.emsenhuber.net`,
**ein** lokaler Klon. Eine History-Rewrite hätte hier also nichts gerettet, was nicht ohnehin nur
auf der eigenen Infrastruktur liegt — und den Preis (alle Hashes neu) für nichts bezahlt.
- 📊 **Warum die Rotation heute teuer ist — live gemessen 2026-08-23:** **26 der 27 WLAN-Clients des
gesamten Netzes hängen auf `dirtyvader-iot`** (`dirtyvader-main` 1, MEDIA/UNTRUSTED/GUEST je 0).
VLAN30 führt **41 Leases/Reservierungen** (5 aktuell `bound`, der Rest sind statische
Reservierungen ausgeschalteter Geräte). Neben den ~26 Shellys hängen dort **P750W** (Drucker),
**Airfryer** (`espressif`, `.180`), **SwitchBot floor-lamp**, **Twinkly** `.228`, **DTUBI** `.8`,
**SONOFF-NSPanelPro** und ein **iPhone** `.177`. ⇒ Ein Passwortwechsel ist heute eine
Batch-Aktion über ~26 Geräte, und **das ist genau die Rechnung, die TP17.1 verkleinert.**
- 🔧 **Zwei Umsetzungshinweise fürs spätere Rotations-Paket:**
**(a)** `mcp/shelly/migrate.py` hat **kein** Rotate-Kommando (nur `migrate`/`deanchor`/`auth`/
`table`) — es bräuchte eine kleine Variante über denselben `set_sta`-Pfad.
**(b) Ein Flag-Day ist vermeidbar:** RouterOS 7 kann in `/interface wifi access-list`
**Passphrase-Overrides je MAC**. Also neuer PSK aufs Profil, Altgeräte übergangsweise per MAC mit
dem alten weiter, danach die Liste leeren. 💡 Nebeneffekt: der TP25-Befund „keine `access-list`"
bekäme damit endlich einen Zweck — und seit dem 08-23 gibt es die ComplianceRule
`wifi-access-list`, die Einträge dort auch sichtbar macht.
- **Mitzuziehen bei der Rotation** (legitime, gitignorete Ablagen desselben Werts):
`~/.secrets/credentials.env` (+ `.bak-20260808`), `routeros/.secrets/wlan-psks.env`,
`mcp/shelly/.env`.
- **CF-Access-Service-Token** — **Rotationsposten #4**, am 2026-08-24 aus den Follow-ups hierher
gezogen (stand dort als „TP14.4 — CF-Access-Service-Token rotieren"). Sachlich gehört er zu
**TP14.4** (CF-Access-Profil für Claude, Runbook `docs/tp14.4-cf-access-token-runbook.md`); hier
steht er, damit die Rotationsliste vollständig ist. ⚠️ Der `CF_API_TOKEN` läuft ohnehin
**2027-01-09** ab — spätestens dann ist dieser Posten fällig, unabhängig vom Projektende.
#### Fallen, die für jede künftige Rotation gelten (nicht neu erheben)
- 🔴 **Niemals `sshpass -p`** — schreibt das Passwort in die Kommandozeile, sichtbar in `ps` und in
jedem argv-Trace. **Genau so entstand Leak #1.** `-e` hält es in der Umgebung des Kindprozesses.
Der Repo-Stand ist bereits durchgängig `-e`.
- 🔴 **Passwörter nie per MCP-`password`-Parameter setzen**, sondern per Skript aus `~/.secrets`
(`manage_user set-password`; `/user set*` ist per Deny-Policy blockiert).
- 🔴 **Keine Secrets-Enumeration im Transkript** — das war Leak #2, ohne dass irgendein Werkzeug
fehlerhaft war.
- 🔒 **`wifi-security` ist als ComplianceRule grün, aber NICHT deploybar** (Passphrasen stehen als
`<<REDACTED>>` im Template). Ist-Stand für #2 immer per REST vom Gerät lesen
(`GET http://10.40.99.1/rest/interface/wifi/security`), **nie** aus `wlan-psks.env`.
- ⚠️ **CAPsMAN, für #2:** eine Profiländerung wirkt erst nach `remote-cap provision` für **beide**
APs — und die Abnahme ist eine **frische DHCP-Lease je SSID**, nicht die Registration-Table.
- ⚠️ **Für #2 zuerst `multi-passphrase` gegen WPA3/SAE testen — auf einer Wegwerf-SSID, nie an
`dirtyvader-iot`.** `sec-iot` fährt `wpa2-psk,wpa3-psk`; trägt multi-passphrase SAE nicht, sperrt
man **26 IoT-Geräte auf einmal** aus, inklusive Nuki und NSPanel.
- ⚠️ **`s1`/`s2`/`s3` sind ausgesteckt** und kommen im Dezember mit dem **alten** PSK zurück — der
Übergangs-Eintrag muss sie überleben oder sie werden bei Rückkehr von Hand nachgezogen.
- 🔴 **HA- und Repo-Nummerierung der Shellys weichen ab** (`s10`, `s17`) — **immer über MAC/IP
zuordnen, nie über die s-Nummer.**
- **Abh.:** keine harten; #2 und #3 laufen sinnvollerweise in **einem** Geräte-Durchgang.
**Blockiert nichts.** Die frühere Kopplung „erst TP17.1, dann rotieren" ist **gegenstandslos** —
TP17.1 ist am 2026-08-23 verworfen, die Geräte bleiben WLAN-Clients.
---
## Abgeschlossene Teilprojekte ▸ [`teilprojekte-archiv.md`](teilprojekte-archiv.md)
@@ -1046,109 +1210,10 @@ den Weg, über den man den Fehler zurücknehmen wollte.
Re-Walk + Dead-Spot-Kartierung laufen als Schritt 5 von TP25 — und zwar erst **nach** den Funkparameter-Fixes,
weil vorher gemessene Werte wertlos sind.
- **TP10 —** DDNS-Auto-Updater für `bf4.emsenhuber.net` (auf dem Core möglich, `device-mode` erlaubt scheduler).
- 🔑 **Admin-Passwort der vier MikroTik rotieren (offen seit 2026-08-22, User: „machen wir,
aber erst später").** ⏭️ ~~**Nachgelagert seit 2026-08-23: erst TP17.1, dann rotieren.**~~
**Revidiert noch am selben Tag (User): beides läuft PARALLEL.** Der Passphrase-Override je MAC
(s.u.) macht den Flag-Day überflüssig, damit war die Reihenfolge nie technisch nötig — nur
ökonomisch. Regel für die Parallelität: **ein Gerät, das ohnehin auf Thread/Zigbee wandert,
bekommt den neuen PSK gar nicht**, sondern nur einen Übergangs-Eintrag in der `access-list`.
⚠️ Der Entscheid betrifft **beide** Passwörter: der **IoT-WLAN-PSK** steht im Klartext im
TP17-Text ([Archiv](teilprojekte-archiv.md#tp17)) und damit auch in der Git-Historie — **bleibt
auf User-Entscheid 2026-08-23 vorerst unverändert**, weil Redigieren ihn nicht aus der Historie
holt und Rotieren heute ~22 Shellys kostet. Nach TP17.1 sind es deutlich weniger. Anlass: das Core-Passwort ist bei einer Debug-Ausgabe im Klartext in
ein Sitzungstranskript geraten. Ursache war `sshpass -p`, das es in die **Kommandozeile**
schreibt — sichtbar in `ps` und in jedem argv-Trace.
---
#### 🔑 Radius-Befund 2026-08-23: das Admin-Konto teilen sich FÜNF Konsumenten
Vor dem Rotieren erhoben — das ist deutlich mehr als „vier Geräte + eine Datei":
| Halter | Nachziehen |
|---|---|
| `~/.secrets/credentials.env` | Datei |
| `~/.claude.json` (MikroMCP-Env, `ROUTER_*`/`CRS326_*`) | JSON + MCP-Neustart |
| **Nautobot-Stack** `NAUTOBOT_ROUTEROS_USERNAME/PASSWORD` | Stack-Datei + **Portainer-Redeploy auf der DS923** |
| **HA: VIER `mikrotik`-Integrationen** (`.99.1/.2/.11/.12`, 40 `device_tracker`) | 🔴 **`supports_reconfigure: false` — NICHT skriptbar**, 4× Re-Auth von Hand |
| Repo-Skripte (`routeros/*.py`, `nautobot/*.py`, `docs/tp*.sh`) | ziehen aus `~/.secrets` → automatisch |
⇒ **User-Entscheid 2026-08-23: Konten trennen statt ein Passwort teilen.** Nicht das Rotieren ist
das Problem, sondern dass ein einziges `admin` von fünf Stellen benutzt wird — jede Rotation kostet
sonst wieder vier Re-Auths und einen Stack-Redeploy.
#### ✅ Etappe 1 DURCH (2026-08-23): dedizierte Service-Accounts auf allen vier Geräten
Rein **additiv**, kein laufender Dienst berührt. Vorher existierte **nur** `admin` (Gruppe `full`).
| Gruppe | Policy | User | Zweck |
|---|---|---|---|
| `grp-ha` | `read,api,rest-api,test` | `ha` | HA-Mikrotik-Integration, **lesend** über API 8728 |
| `grp-nautobot` | `ssh,read,write,test,sensitive` | `nautobot` | Golden Config über SSH/Netmiko (Backup **und** Deploy) |
Passwörter: `MIKROTIK_HA_PASS` / `MIKROTIK_NAUTOBOT_PASS` in `~/.secrets/credentials.env`
(32 Zeichen, je Dienst eigen, geräteübergreifend gleich).
🔒 **Angelegt über ein Skript, das die Werte aus der Datei liest** — bewusst *nicht* über
`manage_user`, dessen `password`-Parameter im Sitzungstranskript landen würde.
**Abnahme, auf allen vier Geräten geprüft:**
- `ha` liest (`identity`, `dhcp-lease` — am Core 59 Einträge, genau was das Device-Tracking braucht),
**Schreibversuch wird abgewiesen**.
- `nautobot` meldet sich per SSH an und exportiert; **kein REST** (bewusst kein `rest-api` in der Policy).
- 🎯 **Der Export von `nautobot` ist byte-identisch zu dem von `admin`** (391/56/87/87 Zeilen je Gerät)
⇒ **die GC-Compliance driftet beim Umstellen nicht.** Das war das Hauptrisiko dieser Etappe.
- `/user` wird von **keiner** ComplianceRule abgedeckt ⇒ die neuen Konten erzeugen selbst keinen Drift.
#### ⏭️ Etappe 2 offen — die Konsumenten umhängen, DANN `admin` rotieren
1. **Nautobot** auf `nautobot` umstellen (Stack-Env + Portainer-Redeploy), GC-Backup-Lauf als Abnahme.
2. **HA**: die vier `mikrotik`-Einträge auf den `ha`-User bringen. ⚠️ Reconfigure ist nicht skriptbar;
Entfernen-und-neu-Anlegen **wäre** skriptbar, riskiert aber die 40 `device_tracker`-Entity-IDs
(sie leiten sich aus der MAC ab, kämen also gleich zurück — **aber erst nachdem** die alten
Registry-Einträge weg sind, sonst hängt HA ein `_2` an; dieselbe Falle wie beim Shelly-Rename).
3. **Erst danach** `admin` rotieren — dann berührt es nur noch `~/.secrets` + `~/.claude.json`.
🔴 **Eigener Fehler, protokolliert:** beim Erheben des Radius hat ein `json.dumps`-Aufruf über
`~/.claude.json` das **ap1-Passwort im Klartext ins Transkript geschrieben**. Da alle vier Geräte
dasselbe `admin`-Passwort teilen, ist es **derselbe Wert, der am 2026-08-22 bereits verbrannt ist** —
keine neue Expositionsklasse, aber ein zweiter Beleg dafür, dass die Rotation fällig ist.
**Merkregel: Secrets-Enumeration gibt nur Schlüsselnamen aus, nie Werte** — auch nicht abgeschnitten.
✅ **Vorbedingung erfüllt, geprüft 2026-08-23:** alle vier
Skripte laufen auf `sshpass -e` (`tp6-marker/server.py`, `tp6-monitor-core.sh`,
`tp25-walk-messung.sh`, `tp25-pin-s22.sh`) — die frühere Notiz, zwei davon hätten die Schwäche
noch, war veraltet. Übrig blieb nur **`CLAUDE.md`, das `sshpass -p` weiterhin aktiv als Reboot-Weg
empfahl** — am 2026-08-23 auf `sshpass -e` korrigiert. **Damit ist die Rotation ohne Rückfallgefahr
fahrbar.**
Weg: `manage_user set-password` (⚠️ `/user set*` ist per Deny-Policy blockiert), danach
`~/.secrets/credentials.env` (`ROUTER_*_PASS`/`CRS326_*_PASS`) und `~/.claude.json` nachziehen.
Es ist **ein** Passwort für alle vier Geräte — die „Überlebensversicherung" aus CLAUDE.md.
- 🔎 **Nachtrag 2026-08-23 (Recherche-Session, read-only — kein Eingriff): der PSK in der Doku ist
noch der LIVE konfigurierte.** Gegengeprüft am Manager `crs326-core` (`/interface wifi security`,
Profil `sec-iot` → `cfg-iot` → SSID `dirtyvader-iot`, `datapath.vlan-id=30`,
`wpa2-psk,wpa3-psk`) — und zwar **per Fingerprint statt Klartext** (Länge + drei Zeichenpaare),
damit der Wert nicht ein zweites Mal in ein Transkript wandert. **Diese Methode ist die Merkregel
daraus:** einen Verdacht auf Übereinstimmung prüft man mit Stichproben, nicht durch Ausgeben.
- ✅ **User-Entscheid 2026-08-23 zur Git-Historie: belassen, im Nachtrag dokumentieren** — **kein**
`git filter-repo`. Begründung trägt: genau **eine** getrackte Fundstelle
([Archiv](teilprojekte-archiv.md#tp17)), drei Commits (`e76289c` Ersteintrag 2026-07-11,
`f0d1ff9` 08-08, `7ee64b5` Doku-Split 08-23), Remote **ausschließlich** `gitea.emsenhuber.net`,
**ein** lokaler Klon. Eine History-Rewrite hätte hier also nichts gerettet, was nicht ohnehin nur
auf der eigenen Infrastruktur liegt — und den Preis (alle Hashes neu) für nichts bezahlt.
- 📊 **Warum die Rotation heute teuer ist — live gemessen 2026-08-23:** **26 der 27 WLAN-Clients des
gesamten Netzes hängen auf `dirtyvader-iot`** (`dirtyvader-main` 1, MEDIA/UNTRUSTED/GUEST je 0).
VLAN30 führt **41 Leases/Reservierungen** (5 aktuell `bound`, der Rest sind statische
Reservierungen ausgeschalteter Geräte). Neben den ~26 Shellys hängen dort **P750W** (Drucker),
**Airfryer** (`espressif`, `.180`), **SwitchBot floor-lamp**, **Twinkly** `.228`, **DTUBI** `.8`,
**SONOFF-NSPanelPro** und ein **iPhone** `.177`. ⇒ Ein Passwortwechsel ist heute eine
Batch-Aktion über ~26 Geräte, und **das ist genau die Rechnung, die TP17.1 verkleinert.**
- 🔧 **Zwei Umsetzungshinweise fürs spätere Rotations-Paket:**
**(a)** `mcp/shelly/migrate.py` hat **kein** Rotate-Kommando (nur `migrate`/`deanchor`/`auth`/
`table`) — es bräuchte eine kleine Variante über denselben `set_sta`-Pfad.
**(b) Ein Flag-Day ist vermeidbar:** RouterOS 7 kann in `/interface wifi access-list`
**Passphrase-Overrides je MAC**. Also neuer PSK aufs Profil, Altgeräte übergangsweise per MAC mit
dem alten weiter, danach die Liste leeren. 💡 Nebeneffekt: der TP25-Befund „keine `access-list`"
bekäme damit endlich einen Zweck — und seit dem 08-23 gibt es die ComplianceRule
`wifi-access-list`, die Einträge dort auch sichtbar macht.
- **Mitzuziehen bei der Rotation** (legitime, gitignorete Ablagen desselben Werts):
`~/.secrets/credentials.env` (+ `.bak-20260808`), `routeros/.secrets/wlan-psks.env`,
`mcp/shelly/.env`.
- **TP14.4 —** CF-Access-Service-Token rotieren.
- 🔑 **Alle Passwort-/Token-Rotationen laufen seit 2026-08-24 unter [TP30](#tp30) — Sammel-TP,
gebündelt gegen Projektende (User-Entscheid).** Dort steht die Rotationsliste (MikroTik `admin`,
IoT-PSK, Shelly-Gerätepasswort, CF-Access-Token), der Radius der fünf Konsumenten, der
Etappe-1-Abschluss und die Fallenliste. **Etappe 1 ist durch**, Etappe 2 wartet bewusst.
- ~~**Doku-Aufräumen** (`docs/doku-cleanup-session.md`)~~ ✅ **abgeschlossen 2026-08-23.** Punkte A–D
(08-16), E (08-18/19) und **F — der Split** (08-23) sind durch: abgeschlossene TPs liegen jetzt in
[`teilprojekte-archiv.md`](teilprojekte-archiv.md), TP25 ist von 394 auf ~100 Zeilen kondensiert,
@@ -1161,7 +1226,8 @@ den Weg, über den man den Fehler zurücknehmen wollte.
**TP8** (Identity/802.1X) · **TP11** (Monitoring) · **TP12** (IPv6) · **TP13** (Botschafts-WLANs) ·
**TP16.2** (Härtung bypass-Dienste) · **TP18** (Garage-Objektspeicher) · **TP20.1** (Vaultwarden → Platform-PG) ·
**TP23** (DNS-Policy, s. Regel oben).
**TP23** (DNS-Policy, s. Regel oben) · **TP30** (Credential-Rotation — nicht research-, sondern
**termin**-gated: gebündelt gegen Projektende, User-Entscheid 2026-08-24).
*(**TP17.1** — Shelly Gen4 → Thread, aktiviert 2026-08-23 — läuft **nicht hier**, sondern im
HA-Projekt `~/dev/ha-xstate`; netzseitig ist nichts zu bauen. Es blockt bewusst die PSK-Rotation.)*
*(**TP16.1** stand hier bis 2026-08-17 fälschlich als offen — es ist seit demselben Tag ✅ abgeschlossen und