--- type: issue id: "0088" status: open created: 2026-08-06 milestone: M1 priority: medium projekt: gitops gitlab_iid: "56" related: [] --- # NetworkPolicy: ausgehender Verkehr ist unbeschränkt (13 Ingress-Regeln, 1 Egress) > Adoptiert aus [gitops#56](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/56) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei. In `apps/production/networkpolicy.yaml` stehen **13 Ingress-Regeln und genau ein Egress-Vorkommen**. Eingehend ist der Cluster dicht, ausgehend ist er offen: Jeder Pod darf ins Internet, in andere Namespaces und an den Metadaten-Dienst. ## Warum das zählt Die Ingress-Regeln verhindern, dass jemand *hineinkommt*. Sie verhindern nicht, dass etwas, das schon drin ist, **hinausredet** — Daten abfließen lässt, Schadcode nachlädt oder sich zu einem Command-and-Control-Server verbindet. Genau das ist der Schritt, der aus einer kompromittierten Abhängigkeit einen Vorfall macht. Besonders relevant hier, weil wir **fremden Code ausführen**: Synapse-Module, ClamAV, Draupnir, dazu eine große npm-Abhängigkeitskette im Client-Build. ## Was zu tun ist 1. Ist-Zustand aufnehmen: welche Pods brauchen wirklich ausgehende Verbindungen und wohin (Föderation, Let's Encrypt, Container-Registries, IONOS-API, Matrix-Push-Gateways). 2. `policyTypes: [Egress]` mit Default-Deny je Namespace, dann gezielt freigeben. 3. ⚠️ **DNS zuerst freigeben** (`kube-dns`/CoreDNS, UDP+TCP 53) — sonst steht alles, und der Fehler sieht aus wie ein Anwendungsproblem, nicht wie eine Firewall. 4. Föderation ist der schwierige Teil: Synapse muss zu beliebigen Matrix-Servern hinaus. Das lässt sich nicht auf eine Liste eingrenzen, solange die Föderation offen ist (gitops#17) — eher über einen Egress-Proxy oder bewusst offen lassen und dokumentieren. ## Rollback NetworkPolicies sind additiv und einzeln löschbar. Der Not-Aus ist das Entfernen der Default-Deny-Regel. *Gefunden am 2026-08-06 bei einer Bestandsaufnahme der Sicherheitslage.* ## Schritt 1 erledigt 2026-08-19 — Bestandsaufnahme, und ein Beinahe-Fehler ### Vorfrage zuerst: werden NetworkPolicies hier überhaupt durchgesetzt? **Ja, belegt.** In k3s laufen Flannel und der Policy-Controller *im k3s-Prozess*, nicht als Pods — ihre Abwesenheit in `kube-system` beweist also nichts. Gemessen statt gefolgert, mit Gegenprobe: | Von | nach `matrix-stack-postgres:5432` | Erwartung laut `allow-ingress-postgres` | |---|---|---| | `synapse-main` (matrix) | **verbunden** | erlaubt ✓ — beweist zugleich, dass dort etwas lauscht | | `authentik-server` (authentik) | **abgewiesen** | nicht erlaubt ✓ | Damit ist die bestehende Ingress-Absicherung echter Schutz — und Egress-Regeln werden echte Wirkung haben, im Guten wie im Schlechten. ### Cluster-Eckdaten Pod-CIDR `10.42.0.0/24` (ein Knoten), Service-CIDR `10.43.0.0/16`, kube-dns `10.43.0.10`, API `10.43.0.1`. Pods: matrix 15, authentik 3, monitoring 2. ### ⚠️ Der naheliegende erste Schritt wäre falsch gewesen Das übliche Muster lautet: Egress auf `0.0.0.0/0` freigeben, aber **RFC1918 und den Metadaten-Dienst ausnehmen**. Hier hätte das **zwei Dinge zerrissen**, beide über `10.0.0.3` (CFGMON im privaten Hetzner-Netz): 1. **Die gesamte Überwachung.** Alloy schreibt nach `http://10.0.0.3:9090/api/v1/write` und `http://10.0.0.3:3100/loki/api/v1/push` (`apps/monitoring/alloy-config.yaml`). Ein RFC1918-Block hätte Metriken *und* Logs abgeschnitten — und zwar still, denn wer meldet den Ausfall des Meldewegs? 2. **Die TURN-Rotation.** Der CronJob erreicht Gitea über `hostAliases: 10.0.0.3 → rohana.axion1337.de`, weil der öffentliche Weg unzuverlässig war (#0076). Ohne privaten Egress kein Rotations-Push — genau die Kette, die wir am 2026-08-18 erst wieder in Gang gebracht haben. **Nebenbefund, der #0008 endgültig schließt:** Dass der Cluster Prometheus und Loki über `10.0.0.3` erreicht, ist der harte Beleg dafür, dass 9090/3100 **öffentlich nicht gebraucht werden**. Die dortige Schlussfolgerung („Weg A ist gegenstandslos") steht damit nicht mehr nur auf der Abwesenheit einer Firewall-Regel. ### Vorschlag für Schritt 2 Nicht RFC1918 sperren. Der risikoarme, trotzdem wirksame erste Schnitt ist der **Metadaten-Dienst** (`169.254.0.0/16`, darin `169.254.169.254`): der klassische Weg, aus einem übernommenen Pod Instanz-Informationen zu ziehen. Kein Dienst hier braucht ihn. Je Namespace also eine Egress-Policy, die freigibt: DNS nach kube-system, das Cluster (`10.42.0.0/16`, `10.43.0.0/16`), und `0.0.0.0/0` **mit `except: 169.254.0.0/16`**. Föderation, ACME, SMTP, Registry und der private Weg zu `10.0.0.3` bleiben offen — der schwierige Teil aus Punkt 4 des Issues wird damit gar nicht erst angefasst. Rollback bleibt das Löschen der einen Policy je Namespace. ## Schritt 2 ausgerollt 2026-08-19 — Metadaten-Dienst gesperrt `egress-block-metadata` liegt in **matrix, authentik und monitoring** (Commit `e7469a3`). Sie erlaubt DNS nach kube-system, die Cluster-Bereiche und `0.0.0.0/0`, und sperrt allein `169.254.0.0/16`. **Abnahme in zwei Namespaces gemessen, nicht angenommen:** | Prüfung | matrix | authentik | |---|---|---| | DNS löst auf | ✅ | ✅ | | Internet raus (matrix.org, HTTP 200 — die Föderation lebt) | ✅ | ✅ | | Metadaten-Dienst | ✅ **blockiert** | ✅ **blockiert** | | privates Netz `10.0.0.3:9090` | ✅ erreichbar | ✅ erreichbar | Alloy meldet seit dem Ausrollen **0 Fehlerzeilen** — der Weg nach `10.0.0.3` steht, Metriken und Logs fließen weiter. **Ehrliche Einordnung des Gewinns:** Der Hetzner-Metadaten-Dienst gab hier instance-id, hostname, region, MAC und Netzkonfiguration preis; `userdata` und `public-keys` waren **leer**. Es lagen also keine Zugangsdaten offen, anders als im AWS-Fall, auf den diese Härtung sonst zielt. Der Schritt schließt eine Klasse, er verhindert keinen bekannten Angriffsweg. ### Was dieses Issue offen lässt Der Kern der Forderung — **Default-Deny mit expliziter Freigabe** — ist damit **nicht** erfüllt. Ausgehend darf weiterhin jeder Pod überall hin, außer ins Link-Local. Der Grund steht in Schritt 1: Solange die Föderation offen ist (#0060), lässt sich Synapse' Ziel-Menge nicht als Liste schreiben, und ein Egress-Proxy ist ein eigenes Vorhaben. Sinnvolle nächste Stufe wäre, **die Namespaces einzeln zu betrachten statt alle gleich**: `monitoring` und `authentik` haben eine überschaubare, benennbare Ziel-Menge (CFGMON privat, IONOS-SMTP, ACME) — dort ist Default-Deny realistisch. `matrix` ist es wegen der Föderation nicht. Das Issue bleibt offen und trägt jetzt die Bestandsaufnahme, die diese Entscheidung tragfähig macht.