# FIPS Mode -- Status, Incident, Recovery, Prevention

**Stand:** 2026-04-15
**Cluster:** 5 mTLS-Cluster-Server (Cert-Server-{0,1}-NBG, Cert-Server-{0,1}-FSN, Cert-Server-HEL)
**Policy:** FIPS ist auf diesem Cluster **deaktiviert** (gilt seit Memory v3.0, 2025-11-23).

---

## 1. Hintergrund: Was ist FIPS?

**FIPS 140-2/3** = US-Bundes-Crypto-Standard. Ubuntu bietet via Ubuntu Pro spezielle FIPS-validated Pakete:
- Eigener Kernel mit `-fips` Suffix (z.B. `linux-image-6.8.0-87-fips`)
- FIPS-validated OpenSSL/libgcrypt/libssl3 (Versions-Suffix `~Fips1`)
- Restriktiver Crypto-Modus, nur zugelassene Algorithmen

Aktiviert wird FIPS via:
- Subscription: `pro enable fips` oder `pro enable fips-updates`
- Kernel-Suffix `-fips` als gebooteter Kernel
- Optional: `fips=1` in der Kernel-Cmdline
- `/etc/default/grub.d/99-ubuntu-fips.cfg` setzt automatisch `fips=1` wenn FIPS-Kernel erkannt

**Wir wollen FIPS NICHT** auf diesem Cluster:
- mTLS-Cipher-Suites unterstuetzen wir bewusst breit (auch nicht-FIPS-zugelassene)
- Cloudflared nutzt QUIC + X25519MLKEM768 -- nicht FIPS-validated
- Apache + Wazuh haben unter strict FIPS Init-Probleme mit aktueller libssl3
- Performance-Einbussen ohne Mehrwert in unserem Use-Case

---

## 2. Incident 2026-04-15: NBG0 nach Reboot nur per Console erreichbar

### Symptomatik

User matthias triggered Reboot ueber `dns-maintenance drain-and-reboot` auf Cert-Server-0-NBG (10.0.0.3, public 116.203.243.240). Drain lief sauber durch (172 records deleted). Server kam nach Reboot **nicht** vollstaendig zurueck:

- WG-Ping (10.0.0.3) antwortet ~ 1ms RTT (Kernel + WG OK)
- Public-IP-Ping (v4 + v6) **failed** (100% packet loss)
- TCP Ports 22, 80, 443, 1514, 5666, 9100, 51820 ALLE refused/closed
- Hard-Reboot via Hetzner Cloud Console = unveraendert
- Console-Login moeglich → User bestaetigt: "FIPS scheint aktiv"

### Root Cause

Auf NBG0 lief noch Kernel `linux-image-6.8.0-87-fips` als Default-Boot-Eintrag (GRUB_DEFAULT=0). Aufgerufene Komponenten:

```
cat /proc/sys/crypto/fips_enabled  → 1
cat /proc/cmdline                   → BOOT_IMAGE=…-fips (kein expliziter fips=1 noetig)
uname -r                            → 6.8.0-87-fips
```

Kernel-Suffix `-fips` aktiviert FIPS-Modus implizit. Folgen:
1. **sshd** scheitert beim Start (libssl3 FIPS-Crypto-Init schlaegt fehl mit aktuellem OpenSSL)
2. **apache2 + cloudflared** scheitern aus gleichem Grund
3. **hc-net-ifup@enp7s0** (Hetzner Cloud Network Setup) scheitert → Public-Interface bekommt keine IP
4. ICMP/TCP-Stack im Kernel sind crypto-unabhaengig → WG-Ping antwortet weiter
5. Ergebnis: Server "halb-tot" → nur ueber Hetzner-Console erreichbar

### Recovery (auf Console)

```bash
# 1. FIPS-Status verifizieren
cat /proc/sys/crypto/fips_enabled    # Erwartung: 1
cat /proc/cmdline                     # Erwartung: …-fips
uname -r                              # Erwartung: …-fips

# 2. Verfuegbare alternative Kernel pruefen
ls /boot/vmlinuz*                     # Erwartung: ein -generic-Kernel vorhanden

# 3. GRUB_DEFAULT auf den non-FIPS Kernel zeigen lassen
sed -i 's|^GRUB_DEFAULT=.*|GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 6.8.0-110-generic"|' /etc/default/grub
update-grub

# 4. Reboot (Console-Sitzung ueberlebt das nicht, aber das ist OK)
reboot
```

Nach Boot mit non-FIPS Kernel: SSH + Public-Network kommen sofort zurueck.

### Vollstaendiges Cleanup nach Recovery

