Skip to content

fix(netpol): scope datastore-toegang + default-deny in rig-system#75

Merged
uittenbroekrobbert merged 10 commits into
mainfrom
fix/datastore-netpol-scope
May 22, 2026
Merged

fix(netpol): scope datastore-toegang + default-deny in rig-system#75
uittenbroekrobbert merged 10 commits into
mainfrom
fix/datastore-netpol-scope

Conversation

@anneschuth

Copy link
Copy Markdown
Member

Probleem

De productie-NetworkPolicies van de gedeelde datastores in rig-system
(overlays/odcn) stonden ingress toe vanuit alle namespaces via een
lege namespaceSelector: {}, en er bestond nergens in de repo een
default-deny. Concreet:

  • Postgres: from: [{namespaceSelector: {}}] -> 5432, plus een volledig
    ongelimiteerde egress: - {} vanuit de DB-pod.
  • MinIO: namespaceSelector: {} -> 9000 (S3 API).
  • Vault: namespaceSelector: {} -> 8200 (Vault API).

Gevolg: elke tenant-pod (rig-prd-{willekeurig project}) kon een TCP-
verbinding opzetten naar elke gedeelde datastore. De enige scheiding
tussen tenants was applicatie-niveau credentials, niet het netwerk. Dit
is cross-tenant bereikbaarheid op de gedeelde dataopslag.

Oplossing

Scoping van de bestaande policies (legitieme consumenten behouden:
de bestaande rig-system-, ingress-nginx- en monitoring-regels zijn
ongewijzigd):

  • Postgres: lege selector vervangen door een tenant-allowlist op
    namespaceSelector.matchLabels.created-by: operations-manager.
  • MinIO: idem voor de S3 API-poort 9000.
  • Vault: de all-namespaces-regel is volledig verwijderd. Vault
    wordt uitsluitend door OPI in rig-system gebruikt; geen enkele tenant
    heeft Vault nodig, dus dit is het strakst gescoped.
  • Postgres egress: egress: - {} ingeperkt tot DNS (kube-dns),
    intra-rig-system (CNPG-replicatie/WAL + in-cluster MinIO-backuptarget)
    en HTTPS uitgaand (off-cluster backup/WAL-archief over TLS). Aanname:
    backups gaan naar een HTTPS S3-endpoint; als er uitsluitend een
    in-cluster backuptarget is, kan de 0.0.0.0/0:443-regel verder dicht.

Default-deny vangnet:

  • Nieuwe default-deny-ingress NetworkPolicy (podSelector: {},
    policyTypes: [Ingress]) in rig-system, gewired via
    postgresql/database/overlays/odcn/kustomization.yaml. Dit zorgt dat
    een datastore-pod zonder matchende allow-policy standaard onbereikbaar
    is, zodat een toekomstige lege-selector-regressie niet stilletjes alles
    heropent. Egress blijft hier bewust onbeheerd zodat andere
    rig-system-workloads niet geraakt worden.

Afhankelijkheid: tenant-namespace-label

Tenant-namespaces krijgen vandaag van OPI exact een onderscheidend label:
created-by: operations-manager
(zie operations-manager/python/manifests/namespace.yaml.jinja). Infra-
namespaces (rig-system, monitoring, ingress-nginx) dragen dit label
niet. De scoping gebruikt dit bestaande label - er is geen nieuw,
nog-niet-bestaand label verzonnen. Er is geen per-tenant onderscheidend
label; deze policies isoleren tenants als groep van infra, niet tenants
onderling op netwerkniveau (dat is buiten scope van deze fix en hangt
samen met de per-tenant allow-all policy die OPI genereert - aparte issue).

Pre-merge gate (verplicht)

