Kubernetes defaults prioritize ease of use over security. Production clusters need hardening. Here’s a comprehensive checklist.
Cluster Configuration
API Server
Disable anonymous authentication:
--anonymous-auth=false
Enable audit logging:
--audit-log-path=/var/log/kubernetes/audit.log
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
Enable admission controllers:
--enable-admission-plugins=NodeRestriction,PodSecurityPolicy
etcd Security
Enable TLS:
--cert-file=/etc/etcd/server.crt
--key-file=/etc/etcd/server.key
--client-cert-auth=true
--trusted-ca-file=/etc/etcd/ca.crt
Encrypt secrets at rest:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-key>
- identity: {}
Kubelet
Disable anonymous access:
--anonymous-auth=false
--authorization-mode=Webhook
Read-only port:
--read-only-port=0
RBAC
Least Privilege Roles
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-manager
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
Avoid Cluster-Admin
# Bad - too permissive
kind: ClusterRoleBinding
roleRef:
kind: ClusterRole
name: cluster-admin # Never for regular users
# Good - scoped permissions
kind: RoleBinding
roleRef:
kind: Role
name: deployment-manager
Service Account Best Practices
# Don't use default service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: api-service
automountServiceAccountToken: false # Only mount when needed
---
# Pod uses specific SA
spec:
serviceAccountName: api-service
automountServiceAccountToken: true # Explicitly enable when needed
Pod Security
Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.25
Security Context
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
Resource Limits
containers:
- name: app
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "100m"
Prevents resource exhaustion attacks.
Network Security
Network Policies
# Default deny all
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
Service Mesh mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
Secrets Management
Don’t Use Environment Variables
# Bad - secrets in env vars
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# Better - mount as files
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: db-secret
External Secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
secretStoreRef:
name: vault
kind: ClusterSecretStore
target:
name: db-secret
data:
- secretKey: password
remoteRef:
key: secret/data/db
property: password
Image Security
Image Policies
# Only allow trusted registries
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
validationFailureAction: enforce
rules:
- name: validate-registries
match:
resources:
kinds:
- Pod
validate:
message: "Images must come from approved registries"
pattern:
spec:
containers:
- image: "registry.company.com/* | gcr.io/company/*"
Image Scanning
Integrate scanning in CI/CD:
# Trivy scan
- name: Scan image
run: trivy image --severity HIGH,CRITICAL --exit-code 1 $IMAGE
Signed Images
# Sigstore/cosign verification
apiVersion: kyverno.io/v1
kind: ClusterPolicy
spec:
rules:
- name: verify-signature
verifyImages:
- image: "*"
key: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
Audit and Monitoring
Audit Policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: Metadata
resources:
- group: ""
resources: ["pods", "services"]
- level: None
resources:
- group: ""
resources: ["endpoints", "events"]
Runtime Security
Deploy Falco for runtime detection:
- rule: Shell Spawned in Container
desc: Detect shell spawned in container
condition: spawned_process and container and shell_procs
output: Shell spawned (user=%user.name container=%container.name)
priority: WARNING
Hardening Checklist
Cluster Level
- API server anonymous auth disabled
- Audit logging enabled
- etcd encrypted at rest
- etcd TLS enabled
- Kubelet anonymous auth disabled
- NodeRestriction admission enabled
RBAC
- No cluster-admin for regular users
- Namespace-scoped roles preferred
- Service accounts per workload
- Default SA token not auto-mounted
Pod Security
- Pods run as non-root
- Read-only root filesystem
- Capabilities dropped
- Privilege escalation disabled
- Resource limits set
Network
- Default deny network policies
- Explicit allow policies
- mTLS between services
Secrets
- Secrets encrypted at rest
- External secrets manager
- Secrets mounted as files
Images
- Trusted registries only
- Images scanned for vulnerabilities
- Images signed and verified
Monitoring
- Audit logs collected
- Runtime security monitoring
- Alert on security events
Key Takeaways
- Default Kubernetes is not secure; hardening is required
- Enable audit logging and etcd encryption
- Implement least-privilege RBAC; avoid cluster-admin
- Enforce Pod Security Standards (restricted profile)
- Implement network policies with default-deny
- Use external secrets management; encrypt at rest
- Allow only trusted registries; scan and sign images
- Deploy runtime security monitoring (Falco)
Security is layers. Each control adds defense depth.