```bash
# Backup
cp /etc/default/grub /etc/default/grub.PREfipscleanup_$(date +%s)

# Drop-in Cfg von Ubuntu-Pro entfernen (verhindert dass `update-grub` fips=1 wieder
# in alle Kernel-Eintraege schreibt, falls FIPS-Pakete reinstalliert werden)
mv /etc/default/grub.d/99-ubuntu-fips.cfg /root/99-ubuntu-fips.cfg.removed_$(date +%s)

# FIPS-Pakete purgen
DEBIAN_FRONTEND=noninteractive apt-get purge -y \
  linux-image-6.8.0-87-fips \
  linux-modules-6.8.0-87-fips \
  linux-modules-extra-6.8.0-87-fips \
  linux-headers-6.8.0-87-fips \
  linux-tools-6.8.0-87-fips \
  linux-fips-headers-6.8.0-87 \
  linux-fips-tools-6.8.0-87

# GRUB_DEFAULT zurueck auf 0 (jetzt ist non-FIPS Kernel der einzige Eintrag)
sed -i 's|^GRUB_DEFAULT=.*|GRUB_DEFAULT=0|' /etc/default/grub
update-grub
```

---

## 3. Cluster-weiter Audit (2026-04-15)

Pruefung aller 5 Cluster-Server:

| Server | grub-fips-cfg | fips-pkgs | Aktiver Kernel | Risiko (vor Cleanup) |
|--------|--------------|-----------|----------------|----------------------|
| Cert-Server-1-NBG (10.0.0.2) | vorhanden | 0 | 6.8.0-88-generic | mittel |
| **Cert-Server-0-NBG (10.0.0.3)** | **vorhanden** | **8 (incl. fips kernel)** | **6.8.0-87-fips** ❌ | **incident-Server** |
| Cert-Server-0-FSN (10.0.0.4) | none | 0 | 6.8.0-110-generic | low |
| Cert-Server-1-FSN (10.0.0.5) | none | 2 (kernel modules) | 6.8.0-110-generic | mittel |
| Cert-Server-HEL (10.0.0.6) | vorhanden | 0 | 6.8.0-110-generic | mittel |

**libgcrypt20** mit `~Fips1~` Versions-Suffix war auf allen 5 Servern installiert. Wurde am 2026-04-15 (nach Abschluss des Cluster-Audits) per Force-Downgrade auf die Standard-Ubuntu-Variante `1.10.3-2build1` gesetzt, Details siehe "Final State" unten. Ausserdem wurden verwaiste `/lib/modules/6.8.0-87-fips/` Verzeichnisse auf 4 Servern entfernt.

### Durchgefuehrte Cleanup-Aktionen

1. **NBG0**: Vollstaendig entfernt -- 7 fips-Kernel-Pakete purged, grub.d-cfg entfernt, GRUB_DEFAULT=0 wieder, update-grub
2. **NBG1, HEL**: `/etc/default/grub.d/99-ubuntu-fips.cfg` entfernt, update-grub
3. **FSN1**: 2 verwaiste fips-Kernel-Module entfernt (`linux-modules-6.8.0-87-fips`, `linux-modules-extra-6.8.0-87-fips`), update-grub
4. **FSN0**: war bereits sauber

### Final State (nach vollstaendigem Cleanup 2026-04-15)

10-Dimensionen-Audit ueber alle 5 Cluster-Server:

| Server | runtime | kernel-fips | grub.d | grub.cfg | kernel-pkg | tool-pkg | fips-pkg | mod-dir | pro-fips | libgcrypt20 |
|--------|---------|-------------|--------|----------|------------|----------|----------|---------|----------|-------------|
| NBG1 | NONE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | disabled | 1.10.3-2build1 |
| NBG0 | NONE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | disabled | 1.10.3-2build1 |
| FSN0 | NONE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | disabled | 1.10.3-2build1 |
| FSN1 | NONE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | disabled | 1.10.3-2build1 |
| HEL  | NONE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | disabled | 1.10.3-2build1 |

- `/proc/sys/crypto/fips_enabled` existiert auf keinem Server (Kernel-Modul nicht geladen).
- Kein Kernel mit `-fips` Suffix installiert oder aktiv.
- `/etc/default/grub.d/*fips*` auf keinem Server vorhanden.
- `grub.cfg` enthaelt 0 fips-Entries.
- `dpkg -l | grep -c '~Fips'` = 0 auf allen (vormals je 1 fuer libgcrypt20).
- `/lib/modules/*-fips/` auf keinem Server vorhanden (orphan dirs entfernt).
- `pro fips-updates: disabled` auf allen.
- **libgcrypt20 auf `1.10.3-2build1` (Standard Ubuntu) downgraded** auf allen 5 -- zuvor `1.10.3-2ubuntu0.1~Fips1~rc7`. Force-Downgrade via `apt-get install --allow-downgrades libgcrypt20=1.10.3-2build1`.

