tailscale: auf natives Plugin konsolidieren, redundanten Docker-Stack entfernen, ACL-Haertung dokumentieren

- host-services/tailscale/ (userspace-only Docker-Stack) entfernt; Komodo stop/destroy durch Operator, danach git rm
- Glance-Widget Tailscale-Docker entfernt
- HOMELAB_ARCHITECTURE/SERVICE_CATALOG/DISASTER_RECOVERY/CLAUDE/RESTORE_MATRIX: tailscale als natives Unraid-Plugin dokumentiert; Restore-State-Pfad korrigiert auf /boot/config/plugins/tailscale/state (Flash-Backup)
- NETWORK_INVENTORY: restriktive tag-basierte grants-ACL (2026-06-06; tag:server/tag:operator, tag:family vorbereitet) und Subnet-Router-Befund dokumentiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 10:58:59 +02:00
parent 023ee63687
commit c3491eb382
9 changed files with 105 additions and 94 deletions
+90 -56
View File
@@ -55,7 +55,7 @@ Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
| Tailscale IPv6 | `fd7a:115c:a1e0::2c01:62b2` (gemessen 2026-06-05) |
| Exit Node | **Nein.** `Self.ExitNodeOption: false` und `Self.ExitNode: false` — Host bietet keinen Exit Node an und nutzt keinen. Entspricht dem Ziel (Operator-Zugang ist eingehend, nicht als Internet-Ausgang). |
| Subnet Router | **Ja, aktiv.** Host advertised und ist Primary fuer `192.168.178.0/24` (`Self.PrimaryRoutes: ["192.168.178.0/24"]`, ebenfalls in `AllowedIPs`). Das LAN ist also fuer das gesamte Tailnet ueber diesen Subnet-Router erreichbar — bewusst gemessener Ist-Zustand, **kein** "keine Route" wie zuvor vermutet. |
| ACL-Policy extern dokumentiert | **Operator-Entscheidung offen** — siehe eigener Block unten; durch den aktiven Subnet-Router ist die ACL-Frage sicherheitsrelevanter als zuvor angenommen. |
| ACL-Policy extern dokumentiert | **Angewendet 2026-06-06** — restriktive Tag-basierte `grants`-Policy live (`tag:server`/`tag:operator`, `tag:family` schlafend). Default-Allow entfernt, verifiziert. Details im Block unten. |
### Tailnet-Geraete (Snapshot 2026-06-05)
@@ -65,13 +65,37 @@ Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
| `100.78.133.37` | baerchen-1 | windows | aktiv (aktuelle Operator-Workstation, direct) |
| `100.105.203.21` | baerchen | windows | offline, zuletzt vor ~1 Tag gesehen (Alt-Node) |
| `100.73.83.55` | iphone-14 | iOS | bekannt |
| `100.112.0.90` | kallilab-core | linux | gelistet, kein aktiver Verkehr — **moeglicher Alt-/Dubletten-Node**, separat pruefen |
| `100.112.0.90` | kallilab-core | linux | **am 2026-06-06 entfernt.** War der redundante userspace-only `Tailscale-Docker`-Stack (`host-services/tailscale/`). Komodo-Stack gestoppt+destroyed, Repo-Pfad per `git rm` entfernt, Container weg (read-only verifiziert). Node-Eintrag in der Admin-Konsole noch zu entfernen. |
> Hygiene-Hinweis (kein Secret): Es existieren zwei linux-Nodes mit aehnlichem
> Namen (`kallilabcore` und `kallilab-core`) sowie zwei `baerchen`-Nodes
> (`baerchen-1` aktiv, `baerchen` offline). Bei Gelegenheit in der
> Tailscale-Admin-Konsole pruefen, ob die inaktiven Eintraege stillgelegt werden
> koennen. Das ist Aufraeumarbeit, kein akutes Risiko.
> **Befund 2026-06-06 (read-only auf dem Host ermittelt):** Der Host hat **zwei**
> `tailscaled`-Prozesse:
>
> 1. **Native Unraid-Plugin** = `kallilabcore` (100.80.98.33). Prozess
> `/usr/local/sbin/tailscaled -statedir /boot/config/plugins/tailscale/state
> -tun tailscale1`. **Echtes TUN-Interface `tailscale1`, ist der Subnet-Router
> fuer `192.168.178.0/24`**, laeuft seit 24. Mai, installiert via
> `tailscale.plg` + `unraid-tailscale-utils`. State unter
> `/boot/config/plugins/tailscale/state` → ueber das **Flash-Backup** gesichert.
> Im ACL-Rollout `tag:server`. **Das ist die funktionale, kanonische Instanz.**
> 2. **Docker-Stack** = `kallilab-core` (100.112.0.90), `host-services/tailscale/`.
> Prozess `tailscaled --tun=userspace-networking` → **nur Userspace, kann
> technisch nicht routen / kein Subnet-Router/Exit-Node sein**, advertised
> nichts, kein Container teilt seinen Namespace, seit 31. Mai. State unter
> `/mnt/user/appdata/tailscale`. Im ACL-Rollout untagged → isoliert.
> **Hochwahrscheinlich redundant.**
>
> **Umgesetzt 2026-06-06:** Der redundante Docker-Stack `host-services/tailscale/`
> wurde sauber per GitOps abgebaut — Komodo-Stack `tailscale` gestoppt+destroyed
> (Operator), `git rm host-services/tailscale/`, Glance-Widget entfernt, und
> Architektur-/Service-Catalog-/DR-/CLAUDE-Doku auf "natives Plugin" nachgezogen.
> Read-only verifiziert: Container weg, nur noch der native `tailscaled` mit
> `tailscale1`, Subnet-Route + Operator-Zugriff intakt. Offen: Node-Eintraege
> `kallilab-core` und alter `baerchen` in der Admin-Konsole entfernen; State-Pfad
> `/mnt/user/appdata/tailscale` bei Gelegenheit nach `_archive/` (kein Sofort-Loeschen).
>
> **Doku-Korrektur erledigt:** `docs/RESTORE_MATRIX.md` zeigt jetzt auf den
> funktionalen State `/boot/config/plugins/tailscale/state` (im Flash-Backup)
> statt auf den entfernten userspace-Docker-Pfad.
### Subnet-Router-Konsequenz
@@ -91,34 +115,22 @@ tailscale ip -4
tailscale ip -6
```
### ACL-Policy — Entwurf und Rollout-Plan (Stand 2026-06-05, NICHT angewendet)
### ACL-Policy — ANGEWENDET 2026-06-06 (restriktive Tag-basierte grants)
Die Tailnet-ACL wird in der Tailscale-Admin-Konsole unter `Access controls`
verwaltet (kein Wert/Secret gehoert ins Repo). Aktueller Live-Stand ist
Default-Allow (`src: ["*"] -> dst: ["*:*"]`), d. h. jedes Tailnet-Geraet darf
alles inklusive der LAN-Subnet-Route.
**Status: live und verifiziert.** Die restriktive Policy wurde am 2026-06-06
gemeinsam mit dem Operator in der lockout-sicheren Reihenfolge ausgerollt und
read-only verifiziert (siehe "Rollout-Protokoll" unten). Ausgangspunkt war die
**unveraenderte Default-Policy** im **`grants`-Schema** (eine Allow-all-Regel,
keine Groups/Tags/`autoApprovers`); es gab also keinen eigenen Bestand zu
erhalten.
**Abgestimmte Richtung (Operator-Entscheidungen 2026-06-05):**
> **Schema-Hinweis:** Dieses Tailnet nutzt das `grants`-Modell
> (`{"src","dst","ip"}`), nicht das aeltere `acls`/`action:accept`-Modell.
> Normaler SSH-Zugriff (`ssh kallilabcore` ueber OpenSSH Port 22) wird ueber
> `grants` geregelt, nicht ueber den `ssh`-Block; letzterer betrifft nur die
> Tailscale-SSH-Funktion.
- Ziel ist eine restriktivere, Tag-basierte ACL.
- Single-User-Realitaet: aktuell gehoeren alle Nodes demselben User
`michaelkaleschke@`. Eine Differenzierung Operator/Familie ist nur ueber
**Tags** moeglich. Tagging aendert Ownership/Key-Expiry und erfordert je Geraet
Re-Auth — deshalb bewusst ein eigener, spaeterer Schritt.
- **Heute bewusst nur Sichtung + Entwurf, kein Tagging, keine Anwendung.**
- Familiengeraete brauchen Tailnet-Zugriff auf **bestimmte** Dienste (welche
genau, ist noch zu konkretisieren) — `tag:family` bekommt gezielte `dst`-Regeln.
- `iphone-14` ist ein Operator-Geraet und faellt unter `tag:operator`.
**Geraete -> Tag (fuer den spaeteren Tagging-Schritt):**
| Tag | Geraete |
|---|---|
| `tag:server` | `kallilabcore` (Host, Subnet-Router) |
| `tag:operator` | `baerchen-1`, `iphone-14` |
| `tag:family` | kuenftige Familiengeraete |
**Entwurf (Vorschlag, noch nicht in der Konsole gespeichert):**
**Angewendete Policy (live, kein Secret):**
```json
{
@@ -130,42 +142,64 @@ alles inklusive der LAN-Subnet-Route.
"autoApprovers": {
"routes": { "192.168.178.0/24": ["tag:server"] }
},
"acls": [
{ "action": "accept", "src": ["tag:operator"], "dst": ["*:*"] },
{ "action": "accept", "src": ["tag:server"], "dst": ["tag:operator:*"] },
{ "action": "accept", "src": ["tag:family"], "dst": ["100.80.98.33:443"] }
"grants": [
{"src": ["tag:operator"], "dst": ["*"], "ip": ["*"]},
{"src": ["tag:server"], "dst": ["tag:operator"], "ip": ["*"]},
{"src": ["tag:family"], "dst": ["tag:server"], "ip": ["tcp:443"]}
],
"ssh": [
{ "action": "accept", "src": ["tag:operator"], "dst": ["tag:server"],
"users": ["root", "autogroup:nonroot"] }
{"action": "check", "src": ["autogroup:member"], "dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]}
]
}
```
> Die `tag:family`-`dst` `100.80.98.33:443` ist ein **Platzhalter** und wird
> durch die real benoetigten Familien-Dienste ersetzt, sobald diese feststehen.
**Geraete-Tags (live):** `kallilabcore` = `tag:server`; `baerchen-1` + `iphone-14`
= `tag:operator`; `kallilab-core` (Docker) + alter `baerchen` bewusst untagged ->
isoliert.
**Lockout-sichere Reihenfolge (wenn die Umsetzung freigegeben wird):**
**Rollout-Protokoll 2026-06-06 (lockout-sicher, je Schritt read-only verifiziert):**
1. `tagOwners` + `autoApprovers` + neue ACL-Regeln speichern, **Allow-all-Regel
zunaechst behalten**.
2. Geraete taggen (`baerchen-1`, `iphone-14` -> operator; `kallilabcore` ->
server) und je Geraet die Verbindung verifizieren.
3. Subnet-Route bleibt approved (jetzt via `autoApprovers`/`tag:server`).
4. **Erst zuletzt** die Allow-all-Regel entfernen -> restriktiv schalten.
5. Sofort Smoke-Tests fahren (siehe unten).
1. Policy additiv erweitert (Tags/grants definiert, Allow-all noch drin) -> alle Peers unveraendert verbunden, Route approved.
2. `baerchen-1` getaggt `tag:operator` -> online, verifiziert.
3. `iphone-14` getaggt `tag:operator` -> verifiziert.
4. `kallilab-core` faktisch geprueft (Docker-Sidecar, keine Abhaengigen) -> bewusst untagged gelassen.
5. Host `kallilabcore` getaggt `tag:server` -> Route blieb via `autoApprovers` automatisch approved, SSH ok.
6. Allow-all entfernt -> restriktiv. Smoke-Tests gruen: Operator-SSH ok, AdGuard-Admin ueber Tailnet `HTTP 302`, Ping 0% Verlust, Route weiter approved; Host sieht nur noch die zwei Operator-Peers (untagged Nodes isoliert). LAN-Rueckweg durchgehend verfuegbar.
**Smoke-Tests nach Anwendung:**
**Schema-/Erhaltungs-Hinweis fuer spaeter:** Die LAN-Subnet-Route
`192.168.178.0/24` wird jetzt ueber `autoApprovers`/`tag:server` approved
(vorher manuell). Es gibt keinen eigenen Bestand zu erhalten; die Policy oben
ist die vollstaendige Wahrheit.
- `baerchen-1` erreicht Host: `ping 100.80.98.33`, `ssh kallilabcore hostname` (read-only).
- `baerchen-1` erreicht LAN ueber Route: z. B. AdGuard-Admin `http://100.80.98.33:8082`, `curl -kI https://192.168.178.58`.
- Falls testbar: ein nicht-Operator-Geraet erreicht Route/Admin-Ports **nicht** mehr.
- Host-Gegencheck: `tailscale status` zeigt alle Soll-Peers weiter verbunden.
**Hintergrund / Designentscheidungen (2026-06-05/06):**
**Noch offene Eingaben vor Finalisierung:**
- Single-User-Realitaet: alle Nodes gehoeren demselben User `michaelkaleschke@`.
Eine Differenzierung Operator/Familie ist nur ueber **Tags** moeglich, deshalb
der Tag-Ansatz statt user-/gruppenbasiert.
- Erster Rollout bewusst klein: nur `tag:server` + `tag:operator`.
- **`tag:family` ist vorbereitet, aber schlafend:** Tag und eine konservative
Minimal-Regel (`dst: tag:server`, `ip: tcp:443`) sind definiert, aber **kein
Geraet traegt den Tag**, daher null Wirkung. Sobald ein echtes Familiengeraet
dazukommt, wird es einmal mit `tag:family` getaggt und die Regel greift sofort
— ohne Policy-Umbau. Vor dem ersten realen Familiengeraet die Regel auf die
dann benoetigten Dienste/Ports pruefen.
- Der `ssh`-Block bleibt der Default (Tailscale-SSH Check-Modus); normaler
OpenSSH-Zugriff laeuft ueber die `grants` (Port 22, fuer `tag:operator` ueber
`ip: ["*"]` abgedeckt).
1. Aktueller ACL-JSON aus der Admin-Konsole (read-only gesichtet, nur Struktur dokumentiert).
2. Konkrete Liste der Dienste/Ports, die Familiengeraete ueber Tailscale brauchen.
**Offene Folgepunkte (kein Risiko, Hygiene/spaeter):**
- Familien-Dienste/Ports konkretisieren — erst wenn ein reales Familiengeraet dazukommt.
- **Zwei-Tailscale-Konsolidierung: ERLEDIGT 2026-06-06** — redundanter Docker-Stack
abgebaut, nur noch die native Plugin-Instanz `kallilabcore` (Subnet-Router) aktiv.
- Tailnet-Konsole aufraeumen: Node-Eintraege `kallilab-core` (jetzt down) und
alter Offline-`baerchen` entfernen (trivial, nur tote Geraeteeintraege).
- State-Pfad `/mnt/user/appdata/tailscale` (vom entfernten Docker-Stack) bei
Gelegenheit nach `_archive/tailscale-removed-2026-06-06/` (kein Sofort-Loeschen).
- Optionaler Off-LAN-Routentest: von einem Operator-Geraet im Mobilfunk
(nicht im Heim-LAN) ein LAN-Ziel ueber `192.168.178.0/24` erreichen, um die
Subnet-Route end-to-end zu bestaetigen (im Heim-LAN nicht sauber isolierbar).
## Portfreigaben und Exposure