796901ec6b
Nach Major-Upgrade 7.2.4 -> 7.3.1 read-only Host-Listener gegen dokumentierte Annahmen geprueft: alle Ausnahmen intakt (InfluxDB 127.0.0.1:8181, AdGuard nur Tailscale, Gitea-SSH 222 LAN/TS, Traefik einziger 80/443-Owner, libvirt :53 weg). Docker-Socket-Lage festgehalten (nur komodo-periphery RW; Traefik C-3 ro, kein Regress). AdGuard-Boot-Race (libvirt-Default-Netz belegte :53 vor AdGuard) + Fix dokumentiert; Dauerfix-Empfehlung VM-Manager aus. SSH-Haertung nach Upgrade verifiziert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
424 lines
25 KiB
Markdown
424 lines
25 KiB
Markdown
# Network Inventory - KalliLab CORE
|
||
|
||
Status: Host-Audit erfasst; Router-Baseline und Portfreigaben-UI bereinigt; FRITZ!Box-Remote-Dienste aus; IPv6-Exposure technisch und per UI entschaerft; Tailscale-Inventar am 2026-06-05 real gemessen.
|
||
Letzte Pruefung: 2026-06-05 (Tailscale-Inventar), 2026-06-01 (Router/Ports)
|
||
|
||
## Zweck
|
||
|
||
Dieses Dokument beschreibt Router, DNS, Tailscale, Portfreigaben und Netztrennung. Es ergaenzt das Architektur-Zielbild in `HOMELAB_ARCHITECTURE_MASTER_V2.md` um konkrete Hardware- und Betriebswerte.
|
||
|
||
## Internet und Router
|
||
|
||
| Feld | Wert |
|
||
|---|---|
|
||
| Anschluss / Provider | DSL, Telekom |
|
||
| Bandbreite (FRITZ!Box-UI) | ca. 87,3 Mbit/s Download, ca. 36 Mbit/s Upload |
|
||
| Router-Modell | FRITZ!Box 7590 |
|
||
| Firmware | FRITZ!OS 8.25 (`154.08.25` per TR-064 am 2026-06-01) |
|
||
| Router-IP | 192.168.178.1 |
|
||
| DHCP-Server | FRITZ!Box (Standardannahme, Override durch Operator nicht dokumentiert) |
|
||
| Lokales Subnetz | 192.168.178.0/24 |
|
||
| IPv6 aktiv | Windows-Client hat Provider-IPv6; Host hat keine globale Provider-IPv6, nur Tailscale-ULA |
|
||
| DynDNS / DDNS | Cloudflare via `ddns-updater` (kein FRITZ!Box-DynDNS in Nutzung) |
|
||
| Heimnetz-Geraete (FRITZ!Box-UI) | 35 aktive Geraete |
|
||
| LAN-Ports belegt | LAN 1-4 verbunden |
|
||
| Telefonie / DECT | aktiv |
|
||
| USB an FRITZ!Box | nicht verbunden |
|
||
| Ausfallschutz (FRITZ!Box) | nicht eingerichtet (Mobilfunk-Stick-Failover nicht aktiv) |
|
||
|
||
### Beobachtungen
|
||
|
||
- Telekom-DSL ist Single-WAN; ohne Ausfallschutz ist Internet-Ausfall = kein DDNS-Update, keine ACME-Erneuerung, keine externen Push-Quellen.
|
||
- Upload 36 Mbit/s ist die effektive Obergrenze fuer Off-site-Backup-Geschwindigkeit nach Hetzner und fuer Plex-Remote-Streaming.
|
||
- FRITZ!OS ist am 2026-06-01 per TR-064 auf `154.08.25` beobachtet; FRITZ!Box-Konfig-Backup `Einstellungen_FRITZ.Box_7590_154.08.25_01.06.26_1318.export` wurde extern/off-system in Vaultwarden abgelegt.
|
||
- `Internet -> Freigaben -> FRITZ!Box-Dienste` ist am 2026-06-01 geprueft: Internetzugriff auf die FRITZ!Box per HTTPS ist aus, FTP/FTPS-Zugriff auf Speichermedien ist aus.
|
||
|
||
## DNS
|
||
|
||
| Komponente | Rolle | Adresse | Bemerkung |
|
||
|---|---|---|---|
|
||
| AdGuard Home | LAN DNS / Filter | Host `192.168.178.58`, Docker `172.23.0.3` | DNS auf Port 53; Admin soll nur via Tailscale-IP `100.80.98.33:8082` erreichbar sein |
|
||
| Unbound | Rekursiver Resolver | Docker `dns_net` | Upstream fuer AdGuard |
|
||
| Cloudflare | Authoritative DNS | extern | DNS-Challenge fuer TLS |
|
||
| Router | DHCP DNS-Verteilung | TBD | Muss auf AdGuard zeigen, falls so betrieben |
|
||
|
||
## Tailscale
|
||
|
||
Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
|
||
`tailscale status --json`, `tailscale ip -4/-6`).
|
||
|
||
| Feld | Wert / Status |
|
||
|---|---|
|
||
| Node-Name | Kallilabcore |
|
||
| Tailnet / MagicDNS | `taild9fcf2.ts.net`; DNSName `kallilabcore.taild9fcf2.ts.net` |
|
||
| Tailscale IPv4 | `100.80.98.33` |
|
||
| 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 | **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)
|
||
|
||
| Tailscale-IP | Node | OS | Status |
|
||
|---|---|---|---|
|
||
| `100.80.98.33` | kallilabcore | linux | aktiv (Host, Subnet-Router) |
|
||
| `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 | **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. |
|
||
|
||
> **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
|
||
|
||
Weil `Kallilabcore` das LAN `192.168.178.0/24` als Subnet-Route anbietet, kann
|
||
**jedes** Tailnet-Geraet mit Zugriff auf diese Route potenziell LAN-Dienste auf
|
||
`192.168.178.0/24` erreichen — auch die Admin-Ports, die im LAN bewusst nur auf
|
||
die Tailscale-IP gebunden sind, sind ueber die Subnet-Route adressierbar. Genau
|
||
deshalb ist die ACL-Policy (unten) der eigentliche Schutzmechanismus und nicht
|
||
nur der LAN-Bind.
|
||
|
||
Pruefkommando (auf dem Unraid-Host, read-only):
|
||
|
||
```bash
|
||
tailscale status
|
||
tailscale status --json | jq '{exitNode: .Self.ExitNodeOption, primaryRoutes: .Self.PrimaryRoutes, allowedIPs: .Self.AllowedIPs}'
|
||
tailscale ip -4
|
||
tailscale ip -6
|
||
```
|
||
|
||
### ACL-Policy — ANGEWENDET 2026-06-06 (restriktive Tag-basierte grants)
|
||
|
||
**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.
|
||
|
||
> **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.
|
||
|
||
**Angewendete Policy (live, kein Secret):**
|
||
|
||
```json
|
||
{
|
||
"tagOwners": {
|
||
"tag:server": ["autogroup:admin"],
|
||
"tag:operator": ["autogroup:admin"],
|
||
"tag:family": ["autogroup:admin"]
|
||
},
|
||
"autoApprovers": {
|
||
"routes": { "192.168.178.0/24": ["tag:server"] }
|
||
},
|
||
"grants": [
|
||
{"src": ["tag:operator"], "dst": ["*"], "ip": ["*"]},
|
||
{"src": ["tag:server"], "dst": ["tag:operator"], "ip": ["*"]},
|
||
{"src": ["tag:family"], "dst": ["tag:server"], "ip": ["tcp:443"]}
|
||
],
|
||
"ssh": [
|
||
{"action": "check", "src": ["autogroup:member"], "dst": ["autogroup:self"],
|
||
"users": ["autogroup:nonroot", "root"]}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Geraete-Tags (live):** `kallilabcore` = `tag:server`; `baerchen-1` + `iphone-14`
|
||
= `tag:operator`; `kallilab-core` (Docker) + alter `baerchen` bewusst untagged ->
|
||
isoliert.
|
||
|
||
**Rollout-Protokoll 2026-06-06 (lockout-sicher, je Schritt read-only verifiziert):**
|
||
|
||
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.
|
||
|
||
**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.
|
||
|
||
**Hintergrund / Designentscheidungen (2026-06-05/06):**
|
||
|
||
- 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).
|
||
|
||
**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: ERLEDIGT 2026-06-06** — Node-Eintraege `kallilab-core`
|
||
und alter Offline-`baerchen` aus der Admin-Konsole entfernt.
|
||
- 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
|
||
|
||
### FRITZ!Box (WAN -> Host)
|
||
|
||
Aktiver Soll-Stand nach Operator-Bereinigung und UI-Gegencheck 2026-06-01:
|
||
|
||
| Aktive Freigabe | Ziel | Zweck | Bemerkung |
|
||
|---|---|---|---|
|
||
| `443/tcp` -> `192.168.178.58:443` | Traefik HTTPS | einziger Public-HTTPS-Einstieg, Wildcard-Cert via Cloudflare-DNS-Challenge | bleibt |
|
||
|
||
Bewusst **nicht** freigegeben:
|
||
|
||
| Port | Begruendung |
|
||
|---|---|
|
||
| `80/tcp` | Cloudflare-DNS-Challenge ersetzt HTTP-01; Traefik macht HTTP->HTTPS-Redirect nur LAN-seitig; WAN-`80` waere zusaetzliche Angriffsflaeche ohne Funktionsnutzen. **2026-05-28 in FRITZ!Box-UI entfernt**, Validierung: Mobilfunk-Test ergibt Timeout auf `http://vault.kaleschke.info`, `https://...` weiter erreichbar. |
|
||
| `222/tcp` (Gitea SSH) | bewusst Tailscale-only: Operator-Pfad ist Tailscale, GitHub-Mirror deckt DR-Bootstrap ab, Gitea-Bundles sind off-host. Externe SSH-Brute-Force-Vektoren vermeiden. |
|
||
| `32400/tcp` (Plex) | Plex wird extern ausschliesslich ueber `https://plex.kaleschke.info` via Traefik/443 erreicht. Kein direkter WAN-Port fuer Plex, Plex Remote Access bleibt aus. |
|
||
|
||
### UPnP / Selbstständige Portfreigaben
|
||
|
||
| Geraet | UPnP-Selbstfreigabe-Recht | Begruendung |
|
||
|---|---|---|
|
||
| `Kallilabcore` (192.168.178.58) | nicht erlaubt | Repo-managed; alle benoetigten Public-Ports sind explizite Freigaben |
|
||
| `PC-192-168-178-71` / VONETS-Adapter (192.168.178.71, MAC 00:17:13:2F:61:96) | **2026-06-01 erneut geprueft und deaktiviert** | wahrscheinlich VONETS-WiFi-Bridge fuer SolarEdge-Wechselrichter; SolarEdge-Cloud-Sync ist ausschliesslich outbound, eingehende Ports sind nicht erforderlich |
|
||
|
||
Sollten neue Geraete UPnP-Selbstfreigaben anfordern, wird das hier als bewusste Ausnahme dokumentiert oder pro Geraet wieder deaktiviert.
|
||
|
||
Historischer UI-Befund vor Bereinigung vom 2026-05-27 (`Internet -> Freigaben -> Kallilabcore`):
|
||
|
||
| Beobachtung | Bewertung |
|
||
|---|---|
|
||
| `HTTP-Server`, TCP, extern `80/tcp` auf `192.168.178.58` | war Abweichung; **2026-05-28 entfernt** |
|
||
| `HTTPS-Server`, TCP, extern `443/tcp` auf `192.168.178.58` | entspricht Repo-Soll |
|
||
| Keine `222/tcp`-Freigabe sichtbar | entspricht seit 2026-05-28 dem Soll: Gitea-SSH bleibt Tailscale-only |
|
||
| Kallilabcore: keine selbststaendige Portfreigabe, kein IPv4-/IPv6-Exposed-Host sichtbar | entspricht Sicherheitsziel |
|
||
| `PC-192-168-178-71`: selbststaendige Portfreigabe erlaubt, `0 aktiv` | **2026-06-01 deaktiviert**; danach nur noch `Kallilabcore` in der Portfreigabenliste sichtbar |
|
||
|
||
### Host (lokal beobachtbar)
|
||
|
||
| Port | Ziel | Zweck | Bewertung |
|
||
|---:|---|---|---|
|
||
| 80/tcp | Traefik | HTTP->HTTPS / ACME | nur LAN, keine WAN-Freigabe noetig |
|
||
| 443/tcp | Traefik | HTTPS | WAN-Freigabe in FRITZ!Box erwartet |
|
||
| 222/tcp | Gitea SSH | Git SSH | nur LAN/Tailscale; keine WAN-Freigabe |
|
||
| 53/tcp+udp | AdGuard | DNS | LAN-only, dokumentierte Ausnahme |
|
||
| 32400/tcp | Plex | Medienserver / Plex Web lokal | LAN/Tailscale direkt; extern nur via Traefik `https://plex.kaleschke.info`, keine WAN-Freigabe fuer 32400 |
|
||
| 8082/tcp | AdGuard Admin | Admin UI | Bind nur `100.80.98.33:8082` (Tailscale), nicht im LAN exponiert |
|
||
| 8181/tcp | InfluxDB 3 Core | Home Assistant / Ecowitt Writer | 2026-05-31 effektiv nur `127.0.0.1:8181`, nicht LAN-exponiert |
|
||
|
||
Pruefkommando:
|
||
|
||
```bash
|
||
ss -ltnp | sort -k4
|
||
docker ps --format "{{.Names}}: {{.Ports}}" | sort
|
||
```
|
||
|
||
## Netztrennung
|
||
|
||
| Netz | Status | Bemerkung |
|
||
|---|---|---|
|
||
| LAN | 192.168.178.0/24 | Hauptnetz, Host `192.168.178.58`, FRITZ!Box meldet 35 aktive Geraete |
|
||
| WLAN 2,4 / 5 GHz | aktiv, SSID `Fritzi` | Standard-WLAN, im LAN-Adressbereich, kein eigener Adressraum |
|
||
| Gast-WLAN | aktiv, SSID `Fritzi Gastzugang` | FRITZ!Box-Gastnetz ist vom Heimnetz getrennt; Smoke 2026-06-06 vom iPhone bestaetigt keine Erreichbarkeit der getesteten LAN-/Admin-Ziele |
|
||
| IoT-Netz | nicht existent | Keine VLAN-Trennung dokumentiert |
|
||
| Tailscale | aktiv | Operator-Zugang, Host-IP `100.80.98.33` |
|
||
| VLANs | nicht in Nutzung | FRITZ!Box 7590 kann VLAN-Tagging an einzelnen LAN-Ports; aktuell nicht konfiguriert |
|
||
|
||
## Docker-Netze
|
||
|
||
Authoritativ ist `HOMELAB_ARCHITECTURE_MASTER_V2.md`. Dieses Inventar haelt nur den Laufzeit-Snapshot fest.
|
||
|
||
| Docker-Netz | Zweck | Erwartung |
|
||
|---|---|---|
|
||
| frontend_net | Traefik/Web | external bridge |
|
||
| backend_net | DB/Cache intern | internal bridge |
|
||
| dns_net | AdGuard/Unbound | bridge |
|
||
| monitoring_net | Observability | compose-intern |
|
||
| app-interne Netze | Stack-isoliert | nur wenn technisch noetig |
|
||
|
||
Pruefkommando:
|
||
|
||
```bash
|
||
docker network ls
|
||
docker network inspect frontend_net | jq '.[0].Containers | keys'
|
||
docker network inspect backend_net | jq '.[0].Internal'
|
||
```
|
||
|
||
## SSH-Konfiguration Host
|
||
|
||
Geprueft 2026-06-06 (read-only), **gehaertet 2026-06-07** via `ssh root@192.168.178.58`.
|
||
|
||
| Parameter | Ist-Wert (effektiv via `sshd -T`, Stand 2026-06-07) | Soll | Status |
|
||
|---|---|---|---|
|
||
| `Port` | `22` | 22 | ok |
|
||
| `PermitRootLogin` | `prohibit-password` | `prohibit-password` | **gehaertet 2026-06-07** |
|
||
| `PasswordAuthentication` | `no` | `no` | **gehaertet 2026-06-07** |
|
||
| `KbdInteractiveAuthentication` | `no` | `no` | **gehaertet 2026-06-07** (noetig wegen `UsePAM yes`) |
|
||
| `PubkeyAuthentication` | `yes` | `yes` | ok |
|
||
| `PermitEmptyPasswords` | `no` | `no` | ok |
|
||
| `AuthorizedKeysFile` | `.ssh/authorized_keys` | `.ssh/authorized_keys` | ok |
|
||
|
||
**Hinterlegte SSH-Keys (root):** 3 Keys vorhanden (persistiert unter `/boot/config/ssh/root/authorized_keys`):
|
||
- `root@Kallilabcore` (Host-eigener Key)
|
||
- `michi@Baerchen` (Operator-Workstation)
|
||
- `hetzner-storagebox-maintenance-2026-06-01` (Hetzner-Maintenance-Key)
|
||
|
||
**Durchgefuehrte Haertung (2026-06-07):** Root-Login ist jetzt key-only,
|
||
Passwort- und Keyboard-Interactive-Auth sind serverseitig abgeschaltet.
|
||
Verifiziert: frischer Key-Login `OK`; `ssh -o PreferredAuthentications=none`
|
||
meldet `Authentications that can continue: publickey`; reiner Passwort-Versuch
|
||
`Permission denied (publickey)`.
|
||
|
||
**Wichtig — Unraid-Persistenz:** `/etc/ssh/sshd_config` wird beim Boot aus dem
|
||
OS-Image regeneriert (`rc.sshd`: `cp -f /boot/config/ssh/* /etc/ssh/`, danach
|
||
`sshd_build`, das nur `Port`/`ListenAddress`/`AddressFamily` setzt). Die
|
||
Unraid-GUI (**Settings → Management Access → SSH**) bietet nur `Use SSH`/`SSH port`
|
||
an — **`PermitRootLogin`/`PasswordAuthentication` sind dort nicht einstellbar.**
|
||
Persistiert wird daher **upgrade-sicher** ueber einen idempotenten Hook:
|
||
|
||
- `/boot/config/ssh-harden.sh` — setzt die drei Direktiven idempotent (bestehende
|
||
aktive Zeile entfernen, genau einmal global vor dem ersten `Match`-Block einfuegen),
|
||
`sshd -t`-Validierung, Reload nur per `kill -HUP` des Host-`sshd` bei valider Config.
|
||
Idempotenz belegt: nach mehreren Laeufen je Direktive exakt 1 aktive Zeile, alte
|
||
`PermitRootLogin yes` entfernt.
|
||
- `/boot/config/go` — ruft `/bin/bash /boot/config/ssh-harden.sh` bei jedem Boot auf.
|
||
|
||
**Selbst-Verifikation (Syslog, rein informativ, keine Reparatur):** Das Skript
|
||
schreibt nach jedem Lauf die effektiven Auth-Werte (`sshd -T`) nach syslog, z. B.
|
||
`ssh-harden: VERIFY permitrootlogin prohibit-password pubkeyauthentication yes
|
||
passwordauthentication no kbdinteractiveauthentication no`. Damit ist nach jedem
|
||
Boot/Upgrade nachweisbar, ob die Haertung gegriffen hat.
|
||
|
||
**Post-Upgrade-/Reboot-Check** (manuell, einmal nach jedem Unraid-Upgrade):
|
||
|
||
```bash
|
||
# A) Effektive Werte direkt abfragen (Soll: prohibit-password / no / no / yes)
|
||
ssh root@192.168.178.58 "sshd -T | grep -Ei 'permitroot|passwordauth|kbdinteractive|pubkey'"
|
||
# B) Oder die automatische VERIFY-Zeile im Syslog lesen (Unraid nutzt rsyslog -> /var/log/syslog, nicht logread)
|
||
ssh root@192.168.178.58 "grep 'ssh-harden' /var/log/syslog | tail -3"
|
||
```
|
||
|
||
Dieser Weg editiert die **jeweils aktuelle** von Unraid generierte Config nach und
|
||
ueberlebt damit Unraid-Upgrades; findet er die Stock-Zeile nicht (z. B. weil eine
|
||
neue Version schon `prohibit-password` ausliefert), macht der `sed` nichts und
|
||
bricht den Boot nicht (fail-safe Richtung offen, nicht ausgesperrt). Bewusst
|
||
**nicht** der oft empfohlene Weg einer kompletten `/boot/config/ssh/sshd_config`
|
||
auf Flash — der wuerde die Stock-Config einfrieren und beim Upgrade neue Defaults
|
||
verschlucken.
|
||
|
||
**Rollback:** `go`-Block + `/boot/config/ssh-harden.sh` entfernen, dann
|
||
`cp /boot/config/ssh-harden.sshd_config.bak-20260607 /etc/ssh/sshd_config` und
|
||
`kill -HUP $(cat /var/run/sshd.pid)`. Notzugang ueber Unraid-Konsole/GUI bleibt.
|
||
|
||
**Abgrenzung:** Ein zweiter `sshd` (`-D -e`) laeuft in einem Docker-Container
|
||
(s6-overlay, moby-Namespace) und bindet **nicht** den Host-`:22`; eigene Config
|
||
im Container, von dieser Haertung unberuehrt.
|
||
|
||
---
|
||
|
||
## Post-Upgrade Posture-Recheck — Unraid 7.3.1 (2026-06-07)
|
||
|
||
Nach dem Major-Upgrade **7.2.4 → 7.3.1** read-only die Host-Listener-Landschaft
|
||
(`ss -tlnp`) gegen die dokumentierten Annahmen geprueft.
|
||
|
||
**Dokumentierte Ausnahmen verifiziert (alle weiterhin gueltig):**
|
||
|
||
| Dienst | Soll | Ist nach 7.3.1 | Status |
|
||
|---|---|---|---|
|
||
| InfluxDB 3 | nur `127.0.0.1:8181` | `127.0.0.1:8181` | ✅ |
|
||
| AdGuard-Admin | nur Tailscale `100.80.98.33:8082` | `100.80.98.33:8082` | ✅ |
|
||
| Gitea-SSH `222` | LAN/Tailscale, keine WAN-Freigabe | `0.0.0.0:222` (LAN/TS), WAN am Router zu | ✅ |
|
||
| Traefik `80/443` | einziger Owner | docker-proxy (Traefik) allein | ✅ |
|
||
| libvirt `:53` | darf nicht existieren | **weg** (Fix vom 2026-06-07 haelt) | ✅ |
|
||
|
||
**Docker-Socket (`/var/run/docker.sock`) — C-3-Kontext:**
|
||
|
||
| Container | Mount | Bewertung |
|
||
|---|---|---|
|
||
| komodo-periphery | **RW** | dokumentierte Ausnahme (Periphery startet/stoppt Container) |
|
||
| traefik | ro | C-3: Direkt-Mount (ro), nicht ueber Socket-Proxy — offener Audit-Punkt, kein Regress |
|
||
| glances / monitoring-promtail / glance-docker-socket-proxy | ro | unkritisch |
|
||
|
||
Keine neue RW-Socket-Exposure durch das Upgrade.
|
||
|
||
**Vorfall-Notiz AdGuard/DNS (Boot-Race, behoben 2026-06-07):** Das Upgrade hatte das
|
||
ungenutzte **libvirt-Default-Netz** auf Autostart gebracht; dessen `dnsmasq` belegte
|
||
beim Boot Port `53` **vor** AdGuard → AdGuards erster Start scheiterte am Bind und
|
||
liess den Container ohne Netz-Anbindung (`Networks={}`, keine Ports) zurueck. Fix:
|
||
`virsh net-autostart default --disable` + `virsh net-destroy default` (kein VM
|
||
betroffen, Liste leer) + AdGuard-Container aus der Compose `--force-recreate`
|
||
(re-attach `dns_net`, `:53` neu veroeffentlicht). DNS danach verifiziert aufloesend.
|
||
`libvirtd` laeuft weiter nur auf `127.0.0.1:16509`.
|
||
|
||
**Empfehlung (Dauerfix):** Da keine VMs genutzt werden, **Unraid VM Manager → Enable
|
||
VMs = No** — dann startet `libvirtd` gar nicht und der `:53`-Konflikt kann prinzipiell
|
||
nicht wiederkehren. Bis dahin verhindert der abgeschaltete Autostart die Wiederkehr.
|
||
|
||
**Beobachtungen (kein Regress, Inventar):** SMB (`:445/:139`) und Plex (`*:32400`)
|
||
lauschen auch auf der Tailscale-IP; durch die seit 2026-06-06 tag-restriktive
|
||
Tailnet-ACL akzeptabel.
|
||
|
||
**SSH-Haertung nach Upgrade:** key-only root unveraendert aktiv und verifiziert
|
||
(`prohibit-password`/`password no`/`kbd no`), go-Hook genau 1× gefeuert — siehe
|
||
Abschnitt „SSH-Konfiguration Host".
|
||
|
||
---
|
||
|
||
## Offene Entscheidungen
|
||
|
||
| Thema | Status | Naechster Schritt |
|
||
|---|---|---|
|
||
| AdGuard Admin nur via Tailscale | live validiert 2026-05-26 | Compose bindet Admin-Port auf `100.80.98.33:8082`; DNS auf Port 53 funktioniert, LAN-Zugriff auf `192.168.178.58:8082` schlaegt fehl |
|
||
| FRITZ!Box-Portfreigaben mit Repo-Soll abgleichen | **erledigt 2026-06-01** | Bereinigt: `80/tcp` entfernt (Cloudflare-DNS-Challenge ersetzt HTTP-01; Mobilfunk-Test bestaetigt Timeout auf `http://`, `https://` weiter ok). `222/tcp` bleibt bewusst nicht eingerichtet (Tailscale-only-Linie). UPnP-Selbstfreigaben sind aus. Aktiver Soll-Stand: ausschliesslich `443/tcp -> 192.168.178.58`. |
|
||
| FRITZ!Box-Dienste aus dem Internet | **erledigt 2026-06-01** | `Internet -> Freigaben -> FRITZ!Box-Dienste`: HTTPS-Zugriff auf die FRITZ!Box aus dem Internet aus; FTP/FTPS auf Speichermedien aus. |
|
||
| FRITZ!OS Update und Konfig-Backup | **erledigt 2026-06-01** | TR-064 meldet `154.08.25`; Konfig-Export liegt extern/off-system in Vaultwarden, Kennwort und Datei bleiben ausserhalb des Repos. |
|
||
| Gast-/IoT-Zugriff auf Admin-Ports | **validiert 2026-06-06** | Runbook `docs/GUEST_IOT_NETWORK.md` und Checks `ops/maintenance/check-guest-iot-isolation.ps1` sowie `ops/maintenance/check-guest-iot-preflight.sh` vorhanden. LAN-Preflight von `baerchen` gruen: `192.168.178.58:8082` und `:8181` blockiert. Host-Preflight auf Unraid gruen, Report `/mnt/user/backups/restore-reports/guest-iot-preflight-2026-06-06-131316.md`. Gast-WLAN-Smoke per iPhone: `192.168.178.58:8082`, `:8181`, `:222`, `https://192.168.178.58` und `192.168.178.1` nicht erreichbar. |
|
||
| IPv6 Exposure | technisch und per UI entschaerft | Public DNS liefert keine AAAA-Records fuer `*.kaleschke.info`; Host hat keine globale Provider-IPv6. TR-064 meldet IPv6-Firewall aktiv und Pinholes grundsaetzlich erlaubt; FRITZ!Box-UI zeigt keine aktiven IPv6-Freigaben, keine Admin-/SSH-Freigaben. |
|
||
| WAN-Ausfallschutz | **geparkt: spaeter evaluieren** (Operator-Entscheidung 2026-06-05) | Mobilfunk-Stick-Failover an FRITZ!Box bleibt vorerst inaktiv. Folgen sind bewusst akzeptiert: Internet-Ausfall = ACME/DDNS pausieren, lokale Apps laufen weiter. Review-Trigger: haeufigere oder laengere DSL-Ausfaelle, oder wenn externer Remote-Zugang (statt nur lokalem Betrieb) geschaeftskritisch wird. Erst dann Mobilfunk-Failover technisch bewerten. |
|
||
| Home Assistant InfluxDB Bind | validiert 2026-05-31 | `docker-proxy` bindet `127.0.0.1:8181`; keine LAN-Exposure. Wenn Home Assistant nicht lokal auf dem Host schreibt, braucht das eine bewusste Bind-Aenderung. |
|
||
| SSH-Haertung Host | **erledigt 2026-06-07** | Root-Login key-only: `PermitRootLogin prohibit-password`, `PasswordAuthentication no`, `KbdInteractiveAuthentication no`. Live gesetzt + verifiziert (Key-Login ok, Passwort-Auth abgelehnt). Persistenz upgrade-sicher ueber `/boot/config/ssh-harden.sh` (idempotent, `sshd -t` vor Reload) aufgerufen aus `/boot/config/go`. GUI bietet diese Optionen nicht. Details im Abschnitt „SSH-Konfiguration Host". |
|