Skip to content

Network Policies

HomeKube uses Kubernetes NetworkPolicy objects enforced by Cilium to implement zero-trust network segmentation. Each namespace has a default-deny baseline, with explicit allow rules for only the traffic that must flow.


Design Principles

  1. Default deny — every namespace starts with a deny all ingress policy
  2. Explicit allow — traffic is permitted only by named policies with specific selectors
  3. Namespace isolation — pods in one namespace cannot reach pods in another unless a policy says so
  4. In-cluster DNS always allowed — all namespaces allow egress to kube-system on UDP/TCP 53

Per-Namespace Summary

Namespace Default Deny Allowed Ingress Allowed Egress
default ✅ Ingress Prometheus scrape (port 9090, 8080) DNS only
identity ✅ Ingress ingress-nginx → port 80; monitoring Prometheus → port 8080 DNS only
monitoring ✅ Ingress ingress-nginx → Grafana port 3000; Grafana → Keycloak (identity) DNS only

identity Namespace (Keycloak)

default-deny-ingress

Blocks all ingress by default. No pod in identity is reachable unless an explicit allow policy matches.

spec:
  podSelector: {}      # applies to all pods
  policyTypes: [Ingress]

allow-ingress-nginx

Permits ingress-nginx to forward HTTP traffic to Keycloak on port 80 (the ingress controller terminates TLS externally).

ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: ingress-nginx
    ports:
      - port: 80

allow-prometheus-scrape

Permits Prometheus (running in monitoring) to scrape Keycloak metrics on port 8080 (if a metrics endpoint is exposed).

ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: monitoring
        podSelector:
          matchLabels:
            app.kubernetes.io/name: prometheus
    ports:
      - port: 8080

allow-dns-egress

All pods in identity may send DNS queries to kube-system (CoreDNS) on UDP/TCP 53. Required for in-cluster service discovery.


monitoring Namespace (Grafana + Prometheus)

default-deny-ingress

Blocks all ingress to monitoring pods by default.

allow-ingress-nginx

Permits ingress-nginx to forward HTTP to Grafana on port 3000.

ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: ingress-nginx
    ports:
      - port: 3000

allow-grafana-to-keycloak

Permits Grafana to reach Keycloak's HTTP service in identity on port 80. This is the egress side of the OIDC token exchange (token_url and api_url use keycloak.identity.svc.cluster.local).

egress:
  - to:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: identity
    ports:
      - port: 80

allow-dns-egress

All pods in monitoring may send DNS queries to CoreDNS.


default Namespace

Covers any ad-hoc pods. Default deny ingress is in place. DNS egress and Prometheus scrape are permitted so that any workload deployed here can be observed.


Files

File Namespace
infrastructure/network-policies/default-deny-ingress.yaml default
infrastructure/network-policies/allow-dns-egress.yaml default
infrastructure/network-policies/allow-monitoring-scrape.yaml default
identity/network-policies.yaml identity
observability/network-policies.yaml monitoring

Verifying with Hubble

# Open the Hubble flow visualiser
cilium hubble ui

# CLI: watch live flows in the identity namespace
hubble observe --namespace identity --follow

# CLI: check if a specific flow is allowed or dropped
hubble observe --namespace monitoring --to-namespace identity --verdict FORWARDED
hubble observe --namespace monitoring --to-namespace identity --verdict DROPPED

A DROPPED verdict from monitoring → identity on port 80 means the allow-grafana-to-keycloak egress policy is missing or not applied yet. Wait for Flux to reconcile or run:

flux reconcile kustomization identity --with-source
flux reconcile kustomization observability --with-source