docs(network): SSH-Host-Haertung dokumentieren (key-only root, upgrade-sichere Persistenz)

Host gehaertet 2026-06-07: PermitRootLogin prohibit-password,
PasswordAuthentication no, KbdInteractiveAuthentication no; PubkeyAuthentication yes.
Persistenz upgrade-sicher via idempotentem /boot/config/ssh-harden.sh aus
/boot/config/go (sshd -t vor HUP-Reload, Syslog-Selbst-Verifikation).
Manueller Post-Upgrade-Check und Rollback dokumentiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 11:02:07 +02:00
parent 8045e22873
commit de7b714b4d
+75 -2
View File
@@ -193,8 +193,8 @@ ist die vollstaendige Wahrheit.
- Familien-Dienste/Ports konkretisieren — erst wenn ein reales Familiengeraet dazukommt. - Familien-Dienste/Ports konkretisieren — erst wenn ein reales Familiengeraet dazukommt.
- **Zwei-Tailscale-Konsolidierung: ERLEDIGT 2026-06-06** — redundanter Docker-Stack - **Zwei-Tailscale-Konsolidierung: ERLEDIGT 2026-06-06** — redundanter Docker-Stack
abgebaut, nur noch die native Plugin-Instanz `kallilabcore` (Subnet-Router) aktiv. abgebaut, nur noch die native Plugin-Instanz `kallilabcore` (Subnet-Router) aktiv.
- Tailnet-Konsole aufraeumen: Node-Eintraege `kallilab-core` (jetzt down) und - **Tailnet-Konsole aufraeumen: ERLEDIGT 2026-06-06** — Node-Eintraege `kallilab-core`
alter Offline-`baerchen` entfernen (trivial, nur tote Geraeteeintraege). und alter Offline-`baerchen` aus der Admin-Konsole entfernt.
- State-Pfad `/mnt/user/appdata/tailscale` (vom entfernten Docker-Stack) bei - State-Pfad `/mnt/user/appdata/tailscale` (vom entfernten Docker-Stack) bei
Gelegenheit nach `_archive/tailscale-removed-2026-06-06/` (kein Sofort-Loeschen). Gelegenheit nach `_archive/tailscale-removed-2026-06-06/` (kein Sofort-Loeschen).
- Optionaler Off-LAN-Routentest: von einem Operator-Geraet im Mobilfunk - Optionaler Off-LAN-Routentest: von einem Operator-Geraet im Mobilfunk
@@ -288,6 +288,78 @@ docker network inspect frontend_net | jq '.[0].Containers | keys'
docker network inspect backend_net | jq '.[0].Internal' 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.
---
## Offene Entscheidungen ## Offene Entscheidungen
| Thema | Status | Naechster Schritt | | Thema | Status | Naechster Schritt |
@@ -300,3 +372,4 @@ docker network inspect backend_net | jq '.[0].Internal'
| 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. | | 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. | | 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. | | 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". |