Files
management/docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md
Thore CimbalandClaude Opus 5 23378c41b7 docs(issues): #0088 - metadata egress blocked, and what that does not achieve
Rolled out to matrix, authentik and monitoring, verified in two namespaces rather
than assumed: DNS resolves, federation still reaches matrix.org, the metadata service
is blocked, and the private path to 10.0.0.3 is intact with Alloy reporting no errors.

The issue stays open on purpose. Its actual demand - default deny with explicit
allows - is not met; egress is still open everywhere except link-local. Federation
makes that unwritable as a list while it stays open (#0060). What is now on record is
the inventory that makes the next decision possible: monitoring and authentik have a
nameable set of destinations and could take default deny, matrix cannot.

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

127 lines
6.6 KiB
Markdown

---
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.