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>
127 lines
6.6 KiB
Markdown
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.
|