Files
management/docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md
T
Thore CimbalandClaude Opus 5 9816d16c6f docs(issues): #0088 - egress inventory taken, and the obvious first move was wrong
Before anything else: NetworkPolicy really is enforced here. k3s runs flannel and the
policy controller inside its own process, so the absence of pods in kube-system
proves nothing - measured instead, with a counter-probe. synapse-main reaches
matrix's postgres and authentik-server does not, exactly as allow-ingress-postgres
says. So the existing ingress rules are real protection and egress rules will bite.

The textbook first step - allow 0.0.0.0/0 but except RFC1918 and the metadata
service - would have cut two things here, both over 10.0.0.3, CFGMON on the private
Hetzner network. Alloy remote-writes metrics and logs there, so monitoring would have
gone silent, and nothing reports the failure of the reporting path. And the TURN
rotation reaches Gitea through a hostAlias to that address because the public route
was unreliable, so the rotation push would have died a day after we got it working
again.

That also settles #0008 harder than the absence of a firewall rule could: the cluster
reaches Prometheus and Loki privately, so 9090 and 3100 are demonstrably not needed
publicly.

Proposed step 2 is therefore narrower and nearly risk-free: block only the metadata
service, 169.254.0.0/16, while leaving federation, ACME, SMTP, registries and the
private path open. Nobody here needs the metadata service, and the hard part of this
issue - federation to arbitrary servers - is not touched at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:00:00 +00:00

4.8 KiB

type, id, status, created, milestone, priority, projekt, gitlab_iid, related
type id status created milestone priority projekt gitlab_iid related
issue 0088 open 2026-08-06 M1 medium gitops 56

NetworkPolicy: ausgehender Verkehr ist unbeschränkt (13 Ingress-Regeln, 1 Egress)

Adoptiert aus gitops#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.