9. Security¶
Goal: restrict who can reach the database at two layers — the Kubernetes
network (NetworkPolicy) and PostgreSQL's own host-based auth (pg_hba.conf) —
and apply sane RBAC. Defense in depth.
The two layers¶
flowchart LR
other["Other pods"] -. blocked by NetworkPolicy .-> pg
app["App pods (label app=myapp)"] -->|allowed| netpol["NetworkPolicy<br/>(L3/L4 firewall)"] --> hba["pg_hba.conf<br/>(Postgres-level auth)"] --> pg["PostgreSQL"]
- NetworkPolicy decides which pods may even open a TCP connection to 5432.
pg_hba.confdecides which clients PostgreSQL itself accepts, and how they authenticate.
Step 9.1 — A NetworkPolicy (the network firewall)¶
Allow only your application pods (and the database's own pods, for replication) to reach the cluster on 5432.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-app-to-pg
namespace: production
spec:
podSelector:
matchLabels:
cnpg.io/cluster: pg # (1)!
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: myapp # (2)!
ports:
- protocol: TCP
port: 5432
- from:
- podSelector:
matchLabels:
cnpg.io/cluster: pg # (3)!
ports:
- protocol: TCP
port: 5432
- Selects the database pods (the operator labels them
cnpg.io/cluster: pg). - Only pods labeled
app: myappmay connect. - Allow the database pods to talk to each other (replication).
Does your CNI enforce NetworkPolicy?
A NetworkPolicy is only meaningful if the network plugin enforces it. On kube-hetzner, k3s ships a built-in NetworkPolicy controller that enforces policies even with the default Flannel CNI; Cilium and Calico enforce them too. If you explicitly disabled the controller, the policy is silently ignored. Confirm which CNI you chose and that enforcement is on.
Step 9.2 — pg_hba with dynamic pod selectors (CNPG 1.29)¶
CloudNativePG 1.29 added podSelectorRefs: you write pg_hba.conf rules
that target client pods by label, and the operator resolves their (changing)
IPs for you — no static CIDRs to maintain. Add this to the Cluster's
spec.postgresql:
postgresql:
# ... synchronous, parameters from chapter 6 ...
pg_hba: # (1)!
- hostssl app app_user podSelectorRefs(app=myapp) scram-sha-256
- Allow user
app_userinto databaseappover TLS, but only from pods labeledapp=myapp, authenticating with SCRAM. The exactpodSelectorRefssyntax is new — confirm it against the 1.29 docs for your minor version.
This complements the NetworkPolicy: even if something slips past the network layer, PostgreSQL itself only trusts the right pods.
Step 9.3 — RBAC hygiene¶
The operator already creates tightly-scoped RBAC for itself. Your job is to not hand application service accounts permission to touch CNPG resources:
- Application pods need access to the Secret (to read the DB password) and the network path — nothing more.
- Do not grant apps
get/list/editonCluster,Backup, orObjectStore. Those are operations concerns.
A minimal Role for an app that only reads its DB secret:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-pg-app-secret
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["pg-app-credentials"]
verbs: ["get"]
(Bind it to the app's ServiceAccount with a RoleBinding.)
Step 9.4 — TLS is already on¶
CNPG issues and rotates TLS certificates for client and replication traffic by
default. Using hostssl in pg_hba (above) requires clients to use TLS. You
generally do not need to manage these certs by hand.
What could go wrong¶
- NetworkPolicy ignored → CNI not enforcing; verify your CNI/controller.
- Locked yourself out → a too-strict NetworkPolicy or
pg_hbacan block the app or even replication. Always allow thecnpg.io/cluster: pg→ 5432 path for intra-cluster replication. podSelectorRefssyntax → it is version-specific and new; check the exact form in the 1.29 docs before relying on it.
Where to go deeper¶
- Kubernetes NetworkPolicy
- k3s network policy controller
- CNPG security & pg_hba
- PostgreSQL pg_hba.conf
Next: Failover & switchover.