---

## 4. Vorbeugung

### a) Verify-Script erweitern

`scripts/verify-deployment.sh` Check 17 (FIPS-Detektor):

```bash
# Check 17: FIPS muss inaktiv sein - schuetzt vor Boot-Hang
fips_runtime=$(ssh_run "$ip" "cat /proc/sys/crypto/fips_enabled 2>/dev/null || echo 0")
fips_grub=$(ssh_run "$ip" "ls /etc/default/grub.d/*fips* 2>/dev/null | wc -l")
fips_pkgs=$(ssh_run "$ip" "dpkg -l 2>/dev/null | grep -cE '^ii.*linux-image.*-fips'")
fips_kernel=$(ssh_run "$ip" "uname -r | grep -c -- '-fips'")

if [[ "$fips_runtime" == "0" && "$fips_grub" == "0" && "$fips_kernel" == "0" && "$fips_pkgs" == "0" ]]; then
    ok "FIPS clean (runtime=0, grub=0, kernel=non-fips, pkgs=0)"
else
    fail "FIPS DETECTED -- REBOOT-RISIKO! runtime=$fips_runtime grub-cfgs=$fips_grub kernel-suffix=$fips_kernel fips-kernel-pkgs=$fips_pkgs"
fi
```

Damit erkennen wir die Falle bevor jemand rebootet.

### b) Pre-Reboot-Checkliste

Vor jedem `dns-maintenance drain-and-reboot` oder normalem Reboot:

```bash
# Quick-Check Inline:
[ "$(cat /proc/sys/crypto/fips_enabled 2>/dev/null || echo 0)" = "0" ] && \
[ "$(uname -r | grep -c fips)" = "0" ] && \
[ "$(ls /etc/default/grub.d/*fips* 2>/dev/null | wc -l)" = "0" ] && \
echo "FIPS clean - safe to reboot" || \
echo "FIPS DETECTED - DO NOT REBOOT"
```

### c) Wenn FIPS jemals wieder gebraucht wird

Sollte FIPS-Compliance fuer einen Use-Case noetig werden, dann **kontrolliert**:
1. Eigenes Test-VM provisionieren (NICHT direkt im Cluster)
2. `pro enable fips-updates` + Reboot
3. Vollstaendigen Test-Lauf von dns-maintenance, Apache (mTLS), cloudflared, Wazuh
4. Erst wenn alles validiert: schrittweiser Cluster-Rollout (ein Server, beobachten, dann naechster)

Nie einfach `pro enable fips-updates` ohne Test-Validierung in Produktion.

---

## 5. Hetzner Cloud Console -- Lessons Learned

Der Recovery war nur moeglich weil **Hetzner Cloud Web-Console** als Out-of-Band-Zugriff verfuegbar war:
- VNC/HTML5-Console direkt am Boot-Bildschirm
- Erlaubt Login auch wenn sshd, Apache, cloudflared, hc-net-ifup alle tot sind
- Funktioniert solange der Hypervisor selbst lebt

**Empfehlung:** Hetzner-Console-Zugang **muss verfuegbar sein** fuer alle 5 Cluster-Server. Wer Zugriff hat:
- Pruefen via Hetzner Cloud → Project → Members
- Mindestens 2 Personen sollten Console-Zugang haben (Bus-Faktor)

Ohne Console-Zugang waere NBG0 unwiederbringlich gewesen → Snapshot-Restore noetig oder Server neu provisionieren + DNS-Records neu konfigurieren.

---

## 6. Zusammenfassung

- **Problem:** NBG0 bootet nach Reboot in FIPS-Kernel, alle Crypto-abhaengigen Services tot, nur Console-Zugang.
- **Root Cause:** `linux-image-6.8.0-87-fips` als Default-Kernel via GRUB_DEFAULT=0 + grub.d/99-ubuntu-fips.cfg.
- **Recovery:** GRUB_DEFAULT auf non-FIPS Kernel zeigen + reboot.
- **Cleanup:** FIPS-Pakete purged, grub.d-cfg entfernt, alle 5 Server abgeglichen.
- **Vorbeugung:** verify-deployment.sh Check 17, Pre-Reboot-Quickcheck, dokumentierte Disable-Routine.
- **Lesson:** Out-of-Band-Console-Zugriff (Hetzner) ist Pflicht.
