Cloud Native Security

Container Security

Docker & Kubernetes Hardening Guide

18 min read

Table of Contents
  1. Docker Security Best Practices
  2. Secure Image Building
  3. Kubernetes Security
  4. RBAC Configuration
  5. Network Policies
  6. Runtime Security

Docker Security Best Practices

1. Don't Run as Root

❌ Bad Practice
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nginx
# Running as root by default!
✅ Good Practice
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nginx
RUN useradd -r -s /bin/false appuser
USER appuser

2. Use Minimal Base Images

# Instead of ubuntu (77MB), use alpine (5MB)
FROM alpine:3.18

# Or distroless for production
FROM gcr.io/distroless/python3-debian11

Secure Image Building

Complete Secure Dockerfile

# Multi-stage build for minimal final image
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Final minimal image
FROM gcr.io/distroless/nodejs18-debian11
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

# Non-root user
USER nonroot:nonroot

# Health check
HEALTHCHECK --interval=30s CMD ["node", "healthcheck.js"]

EXPOSE 3000
CMD ["dist/server.js"]

Image Scanning

# Scan with Trivy
trivy image myapp:latest

# Scan with Docker Scout
docker scout cves myapp:latest

# Scan with Grype
grype myapp:latest

Kubernetes Security

Pod Security Standards

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Secure Pod Specification

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
      requests:
        memory: "64Mi"
        cpu: "250m"

RBAC Configuration

# Read-only Role for developers
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: developer-readonly
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]

---
# Bind role to group
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-binding
  namespace: production
subjects:
- kind: Group
  name: developers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer-readonly
  apiGroup: rbac.authorization.k8s.io

Network Policies

# Default deny all ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

---
# Allow specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - port: 8080

Runtime Security

Tools for Runtime Protection

FalcoRuntime threat detection
SysdigContainer monitoring
CiliumeBPF-based security
OPA/GatekeeperPolicy enforcement

Falco Rule Example

- rule: Shell Spawned in Container
  desc: Detect shell being spawned in container
  condition: >
    spawned_process and container and
    shell_procs and proc.pname exists and
    not proc.pname in (shell_binaries)
  output: >
    Shell spawned in container
    (user=%user.name container=%container.name
    shell=%proc.name parent=%proc.pname)
  priority: WARNING

Updated: December 2024