Docker and Kubernetes Security Best Practices: Complete Hardening Guide
Introduction
Container security has become critical as organizations increasingly rely on Docker and Kubernetes for production workloads. This comprehensive guide covers essential security practices for hardening your container infrastructure in 2025.
📑 Table of Contents
- Introduction
- Docker Security Fundamentals
- 1. Use Minimal Base Images
- 2. Run as Non-Root User
- 3. Multi-Stage Builds
- 4. Image Scanning
- Docker Daemon Hardening
- 5. Enable Content Trust
- 6. Limit Container Resources
- 7. Secure Docker Socket
- Kubernetes Security
- 8. Pod Security Standards
- 9. Security Context Configuration
- 10. Network Policies
- 11. RBAC Configuration
- Secret Management
- 12. External Secrets Operator
- 13. Sealed Secrets
- Runtime Security
- 14. Falco for Runtime Detection
- 15. Image Admission Control
- Security Scanning Pipeline
- 16. CI/CD Integration
- Security Checklist
- Docker Checklist:
- Kubernetes Checklist:
- Conclusion
Docker Security Fundamentals
1. Use Minimal Base Images
Start with the smallest possible base image to reduce your attack surface.
# Bad: Full OS image
FROM ubuntu:22.04
# Better: Slim variant
FROM python:3.12-slim
# Best: Distroless image
FROM gcr.io/distroless/python3-debian12
# Or scratch for static binaries
FROM scratch
COPY myapp /myapp
ENTRYPOINT ["/myapp"]
2. Run as Non-Root User
FROM python:3.12-slim
# Create non-root user
RUN useradd -r -s /bin/false appuser
# Set working directory
WORKDIR /app
# Copy and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY --chown=appuser:appuser . .
# Switch to non-root user
USER appuser
CMD ["python", "app.py"]
3. Multi-Stage Builds
# Build stage
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Final stage - minimal image
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
RUN adduser -D -s /bin/false appuser
WORKDIR /root/
COPY --from=builder /app/main .
USER appuser
CMD ["./main"]
4. Image Scanning
# Scan with Trivy
trivy image myapp:latest
# Scan with specific severity
trivy image --severity CRITICAL,HIGH myapp:latest
# Scan with exit code for CI/CD
trivy image --exit-code 1 --severity CRITICAL myapp:latest
# Generate SBOM
trivy image --format spdx-json myapp:latest > sbom.json
Docker Daemon Hardening
5. Enable Content Trust
# Enable Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# Sign and push images
docker trust sign myregistry/myimage:tag
# Verify signatures
docker trust inspect myregistry/myimage:tag
6. Limit Container Resources
# Run with resource limits
docker run -d \
--memory="512m" \
--memory-swap="512m" \
--cpus="0.5" \
--pids-limit=100 \
--read-only \
--tmpfs /tmp \
myapp:latest
7. Secure Docker Socket
# Use TLS for remote Docker daemon
# Generate certificates
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# Configure daemon.json
{
"tls": true,
"tlscacert": "/etc/docker/ca.pem",
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem",
"tlsverify": true,
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"]
}
Kubernetes Security
8. Pod Security Standards
# Restricted Pod Security Standard
apiVersion: v1
kind: Namespace
metadata:
name: secure-app
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
9. Security Context Configuration
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
10. Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
11. RBAC Configuration
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: ServiceAccount
name: monitoring
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Secret Management
12. External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-password
remoteRef:
key: secret/data/production/database
property: password
13. Sealed Secrets
# Install kubeseal
brew install kubeseal
# Create sealed secret
kubectl create secret generic my-secret \
--from-literal=password=supersecret \
--dry-run=client -o yaml | \
kubeseal --format yaml > sealed-secret.yaml
# Apply sealed secret
kubectl apply -f sealed-secret.yaml
Runtime Security
14. Falco for Runtime Detection
# Install Falco
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--namespace falco --create-namespace \
--set driver.kind=ebpf
15. Image Admission Control
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: image-policy
webhooks:
- name: image-policy.example.com
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
clientConfig:
service:
name: image-policy-webhook
namespace: kube-system
path: "/validate"
Security Scanning Pipeline
16. CI/CD Integration
# .github/workflows/security.yml
name: Container Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:test .
- name: Run Trivy scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:test
exit-code: 1
severity: CRITICAL,HIGH
- name: Run Dockle
uses: goodwithtech/dockle-action@main
with:
image: myapp:test
exit-code: 1
Security Checklist
Docker Checklist:
- Use minimal base images (distroless, alpine, scratch)
- Run containers as non-root user
- Use multi-stage builds
- Scan images for vulnerabilities
- Enable Docker Content Trust
- Set resource limits
- Use read-only filesystem
- Keep Docker updated
Kubernetes Checklist:
- Enable Pod Security Standards
- Configure security contexts
- Implement network policies
- Use RBAC with least privilege
- Encrypt secrets at rest
- Enable audit logging
- Use admission controllers
- Implement runtime security monitoring
Conclusion
Container security requires a defense-in-depth approach. By implementing these best practices at build time, deploy time, and runtime, you can significantly reduce your attack surface and protect your containerized workloads.
Remember: security is an ongoing process. Regularly scan your images, update your dependencies, and stay informed about new vulnerabilities and security practices.
Was this article helpful?
About Ramesh Sundararamaiah
Red Hat Certified Architect
Expert in Linux system administration, DevOps automation, and cloud infrastructure. Specializing in Red Hat Enterprise Linux, CentOS, Ubuntu, Docker, Ansible, and enterprise IT solutions.