592 lines
17 KiB
YAML
592 lines
17 KiB
YAML
# Default-deny ingress for the matrix namespace, with explicit allow rules per component.
|
|
# Egress is intentionally untouched (federation to arbitrary Matrix servers, ACME, SMTP,
|
|
# DNS all stay unrestricted).
|
|
#
|
|
# Lesson learned deploying the authentik namespace's equivalent policy: NetworkPolicy
|
|
# filters on the pod's actual container port, not the Service's external port (e.g.
|
|
# authentik-server's Service maps 80->9000). Wherever a Service here uses a *named*
|
|
# targetPort, this file references that name directly instead of guessing a number -
|
|
# Kubernetes resolves it from the pod spec, which is safer than a hardcoded port.
|
|
#
|
|
# matrix-stack-postgres already effectively has no dedicated chart NetworkPolicy of its
|
|
# own (unlike authentik-postgresql's Bitnami one) - the rules below are the only gate.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: default-deny-ingress
|
|
namespace: matrix
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes:
|
|
- Ingress
|
|
---
|
|
# axion1337.chat (root) -> Element Web
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-element-web
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: element-web
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: element
|
|
---
|
|
# admin.axion1337.chat -> Element Admin
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-element-admin
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: element-admin
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: http
|
|
---
|
|
# axion1337.chat/docs/setup -> Element desktop setup docs (our own nginx)
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-element-web-docs
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: element-web-docs
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
---
|
|
# matrix.axion1337.chat AND the well-known delegation both front through haproxy
|
|
# (matrix-stack-synapse and matrix-stack-well-known Services both target haproxy's
|
|
# named ports, not synapse-main directly).
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-haproxy
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: haproxy
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: haproxy-synapse
|
|
- protocol: TCP
|
|
port: haproxy-403
|
|
- protocol: TCP
|
|
port: haproxy-wkd
|
|
# Draupnir (Issue #18) calls Synapse's client-server API directly, in-namespace -
|
|
# without this it would be silently blocked by the default-deny policy.
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: draupnir
|
|
ports:
|
|
- protocol: TCP
|
|
port: haproxy-synapse
|
|
# Client-Scan-Dienst (Issue #19-Erweiterung) validiert Access-Tokens gegen Synapses
|
|
# eigenen /whoami-Endpoint statt eine eigene Auth zu bauen.
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: clamav-http-scanner
|
|
ports:
|
|
- protocol: TCP
|
|
port: haproxy-synapse
|
|
---
|
|
# account.axion1337.chat (Traefik) + matrix.axion1337.chat (also routes to MAS for some
|
|
# paths) + synapse-main calling MAS's internal port for session/token introspection.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-mas
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-authentication-service
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-main
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-main
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8081
|
|
---
|
|
# Synapse itself: reached via haproxy (same namespace), calls from MAS (provisioning),
|
|
# metrics scraped by Alloy (monitoring namespace).
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-synapse
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-main
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: haproxy
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-authentication-service
|
|
ports:
|
|
- protocol: TCP
|
|
port: synapse-http
|
|
- protocol: TCP
|
|
port: synapse-health
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: monitoring
|
|
ports:
|
|
- protocol: TCP
|
|
port: synapse-metrics
|
|
---
|
|
# mrtc.axion1337.chat (Traefik) for the auth handshake, plus Alloy scraping metrics.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-rtc-authorisation-service
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-rtc-authorisation-service
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-rtc-sfu
|
|
ports:
|
|
- protocol: TCP
|
|
port: http
|
|
---
|
|
# The SFU: mrtc.axion1337.chat (Traefik) for signalling, Alloy for metrics, and the
|
|
# NodePort-exposed WebRTC media ports need to stay open to the internet by design -
|
|
# that's the actual point of a TURN/SFU media relay, not a mistake.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-rtc-sfu
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-rtc-sfu
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: http
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: monitoring
|
|
ports:
|
|
- protocol: TCP
|
|
port: metrics
|
|
- from:
|
|
- ipBlock:
|
|
cidr: 0.0.0.0/0
|
|
ports:
|
|
- protocol: TCP
|
|
port: 30001
|
|
- protocol: UDP
|
|
port: 30002
|
|
---
|
|
# Postgres: only Synapse and MAS need data access; Alloy scrapes the exporter.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-postgres
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: postgres
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-main
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: matrix-authentication-service
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-backup
|
|
ports:
|
|
- protocol: TCP
|
|
port: 5432
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: monitoring
|
|
ports:
|
|
- protocol: TCP
|
|
port: 9187
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-acme-solver
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
acme.cert-manager.io/http01-solver: "true"
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8089
|
|
|
|
# Note: coturn runs with hostNetwork: true, so NetworkPolicy does not apply to it at all -
|
|
# it's already gated by the Hetzner Cloud Firewall instead. Nothing to write here.
|
|
---
|
|
# ClamAV (Issue #19): only Synapse's check_media_file_for_spam module calls this, over
|
|
# clamd's plain TCP protocol on port 3310. Nothing else needs to reach it.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-clamav
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: clamav
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: synapse-main
|
|
# Client-seitiger Scan-Dienst (Issue #19-Erweiterung) braucht denselben ClamAV.
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: clamav-http-scanner
|
|
ports:
|
|
- protocol: TCP
|
|
port: clamd
|
|
---
|
|
# axion1337.chat/_scan (Traefik) - client-seitiger Scan-Dienst, direkt vom Browser aufgerufen.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-clamav-http-scanner
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: clamav-http-scanner
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: TCP
|
|
port: http
|
|
---
|
|
# wiki.axion1337.chat (Traefik) -> Wiki.js (ADR-0014, #0048)
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-wikijs
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs-config # Konfig-Job darf Wiki.js erreichen
|
|
ports:
|
|
- protocol: TCP
|
|
port: http
|
|
---
|
|
# Wiki.js -> sein eigener Postgres (nur von Wiki.js-Pods erreichbar)
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-wikijs-postgres
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs-postgres
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs
|
|
# Nächtliches Backup (wikijs-postgres-backup.yaml) braucht denselben DB-Zugang.
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs-backup
|
|
# Tägliche Gruppenprüfung (wikijs-gruppenpruefung.yaml, #0103) liest nur.
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: wikijs-gruppenpruefung
|
|
ports:
|
|
- protocol: TCP
|
|
port: postgres
|
|
---
|
|
# Egress: alles bleibt erlaubt AUSSER dem Metadaten-Dienst (management #0088).
|
|
#
|
|
# Warum so schmal? Der Lehrbuch-Schnitt (0.0.0.0/0 except RFC1918) haette hier zwei
|
|
# Dinge zerrissen, beide ueber 10.0.0.3 (CFGMON im privaten Hetzner-Netz): Alloy
|
|
# schreibt Metriken und Logs dorthin, und der TURN-Rotations-CronJob erreicht Gitea
|
|
# ueber einen hostAlias auf dieselbe Adresse. Private Netze bleiben deshalb offen.
|
|
#
|
|
# 169.254.0.0/16 ist Link-Local; darin liegt bei Hetzner unter 169.254.169.254 der
|
|
# Metadaten-Dienst, aus jedem Pod unauthentifiziert abfragbar (nachgemessen 2026-08-19:
|
|
# HTTP 200 mit instance-id, hostname, region, MAC, Netzkonfiguration). userdata und
|
|
# public-keys waren LEER - es liegen hier also keine Zugangsdaten offen. Der Gewinn ist
|
|
# entsprechend bescheiden; der Schnitt kostet aber nichts und schliesst die Klasse.
|
|
#
|
|
# ACHTUNG beim Erweitern: Sobald eine Egress-Regel fuer einen Pod existiert, gilt fuer
|
|
# ihn Default-Deny fuer alles NICHT Aufgefuehrte. Die drei Bloecke unten sind daher
|
|
# Pflicht, nicht Bequemlichkeit - insbesondere DNS: fehlt es, steht alles, und der
|
|
# Fehler sieht wie ein Anwendungsproblem aus, nicht wie eine Firewall.
|
|
#
|
|
# Rollback: diese eine Policy im Namespace loeschen.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: egress-block-metadata
|
|
namespace: matrix
|
|
spec:
|
|
# Waehlt alle Pods AUSSER denen, die gar keinen Ausgang brauchen (#0088).
|
|
#
|
|
# ⚠️ Der Ausschluss muss HIER stehen, nicht in einer zusaetzlichen engen Policy:
|
|
# NetworkPolicies sind additiv. Solange diese Policy 0.0.0.0/0 fuer einen Pod
|
|
# erlaubt, aendert eine zweite, strengere Regel fuer denselben Pod nichts.
|
|
#
|
|
# `NotIn` waehlt auch Pods, die das Label gar nicht tragen - am 2026-08-20 im
|
|
# Cluster nachgemessen (29 Pods, 1 Treffer, notin liefert 28). Drei Pods im
|
|
# Namespace fuehren kein app.kubernetes.io/name, deshalb der zweite Ausdruck
|
|
# auf `app`. Beide Ausdruecke sind UND-verknuepft.
|
|
podSelector:
|
|
matchExpressions:
|
|
- key: app.kubernetes.io/name
|
|
operator: NotIn
|
|
values:
|
|
- element-web
|
|
- element-admin
|
|
- postgres
|
|
- wikijs-postgres
|
|
- clamav-http-scanner
|
|
- wikijs-gruppenpruefung
|
|
- haproxy
|
|
- draupnir
|
|
# Welle 1 (#0088): kein externer Bedarf nachweisbar.
|
|
- matrix-rtc-authorisation-service
|
|
- wikijs-config
|
|
- init-secrets
|
|
- synapse-check-config
|
|
- deployment-markers-pre
|
|
- deployment-markers-post
|
|
- key: app
|
|
operator: NotIn
|
|
values:
|
|
- element-web-docs
|
|
- concierge-bot
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
# 1. DNS - zuerst, weil ohne DNS nichts geht.
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 53
|
|
# 2. Cluster-intern: Pods und Services (inkl. API-Server 10.43.0.1).
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.42.0.0/16
|
|
- ipBlock:
|
|
cidr: 10.43.0.0/16
|
|
# 3. Alles uebrige - Foederation, ACME, SMTP, Registries, privates Netz -
|
|
# ausser Link-Local.
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 0.0.0.0/0
|
|
except:
|
|
- 169.254.0.0/16
|
|
|
|
---
|
|
# Pods ohne jeden Bedarf an Ausgang nach draussen (#0088).
|
|
#
|
|
# Zehn Arbeitslasten sprechen ausschliesslich cluster-intern: die drei nginx-Dienste,
|
|
# beide Postgres, der ClamAV-Vorschalter, haproxy, die Wiki-Gruppenpruefung und die
|
|
# beiden Matrix-Bots (Foederation ist geschlossen, Draupnir holt keine fremden
|
|
# Policy-Listen). Fuer sie faellt der pauschale 0.0.0.0/0-Satz weg; erlaubt bleibt
|
|
# DNS und das Cluster-Netz.
|
|
#
|
|
# Zwei Policies, weil ein podSelector nicht ueber zwei Label-Schluessel odern kann -
|
|
# die Vereinigung mehrerer Policies leistet genau das.
|
|
#
|
|
# Rollback: beide Policies loeschen und den Ausschluss in egress-block-metadata
|
|
# zuruecknehmen.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: egress-nur-intern
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchExpressions:
|
|
- key: app.kubernetes.io/name
|
|
operator: In
|
|
values:
|
|
- element-web
|
|
- element-admin
|
|
- postgres
|
|
- wikijs-postgres
|
|
- clamav-http-scanner
|
|
- wikijs-gruppenpruefung
|
|
- haproxy
|
|
- draupnir
|
|
# Welle 1 (#0088): Deploy- und Hilfs-Arbeitslasten.
|
|
- matrix-rtc-authorisation-service
|
|
- wikijs-config
|
|
- init-secrets
|
|
- synapse-check-config
|
|
- deployment-markers-pre
|
|
- deployment-markers-post
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 53
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.42.0.0/16
|
|
- ipBlock:
|
|
cidr: 10.43.0.0/16
|
|
---
|
|
# Dieselben Regeln fuer die zwei Pods, die kein app.kubernetes.io/name fuehren.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: egress-nur-intern-app-label
|
|
namespace: matrix
|
|
spec:
|
|
podSelector:
|
|
matchExpressions:
|
|
- key: app
|
|
operator: In
|
|
values:
|
|
- element-web-docs
|
|
- concierge-bot
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 53
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.42.0.0/16
|
|
- ipBlock:
|
|
cidr: 10.43.0.0/16
|