Kubernetes security: what I actually enforce
Best-practice lists are easy to write and cheap to believe. This is the list I actually enforce on the clusters I operate, with the manifests I use.
1. Network policies first
Default-deny is non-negotiable. Everything else is allowlisting traffic that should never have existed:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: app
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Then allow only what the workload needs — namespace to namespace, port to port. CNI choice matters here; I use Cilium or Calico, both with NetworkPolicy support.
Lesson: the first time you enable default-deny on a running namespace, you discover exactly which implicit traffic your app was secretly relying on. Budget a rollback window for that.
2. RBAC with real least privilege
Rules of thumb I apply:
- No cluster-admin outside the platform team, and even then, never in CI tokens.
- CI gets per-namespace service accounts, not shared credentials.
- Regular reviews:
kubectl auth can-i --listper team, and revoke what’s unused.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app
name: deployer
rules:
- apiGroups: ['apps', '']
resources: ['deployments', 'services', 'configmaps']
verbs: ['get', 'list', 'create', 'update', 'patch']
Lesson: an audit of can-i --list outputs is the cheapest RBAC cleanup you will
ever do — most clusters accumulate rights nobody still needs.
3. Secrets: no plaintext, no ConfigMaps
ConfigMaps are for configuration, not credentials. Depending on the platform:
- ExternalSecrets pulling from a vault on the cluster side,
- SealedSecrets when the cluster must be self-contained,
- direct Vault integration for the sensitive ones.
Whatever the mechanism, the rule is the same: the manifest repository must never contain a real secret, and git history must never have contained one.
4. Image security: scan before deploy, not after
Every build pushes to a registry where the scan runs at rest, and promotion is gated on it:
# .gitlab-ci.yml — extract
scan_image:
stage: security
image: aquasec/trivy
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL "$IMAGE_TAG"
--exit-code 1 is the important part: a failing scan must fail the pipeline. Add
admission-time enforcement (Kyverno or similar) as the second gate, so images can’t
bypass the pipeline path entirely.
What this buys you
Network segmentation, least privilege, secrets hygiene and image scanning cover the majority of what actually gets clusters compromised. None of it is exciting — and that is precisely the point. Security work that makes news is usually the work you skipped.
Written by Rulx Philomé Alexis · ← All notes