feat(kube-system): add cilium network segmentation policies - #4072
Conversation
Introduce two static CiliumNetworkPolicies on the previously flat pod network: - database ingress allowlist (default-deny ingress for all database pods, allow only ai/default/media/observability/security consumers, intra-ns, and kube-apiserver->cnpg webhooks) - deny kube-apiserver egress in media/default/web3 for non-kopiur pods (enableDefaultDeny egress:false keeps all other egress open; kopiur movers excluded)
@@ (root level) @@
# kustomize.toolkit.fluxcd.io/v1/Kustomization/kube-system/network-policies
! + one document added:
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+ name: network-policies
+ namespace: kube-system
+ labels:
+ kustomize.toolkit.fluxcd.io/name: cluster-apps
+ kustomize.toolkit.fluxcd.io/namespace: flux-system
+ spec:
+ decryption:
+ provider: sops
+ secretRef:
+ name: sops-age
+ deletionPolicy: WaitForTermination
+ interval: 1h
+ patches:
+ - patch: |
+ apiVersion: helm.toolkit.fluxcd.io/v2
+ kind: HelmRelease
+ metadata:
+ name: _
+ spec:
+ install:
+ crds: CreateReplace
+ rollback:
+ cleanupOnFail: true
+ upgrade:
+ cleanupOnFail: true
+ crds: CreateReplace
+ strategy:
+ name: RemediateOnFailure
+ remediation:
+ remediateLastFailure: true
+ retries: 2
+ target:
+ kind: HelmRelease
+ group: helm.toolkit.fluxcd.io
+ path: ./kubernetes/apps/kube-system/network-policies/app
+ postBuild:
+ substituteFrom:
+ - name: cluster-settings
+ kind: ConfigMap
+ optional: false
+ - name: cluster-secrets
+ kind: Secret
+ optional: false
+ prune: true
+ retryInterval: 2m
+ sourceRef:
+ name: flux-system
+ kind: GitRepository
+ namespace: flux-system
+ timeout: 5m
+ wait: false
@@ (root level) @@
# cilium.io/v2/CiliumNetworkPolicy/database/database-ingress-allowlist
! + one document added:
+ apiVersion: cilium.io/v2
+ kind: CiliumNetworkPolicy
+ metadata:
+ name: database-ingress-allowlist
+ namespace: database
+ labels:
+ kustomize.toolkit.fluxcd.io/name: network-policies
+ kustomize.toolkit.fluxcd.io/namespace: kube-system
+ spec:
+ endpointSelector: {}
+ ingress:
+ - fromEndpoints:
+ - matchLabels:
+ io.kubernetes.pod.namespace: ai
+ - matchLabels:
+ io.kubernetes.pod.namespace: default
+ - matchLabels:
+ io.kubernetes.pod.namespace: media
+ - matchLabels:
+ io.kubernetes.pod.namespace: observability
+ - matchLabels:
+ io.kubernetes.pod.namespace: security
+ - matchLabels:
+ io.kubernetes.pod.namespace: database
+ - fromEntities:
+ - kube-apiserver
@@ (root level) @@
# cilium.io/v2/CiliumNetworkPolicy/media/deny-kube-apiserver-egress
! + one document added:
+ apiVersion: cilium.io/v2
+ kind: CiliumNetworkPolicy
+ metadata:
+ name: deny-kube-apiserver-egress
+ namespace: media
+ labels:
+ kustomize.toolkit.fluxcd.io/name: network-policies
+ kustomize.toolkit.fluxcd.io/namespace: kube-system
+ spec:
+ egressDeny:
+ - toEntities:
+ - kube-apiserver
+ enableDefaultDeny:
+ egress: false
+ endpointSelector:
+ matchExpressions:
+ - key: app.kubernetes.io/managed-by
+ operator: NotIn
+ values:
+ - kopiur
@@ (root level) @@
# cilium.io/v2/CiliumNetworkPolicy/default/deny-kube-apiserver-egress
! + one document added:
+ apiVersion: cilium.io/v2
+ kind: CiliumNetworkPolicy
+ metadata:
+ name: deny-kube-apiserver-egress
+ namespace: default
+ labels:
+ kustomize.toolkit.fluxcd.io/name: network-policies
+ kustomize.toolkit.fluxcd.io/namespace: kube-system
+ spec:
+ egressDeny:
+ - toEntities:
+ - kube-apiserver
+ enableDefaultDeny:
+ egress: false
+ endpointSelector:
+ matchExpressions:
+ - key: app.kubernetes.io/managed-by
+ operator: NotIn
+ values:
+ - kopiur
@@ (root level) @@
# cilium.io/v2/CiliumNetworkPolicy/web3/deny-kube-apiserver-egress
! + one document added:
+ apiVersion: cilium.io/v2
+ kind: CiliumNetworkPolicy
+ metadata:
+ name: deny-kube-apiserver-egress
+ namespace: web3
+ labels:
+ kustomize.toolkit.fluxcd.io/name: network-policies
+ kustomize.toolkit.fluxcd.io/namespace: kube-system
+ spec:
+ egressDeny:
+ - toEntities:
+ - kube-apiserver
+ enableDefaultDeny:
+ egress: false
+ endpointSelector:
+ matchExpressions:
+ - key: app.kubernetes.io/managed-by
+ operator: NotIn
+ values:
+ - kopiur
|
AI Automated ReviewAnalysis engine: qwen-3.6-fast@http://litellm.ai.svc.cluster.local/v1 (openai) RecommendationApprove — The PR introduces well-scoped CiliumNetworkPolicies that establish baseline network segmentation for database ingress and apiserver egress. The manifests follow repository conventions, are internally consistent, and address the stated security goals without introducing breaking changes to existing workloads. Change-by-Change Findings
|
First network policies on a previously flat pod network (zero policies existed cluster-wide; Cilium
enable-policy: default, so unselected pods stay default-allow). Two static, maintenance-free CiliumNetworkPolicies wired viakube-system/network-policies.Policy 1 —
database-ingress-allowlist(namespace: database)endpointSelector: {}puts every database pod (postgres, pgbouncer poolers, dragonfly, cnpg + dragonfly operators) into default-deny ingress. Allowed ingress:ai,default,media,observability,security,database— the only postgres/pgbouncer/dragonfly consumers (derived from the cnpgdatabases/CRs + dragonfly host refs).observabilitycovers vmagent scraping the exporters;databasecovers intra-ns operator/pooler/replica traffic.kube-apiserver— cnpg serves validating/mutating webhooks the apiserver must reach.allow-localhost: auto(host-firewall is off); egress from DB pods is untouched (only ingress rules present), so backups to NFS/S3 and operator→apiserver are unaffected.Denies: ingress from any other namespace (kube-system, network, kopiur-system, actions-runner, cert-manager, rook-ceph, web3, ai-non-consumers, etc.).
Policy 2 —
deny-kube-apiserver-egress(namespaces: media, default, web3)An
egressDenyto entitykube-apiserveron the internet-exposed app namespaces, withenableDefaultDeny.egress: falseso it is a pure deny overlay — DNS, internet, and all other cluster egress remain default-allow.endpointSelectorusesmatchExpressions: app.kubernetes.io/managed-by NotIn [kopiur], which also matches label-absent app pods (so they ARE denied) while excluding kopiur mover pods, which legitimately need the apiserver (kopiur-moverSA confirmed present in media/default; web3 has none). Audited running pods in all three namespaces: every workload is an application pod with no apiserver need.Denies: kube-apiserver egress from all app pods in media/default/web3. Allows: everything else, plus full egress for kopiur movers.
Verification
kustomize buildon the app dir and parentkube-systemboth succeed; namespaces render correctly (no namespace-transformer clobber — ks.yaml intentionally omitstargetNamespace).kubectl apply --dry-run=serveraccepts all 4 objects against the deployed Cilium CRD (schema 1.32.7):enableDefaultDeny,egressDeny.toEntities: kube-apiserver, andNotInall valid.cilium-config:enable-policy: default,enable-host-firewall: false.Post-merge checks: after Flux applies, watch for connection resets on DB consumers (
hubble observe --namespace database --verdict DROPPED) and confirm kopiur backup/restore jobs in media/default still reach the apiserver. If a consumer namespace was missed, add onematchLabels: io.kubernetes.pod.namespaceentry to policy 1.Rollback
Delete the two policy files (
network-policies/app/database-ingress.yaml,deny-apiserver-egress.yaml) — or remove./network-policies/ks.yamlfromkube-system/kustomization.yamlto drop the whole app; Flux prunes both.