kustomize is niet beschikbaar in de werkomgeving en de productie-SOPS-
sleutel ontbreekt lokaal, dus een volledige kustomize build van de
odcn-overlays kon hier niet draaien. Wel gevalideerd: alle gewijzigde en
toegevoegde YAML-bestanden parsen correct (ruamel) en de pre-commit
check yaml-hook is geslaagd. Voor merge moet een
kustomize build van de drie odcn-overlays draaien als gate:

  • infrastructure/bootstrap/infrastructure/postgresql/database/overlays/odcn
  • infrastructure/bootstrap/infrastructure/minio/config/overlays/odcn
  • infrastructure/bootstrap/infrastructure/vault/config/overlays/odcn

Aard

Pre-existing en onafhankelijk van andere openstaande security-fixes.
Raakt alleen overlays/odcn NetworkPolicies en een kustomization-resourcelijst.

@uittenbroekrobbert

Copy link
Copy Markdown
Contributor

⚠️ Twee blokkers — fix nodig vóór merge, anders breekt productie. Richting van de scoping is correct (zelfde klasse als #64), maar de huidige set wijzigingen brengt productie offline.

Blokker 1: OPI verliest Postgres-toegang

OPI draait in rig-prd-operations (bootstrap/rig-system/kustomize/operations-manager/overlays/odcn-production/kustomization.yaml:4, configmap.yaml:26 DATABASE_HOST=rig-db-rw). Die namespace heeft niet het label created-by: operations-manager — dat label zet OPI alleen op door OPI gegenereerde tenant-namespaces (zie operations-manager/python/manifests/namespace.yaml.jinja:6).

De nieuwe ingress-rule op postgresql-networkpolicy.yaml:22-29 accepteert: rig-system (intra-ns) óf created-by: operations-manager (tenants). rig-prd-operations matcht beide niet → DB-verkeer naar rig-db-rw:5432 geblokkeerd → OPI start niet.

Fix-opties (kies een):

A. Voeg een derde ingress-rule toe voor kubernetes.io/metadata.name: rig-prd-operations.

B. Label rig-prd-operations eenmalig in de overlay-namespace.yaml met created-by: operations-manager zodat de bestaande rule matcht.

A is netter (geen "alsof het een tenant is"-label op de ops-namespace).

Blokker 2: default-deny-ingress breekt Keycloak en Redis

default-deny-ingress.yaml:21-23 selecteert podSelector: {} met policyTypes: [Ingress] — raakt elke pod in rig-system. In productie draaien daar (zie infrastructure/bootstrap/clusters/odcn/kustomization.yaml) ook:

  • Keycloak (infrastructure/bootstrap/infrastructure/keycloak/controller/overlays/odcn) — geen NetworkPolicy
  • Redis (infrastructure/bootstrap/infrastructure/redis/controller/overlays/odcn) — geen NetworkPolicy

Resultaat na deploy:

  • OpenShift router → Keycloak: geblokkeerd → auth-frontdeur kapot
  • rig-prd-operations → Redis: geblokkeerd

Fix-opties (kies een):

A. Versmal de default-deny via matchExpressions zodat hij alleen de datastore-pods selecteert (postgres/minio/vault), niet Keycloak/Redis.

B. Voeg in deze PR allow-ingress-policies toe voor Keycloak (router → 8080/8443) en Redis (rig-prd-operations → 6379).

A is consistenter met de KISS-lijn van #64.

Architectonische opmerking

default-deny-ingress.yaml zit nu in infrastructure/bootstrap/infrastructure/postgresql/database/overlays/odcn/network-policies/. Maar de policy claimt namespace-brede eigenaarschap (podSelector: {} in rig-system). Hoort architectonisch in infrastructure/common of een eigen security/ overlay — anders heeft de postgres-overlay een verborgen side-effect op de hele namespace.

Twee opmerkingen die geen blokker zijn

  • vault/config/overlays/odcn en minio/config/overlays/odcn zijn uitgecommentarieerd in infrastructure/bootstrap/clusters/odcn/kustomization.yaml:7-8,14. De scoping daarin is correct als preventieve hardening, maar nul productie-effect nu. Even expliciet maken in PR-tekst dat dat bewust is.
  • postgresql-networkpolicy.yaml:60-65 egress: 0.0.0.0/0:443 is speculatief — cluster.yaml-barmanObjectStore staat uitgecommentarieerd, geen actieve off-cluster backup-target. YAGNI: weghalen tot er een concrete backup-destination CIDR is.

Pre-merge gate

kustomize build alleen is voor deze klasse onvoldoende. Verplicht op staging vóór merge:

  • OPI → rig-db-rw:5432 werkt
  • Keycloak-login via OpenShift router werkt
  • OPI → Redis werkt
  • CNPG-WAL/replicatie intra-rig-system werkt

Zonder rookproef komen blokkers 1 en 2 pas op productie aan het licht.

@uittenbroekrobbert

Copy link
Copy Markdown
Contributor

Follow-up: Blokker 2 opgelost door author, Blokker 1 augmentation gepusht

Blokker 2 (default-deny breekt Keycloak/Redis) is volledig opgelost door author in commit `b28e194c` — de `default-deny-ingress` selecteert nu alleen `cnpg.io/cluster: rig-db` pods, met expliciete comment die de Keycloak/Redis-impact erkent. Mooi gedaan.

Blokker 1 (OPI verliest postgres-toegang) persisteerde nog: `rig-prd-operations` heeft geen `created-by: operations-manager` label (OPI genereert zijn eigen namespace niet), dus de tenant-rule miste 'm. Augmentation gepusht (`1fa59764`): derde ingress-rule met expliciete naam-match op `kubernetes.io/metadata.name: rig-prd-operations`. Additief — beperkt niets, dekt alleen de OPI-namespace.

Verificatie deep-audit

  • Default-deny scope (postgres pods only): geverifieerd, comment in YAML documenteert het waarom (Keycloak/Redis-uitsluiting).
  • OPI Postgres-pad: `bootstrap/.../odcn-production/kustomization.yaml:4` namespace=`rig-prd-operations`, `configmap.yaml:26` `DATABASE_HOST=rig-db-rw`. Namespace YAML (`namespace.yaml`) draagt geen labels — alleen annotations — bevestigt dat zonder mijn rule de ingress niet matcht.
  • MinIO/Vault overlays niet-actief: `infrastructure/bootstrap/clusters/odcn/kustomization.yaml:7-8,14` heeft `vault/config` en `minio/config` uitgecommentarieerd. De scoping op die files is correcte preventieve hardening voor wanneer ze geactiveerd worden, maar heeft nu nul productie-impact.
  • OPI gebruikt geen Vault: geen `vault` referenties in `operations-manager/python/opi/connectors/` of de odcn-overlay configmap. Volledige verwijdering van de all-namespaces Vault-rule is dus veilig.
  • Andere consumers van shared `rig-db`: Keycloak (in `rig-system`, gedekt door rule 1), tenant-namespaces (gedekt door rule 2), OPI (nu gedekt door rule 3). Geen andere consumers gevonden.

Pre-merge gate

`kustomize build` van `infrastructure/.../postgresql/database/overlays/odcn` schoon. Voor merge: rookproef op staging:

  • OPI → `rig-db-rw:5432` werkt
  • Keycloak-login via OpenShift router werkt
  • Tenant-pod → `rig-db-rw:5432` werkt (één willekeurig project)
  • CNPG-WAL/replicatie intra-`rig-system` werkt

Daarna LGTM voor merge.

Aparte opmerking (niet blokkerend)

`rig-prd-operations` is conceptueel een door-OPI-bediende namespace; je zou er ook gewoon het label `created-by: operations-manager` op kunnen zetten (in `bootstrap/rig-system/kustomize/overlays/odcn-production/namespace.yaml`), dan dekt rule 2 het al en valt rule 3 weg. Twee mechanismes voor één concept is dan op termijn op te ruimen. Voor nu is de rule duidelijk genoeg en maakt niks stuk.

@uittenbroekrobbert

Copy link
Copy Markdown
Contributor

Follow-up issues aangemaakt

Items uit deze PR die naar follow-ups verschuiven:

Pre-merge gates (staging-validatie) blijven in deze PR.

@uittenbroekrobbert uittenbroekrobbert force-pushed the fix/datastore-netpol-scope branch from 1fa5976 to 3c81775 Compare May 20, 2026 06:37
@uittenbroekrobbert

uittenbroekrobbert commented May 21, 2026

Copy link
Copy Markdown
Contributor

Productie-realiteit

  • rig-system namespace bestaat niet op odcn — alles draait in rig-prd-operations.
  • CMP plugin schrijft kustomization-namespace om naar ARGOCD_APP_NAMESPACE maar laat label-selectors binnen netpol-rules letterlijk staan, dus selectors moeten zelf op de werkelijke namespace wijzen.
  • Oude tenants missen created-by: operations-managerrig-prd-amt (actief) en rig-prd-test met kubectl label gepatcht; rig-prd-example is leeg.

Wijzigingen

  • 4 netpols: namespace + selectors op rig-prd-operations, allow vanuit platform (name match) + tenants (created-by label).
  • Ingress controller bron op odcn = openshift-ingress.
  • Default-deny behouden als zelfdocumenterende baseline.

Volgende

Argo dry-run + sync op staging vóór merge.

@uittenbroekrobbert uittenbroekrobbert force-pushed the fix/datastore-netpol-scope branch from a64be27 to 95d083e Compare May 21, 2026 08:17
anneschuth and others added 6 commits May 22, 2026 16:03
De productie-NetworkPolicies van de gedeelde datastores in rig-system
stonden ingress toe vanuit ALLE namespaces via een lege
`namespaceSelector: {}`, en er was nergens een default-deny. Daardoor kon
elke tenant-pod (rig-prd-{willekeurig project}) TCP openen naar Postgres
(5432), MinIO (9000) en Vault (8200); alleen applicatie-credentials
scheidden tenants nog van elkaar.

Wijzigingen:
- Postgres: lege selector vervangen door een tenant-allowlist op label
  `created-by: operations-manager` (stempel die OPI op tenant-namespaces
  zet, niet op infra-namespaces). Ongelimiteerde `egress: - {}` van de
  DB-pod ingeperkt tot DNS, intra-rig-system (CNPG-replicatie + in-cluster
  MinIO-backuptarget) en HTTPS uitgaand (off-cluster backup/WAL).
- MinIO: lege selector vervangen door dezelfde tenant-allowlist voor de
  S3 API-poort (9000).
- Vault: de all-namespaces-regel volledig verwijderd. Vault wordt alleen
  door OPI in rig-system gebruikt; geen enkele tenant heeft Vault nodig.
- Nieuwe default-deny-ingress NetworkPolicy in rig-system als vangnet,
  zodat een toekomstige lege-selector-regressie niet alles heropent.
De default-deny-ingress gebruikte podSelector: {} en selecteerde
daarmee elke pod in rig-system. In de odcn-productiebundel draaien
Keycloak en Redis ook in rig-system zonder eigen NetworkPolicy. Een
cluster-brede deny zou al hun ingress blokkeren (OPI -> Keycloak/Redis,
ingress-nginx -> Keycloak, tenant -> OIDC), een platformbrede storing
die erger is dan de cross-tenant blootstelling die deze PR dicht.

Selector nu beperkt tot de CNPG-postgres-pods (cnpg.io/cluster: rig-db),
conform het gedocumenteerde doel van de policy.
De ingress-rules accepteerden alleen rig-system (intra-namespace) en
tenant-namespaces met label 'created-by: operations-manager'. In
odcn-production draait OPI echter in 'rig-prd-operations', een
bootstrap-gegenereerde namespace die dat label niet draagt (OPI
genereert zijn eigen namespace niet). Zonder expliciete naam-match
viel OPI's verbinding naar rig-db-rw:5432 onder de default-deny en
zou OPI niet meer kunnen starten na deze policy-update.

De toegevoegde rule is een namespaceSelector op
'kubernetes.io/metadata.name: rig-prd-operations', dezelfde vorm als
de bestaande rig-system-rule. Tenant-toegang via created-by-label
en monitoring-toegang via aparte allow-postgres-monitoring policy
blijven ongewijzigd.
Voorgaande versie verwees overal naar 'rig-system' en aanverwante
labels, maar die namespace bestaat niet op odcn-production. De kustomize-sops
CMP plugin schrijft alleen de top-level kustomization.yaml namespace om naar
ARGOCD_APP_NAMESPACE (rig-prd-operations); label-selectors binnen
NetworkPolicy ingress/egress regels blijven echter letterlijk staan en
matchten dus niets, waardoor de cross-tenant lek bleef bestaan.

Vier files herzien:
- postgresql-networkpolicy.yaml: namespace rig-prd-operations,
  ingress-rules op kubernetes.io/metadata.name=rig-prd-operations
  (platform: OPI + Keycloak + co-located) plus created-by=operations-manager
  (tenant-namespaces; oude pre-label-era tenants zijn handmatig gepatcht).
- default-deny-ingress.yaml: namespace rig-prd-operations.
- minio-networkpolicy.yaml: zelfde labelmodel; ingress controller bron
  is openshift-ingress (odcn) ipv ingress-nginx.
- vault-networkpolicy.yaml: idem. Vault wordt alleen door OPI consumed
  in rig-prd-operations.

Tenant-namespaces zonder created-by-label (rig-prd-amt, rig-prd-test)
zijn met kubectl label gepatcht voor backfill. rig-prd-example was leeg.
@uittenbroekrobbert uittenbroekrobbert force-pushed the fix/datastore-netpol-scope branch from 8ac22d8 to c110b88 Compare May 22, 2026 14:03
Eén NetworkPolicy-file per service, met deny + allow samen. Verwijdert
de losse default-deny-ingress.yaml; elke service-file is nu zelfdragend.
K8s NetworkPolicy is allow-only; podSelector + policyTypes maakt pods
automatisch deny-by-default. Aparte deny-policy was puur cosmetisch en
verwarrend. Header-comment in elke file legt het kort uit.
Vault is niet deployed op productie; barmanObjectStore is niet geconfigureerd.
Egress naar 0.0.0.0/0:443 was erfgoed van een originele PR-aanname, niet
gebaseerd op werkelijke usage. YAGNI — toevoegen wanneer feature aangezet
wordt en het concrete CIDR-bereik bekend is.
…orrigeerd

Live productie-verificatie:
- minio pod heeft label 'app: minio', niet 'app.kubernetes.io/name: minio'
  (selector aangepast; oude selector matchte 0 pods).
- minio/config/overlays/odcn was uitgecommentarieerd in clusters/odcn —
  geactiveerd, anders had de minio-policy geen effect op productie.
- rig-redis (in rig-prd-operations) had geen NetworkPolicy; tenants en
  alle cluster-pods konden cross-tenant cache-data benaderen. Policy
  toegevoegd: alleen rig-prd-operations + tenant-namespaces met
  created-by-label op port 6379.
- Postgres + MinIO + Redis egress beperkt tot DNS + intra-namespace
  (Postgres CNPG replicatie). Geen speculatieve internet-egress.
@uittenbroekrobbert uittenbroekrobbert merged commit 672a11f into main May 22, 2026
14 of 15 checks passed
@uittenbroekrobbert uittenbroekrobbert deleted the fix/datastore-netpol-scope branch May 22, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants