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.

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?

R

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.

🐧 Stay Updated with Linux Tips

Get the latest tutorials, news, and guides delivered to your inbox weekly.

Add Comment