Skip to content
CSY302 Week 06 Advanced

Secure containers and Kubernetes from image build to production deployment.

Cloud & Infrastructure Security

Track your progress through this week's content

Opening Framing

Containers have revolutionized application deployment with their portability, efficiency, and speed. Kubernetes has become the de facto standard for orchestrating containers at scale. But the speed and convenience that makes containers attractive also creates security challenges: images pulled from public registries may contain vulnerabilities, misconfigured containers can expose sensitive data, and Kubernetes itself presents a complex attack surface.

Container security spans the entire lifecycle: securing the build pipeline, hardening base images, scanning for vulnerabilities, configuring runtime protections, and monitoring deployed workloads. Kubernetes adds another layer of complexity with its API server, RBAC system, network policies, and the shared responsibility between managed services and customer configurations.

This week covers container image security, Docker hardening, Kubernetes architecture and security controls, managed Kubernetes services (EKS/AKS/GKE), and runtime security. You'll learn to secure containerized workloads from build to production.

Key insight: The ephemeral nature of containers is a security advantage—if you build security into the pipeline, every new container starts secure.

1) Container Security Fundamentals

Understanding container architecture is essential for securing containerized workloads:

Container Architecture:

CONTAINERS VS VIRTUAL MACHINES:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  VIRTUAL MACHINES              CONTAINERS                   │
│  ┌─────┐ ┌─────┐ ┌─────┐      ┌─────┐ ┌─────┐ ┌─────┐       │
│  │App 1│ │App 2│ │App 3│      │App 1│ │App 2│ │App 3│       │
│  ├─────┤ ├─────┤ ├─────┤      └──┬──┘ └──┬──┘ └──┬──┘       │
│  │Guest│ │Guest│ │Guest│         │       │       │          │
│  │ OS  │ │ OS  │ │ OS  │      ┌──┴───────┴───────┴──┐       │
│  └──┬──┘ └──┬──┘ └──┬──┘      │   Container Runtime │       │
│     │       │       │         │      (Docker)       │       │
│  ┌──┴───────┴───────┴──┐      └──────────┬──────────┘       │
│  │     Hypervisor      │                 │                  │
│  └──────────┬──────────┘      ┌──────────┴──────────┐       │
│             │                 │     Host OS         │       │
│  ┌──────────┴──────────┐      └──────────┬──────────┘       │
│  │     Host OS         │                 │                  │
│  └──────────┬──────────┘      ┌──────────┴──────────┐       │
│  │     Hardware        │      │     Hardware        │       │
│  └─────────────────────┘      └─────────────────────┘       │
│                                                             │
│  VMs: Hardware virtualization   Containers: OS-level        │
│  Full OS per VM                 Shared kernel               │
│  Stronger isolation             Lighter weight              │
└─────────────────────────────────────────────────────────────┘

CONTAINER ISOLATION MECHANISMS:
┌─────────────────────────────────────────────────────────────┐
│ Namespaces (What container can see):                        │
│ - PID: Process isolation                                    │
│ - Network: Network stack isolation                          │
│ - Mount: Filesystem isolation                               │
│ - User: User ID mapping                                     │
│ - UTS: Hostname isolation                                   │
│ - IPC: Inter-process communication isolation                │
│                                                             │
│ Cgroups (What container can use):                           │
│ - CPU limits                                                │
│ - Memory limits                                             │
│ - I/O limits                                                │
│ - Process limits                                            │
│                                                             │
│ Seccomp (What syscalls container can make):                 │
│ - Restricts available system calls                          │
│ - Default Docker profile blocks ~44 dangerous syscalls      │
│                                                             │
│ Capabilities (What privileges container has):               │
│ - Fine-grained root privileges                              │
│ - Docker drops many by default                              │
│ - Further restrict with --cap-drop                          │
└─────────────────────────────────────────────────────────────┘

Container Security Risks:

Container Attack Surface:

IMAGE RISKS:
┌─────────────────────────────────────────────────────────────┐
│ - Vulnerable base images                                    │
│ - Outdated packages                                         │
│ - Malware in public images                                  │
│ - Secrets embedded in images                                │
│ - Excessive packages (attack surface)                       │
│                                                             │
│ Example: Official Python image had 400+ CVEs                │
│ Alpine-based: ~10 CVEs (minimal attack surface)             │
└─────────────────────────────────────────────────────────────┘

CONFIGURATION RISKS:
┌─────────────────────────────────────────────────────────────┐
│ - Running as root                                           │
│ - Privileged containers                                     │
│ - Excessive capabilities                                    │
│ - Host filesystem mounts                                    │
│ - Host network mode                                         │
│ - Disabled security features                                │
│                                                             │
│ Example: --privileged = full host access                    │
└─────────────────────────────────────────────────────────────┘

RUNTIME RISKS:
┌─────────────────────────────────────────────────────────────┐
│ - Container escape vulnerabilities                          │
│ - Kernel exploits (shared kernel)                           │
│ - Resource exhaustion attacks                               │
│ - Network-based attacks between containers                  │
│ - Cryptomining                                              │
│                                                             │
│ Example: CVE-2019-5736 runc escape vulnerability            │
└─────────────────────────────────────────────────────────────┘

ORCHESTRATION RISKS:
┌─────────────────────────────────────────────────────────────┐
│ - Exposed Kubernetes API                                    │
│ - Overly permissive RBAC                                    │
│ - Missing network policies                                  │
│ - Secrets in plain text                                     │
│ - Insecure service accounts                                 │
│                                                             │
│ Example: Tesla cryptomining via exposed K8s dashboard       │
└─────────────────────────────────────────────────────────────┘

Key insight: Containers share the host kernel. A kernel vulnerability affects all containers on the host.

2) Container Image Security

Secure containers start with secure images—vulnerabilities in images become vulnerabilities in every deployment:

Secure Image Building:

DOCKERFILE SECURITY BEST PRACTICES:

# BAD: Using latest tag, running as root
FROM python:latest
COPY . /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

# GOOD: Pinned version, minimal base, non-root user
FROM python:3.11-alpine@sha256:abc123...
WORKDIR /app

# Install dependencies first (better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Create non-root user
RUN adduser -D appuser
USER appuser

# Copy application code
COPY --chown=appuser:appuser . .

# Use exec form for signals
CMD ["python", "app.py"]

KEY PRINCIPLES:
┌─────────────────────────────────────────────────────────────┐
│ 1. Use minimal base images                                  │
│    - Alpine, distroless, scratch                            │
│    - Fewer packages = fewer vulnerabilities                 │
│                                                             │
│ 2. Pin image versions with digest                           │
│    - Never use :latest in production                        │
│    - SHA256 digest for reproducibility                      │
│                                                             │
│ 3. Run as non-root user                                     │
│    - Create dedicated user                                  │
│    - Use USER directive                                     │
│                                                             │
│ 4. Multi-stage builds                                       │
│    - Build in one stage, run in another                     │
│    - No build tools in production image                     │
│                                                             │
│ 5. Don't store secrets in images                            │
│    - No passwords, API keys, tokens                         │
│    - Use secrets management at runtime                      │
│                                                             │
│ 6. Minimize layers                                          │
│    - Combine RUN commands                                   │
│    - Clean up in same layer                                 │
└─────────────────────────────────────────────────────────────┘

Multi-Stage Build Example:

Multi-Stage Build (Go application):

# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

# Production stage
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /build/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]

Benefits:
┌─────────────────────────────────────────────────────────────┐
│ - Build tools not in final image                            │
│ - Dramatically smaller image size                           │
│ - Reduced attack surface                                    │
│ - No shell in distroless (harder to exploit)                │
│                                                             │
│ Size comparison:                                            │
│ golang:1.21       ~800MB                                    │
│ golang:1.21-alpine ~250MB                                   │
│ distroless/static  ~2MB                                     │
└─────────────────────────────────────────────────────────────┘

Image Scanning:

Container Image Scanning:

SCANNING TOOLS:
┌─────────────────────────────────────────────────────────────┐
│ Trivy (Open Source):                                        │
│ - Fast, comprehensive scanning                              │
│ - OS packages, language packages, IaC                       │
│ - CI/CD integration                                         │
│                                                             │
│ trivy image myapp:latest                                    │
│ trivy image --severity HIGH,CRITICAL myapp:latest           │
│ trivy image --exit-code 1 --severity CRITICAL myapp:latest  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Amazon ECR Scanning:                                        │
│ - Basic scanning (Clair-based)                              │
│ - Enhanced scanning (Inspector integration)                 │
│ - Scan on push                                              │
│ - Continuous monitoring                                     │
│                                                             │
│ aws ecr put-image-scanning-configuration \                  │
│   --repository-name myrepo \                                │
│   --image-scanning-configuration scanOnPush=true            │
└─────────────────────────────────────────────────────────────┘

CI/CD INTEGRATION (GitHub Actions):
name: Build and Scan
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'
      
      - name: Push to ECR (if scan passes)
        run: |
          aws ecr get-login-password | docker login ...
          docker push $ECR_REPO:${{ github.sha }}

SCANNING POLICY:
┌─────────────────────────────────────────────────────────────┐
│ Gate Criteria:                                              │
│ - Critical vulnerabilities: 0 (block deployment)            │
│ - High vulnerabilities: < 3 (warn, require approval)        │
│ - Base image age: < 30 days                                 │
│ - No secrets detected                                       │
│ - No root user                                              │
│                                                             │
│ Continuous Monitoring:                                      │
│ - Rescan when CVE database updates                          │
│ - Alert on newly discovered vulnerabilities                 │
│ - Track vulnerability trends over time                      │
└─────────────────────────────────────────────────────────────┘

Key insight: Scan images in CI/CD before deployment AND continuously in production—new CVEs are discovered daily.

3) Container Runtime Security

Secure runtime configuration limits what containers can do if compromised:

Docker Security Configuration:

RUNNING CONTAINERS SECURELY:

# Bad: Running as privileged
docker run --privileged myapp

# Good: Minimal privileges
docker run \
  --user 1000:1000 \
  --read-only \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges:true \
  --security-opt seccomp=default.json \
  --memory 512m \
  --cpus 1 \
  --pids-limit 100 \
  myapp

SECURITY OPTIONS EXPLAINED:
┌─────────────────────────────────────────────────────────────┐
│ --user 1000:1000                                            │
│   Run as non-root user                                      │
│                                                             │
│ --read-only                                                 │
│   Read-only root filesystem                                 │
│   (use volumes for writable data)                           │
│                                                             │
│ --cap-drop ALL                                              │
│   Remove all Linux capabilities                             │
│                                                             │
│ --cap-add NET_BIND_SERVICE                                  │
│   Add back only what's needed                               │
│                                                             │
│ --security-opt no-new-privileges:true                       │
│   Prevent privilege escalation                              │
│                                                             │
│ --security-opt seccomp=default.json                         │
│   Apply seccomp profile (restrict syscalls)                 │
│                                                             │
│ --memory 512m / --cpus 1                                    │
│   Resource limits (prevent DoS)                             │
│                                                             │
│ --pids-limit 100                                            │
│   Limit processes (prevent fork bombs)                      │
└─────────────────────────────────────────────────────────────┘

Docker Daemon Security:

Docker Daemon Hardening:

DAEMON.JSON CONFIGURATION:
{
  "icc": false,
  "userns-remap": "default",
  "no-new-privileges": true,
  "seccomp-profile": "/etc/docker/seccomp.json",
  "live-restore": true,
  "userland-proxy": false,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2"
}

OPTIONS EXPLAINED:
┌─────────────────────────────────────────────────────────────┐
│ "icc": false                                                │
│   Disable inter-container communication by default          │
│   Containers must be explicitly linked                      │
│                                                             │
│ "userns-remap": "default"                                   │
│   Enable user namespaces                                    │
│   Container root maps to unprivileged host user             │
│                                                             │
│ "no-new-privileges": true                                   │
│   Global setting to prevent privilege escalation            │
│                                                             │
│ "live-restore": true                                        │
│   Keep containers running during daemon restart             │
│                                                             │
│ "userland-proxy": false                                     │
│   Use iptables instead of docker-proxy                      │
│   Better performance and security                           │
└─────────────────────────────────────────────────────────────┘

DOCKER SOCKET SECURITY:
┌─────────────────────────────────────────────────────────────┐
│ /var/run/docker.sock = root on the host                     │
│                                                             │
│ NEVER mount docker.sock into containers unless absolutely   │
│ necessary (CI/CD builders, monitoring)                      │
│                                                             │
│ If required:                                                │
│ - Use read-only mount                                       │
│ - Restrict to specific containers                           │
│ - Consider docker-socket-proxy                              │
│ - Audit all socket access                                   │
│                                                             │
│ Example attack:                                             │
│ docker run -v /var/run/docker.sock:/var/run/docker.sock \   │
│   attacker-image docker run --privileged -v /:/host ...     │
└─────────────────────────────────────────────────────────────┘

Container Registry Security:

Registry Security:

AMAZON ECR SECURITY:
┌─────────────────────────────────────────────────────────────┐
│ Access Control:                                             │
│ - IAM policies for push/pull                                │
│ - Repository policies for cross-account                     │
│ - No anonymous access (by default)                          │
│                                                             │
│ Image Security:                                             │
│ - Image scanning (basic or enhanced)                        │
│ - Image signing (coming: ECR signing)                       │
│ - Immutable tags (prevent overwriting)                      │
│                                                             │
│ Enable Immutable Tags:                                      │
│ aws ecr put-image-tag-mutability \                          │
│   --repository-name myrepo \                                │
│   --image-tag-mutability IMMUTABLE                          │
└─────────────────────────────────────────────────────────────┘

REPOSITORY POLICY EXAMPLE:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPushPull",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/EKSNodeRole"
      },
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ]
    },
    {
      "Sid": "DenyUnscannedImages",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "ecr:BatchGetImage",
      "Condition": {
        "StringEquals": {
          "ecr:ImageScanStatus": "FAILED"
        }
      }
    }
  ]
}

IMAGE SIGNING WITH COSIGN:
┌─────────────────────────────────────────────────────────────┐
│ # Sign image                                                │
│ cosign sign --key cosign.key myregistry/myapp:v1.0          │
│                                                             │
│ # Verify signature                                          │
│ cosign verify --key cosign.pub myregistry/myapp:v1.0        │
│                                                             │
│ Benefits:                                                   │
│ - Proves image hasn't been tampered                         │
│ - Verify images came from trusted pipeline                  │
│ - Can enforce signed images in Kubernetes                   │
└─────────────────────────────────────────────────────────────┘

Key insight: The registry is a critical control point. Only allow deployment of scanned, signed images from trusted sources.

4) Kubernetes Security Architecture

Kubernetes adds powerful orchestration capabilities but also significant attack surface that must be secured:

Kubernetes Architecture:

COMPONENTS AND SECURITY:
┌─────────────────────────────────────────────────────────────┐
│                     CONTROL PLANE                           │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                                                     │    │
│  │  ┌────────────┐  ┌────────────┐  ┌──────────────┐   │    │
│  │  │ API Server │  │ Controller │  │  Scheduler   │   │    │
│  │  │            │  │  Manager   │  │              │   │    │
│  │  │ - AuthN    │  │            │  │              │   │    │
│  │  │ - AuthZ    │  │            │  │              │   │    │
│  │  │ - Admission│  │            │  │              │   │    │
│  │  └────────────┘  └────────────┘  └──────────────┘   │    │
│  │                                                     │    │
│  │  ┌──────────────────────────────────────────────┐   │    │
│  │  │                   etcd                       │   │    │
│  │  │         (encrypted secrets storage)          │   │    │
│  │  └──────────────────────────────────────────────┘   │    │
│  └─────────────────────────────────────────────────────┘    │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    WORKER NODES                     │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐           │    │
│  │  │  kubelet │  │kube-proxy│  │Container │           │    │
│  │  │          │  │          │  │ Runtime  │           │    │
│  │  └──────────┘  └──────────┘  └──────────┘           │    │
│  │                                                     │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │              Pods / Containers              │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

KEY SECURITY CONTROLS:
┌─────────────────────────────────────────────────────────────┐
│ Authentication (Who are you?):                              │
│ - Service accounts for pods                                 │
│ - OIDC for users (AWS IAM, Azure AD, etc.)                  │
│ - No anonymous access                                       │
│                                                             │
│ Authorization (What can you do?):                           │
│ - RBAC (Role-Based Access Control)                          │
│ - Principle of least privilege                              │
│                                                             │
│ Admission Control (What's allowed in?):                     │
│ - Validating/Mutating webhooks                              │
│ - Pod Security Standards                                    │
│ - Policy engines (OPA/Gatekeeper, Kyverno)                  │
│                                                             │
│ Network Security:                                           │
│ - Network policies                                          │
│ - Service mesh (mTLS)                                       │
│                                                             │
│ Secrets Management:                                         │
│ - Encrypted secrets in etcd                                 │
│ - External secrets (AWS Secrets Manager, Vault)             │
└─────────────────────────────────────────────────────────────┘

Kubernetes RBAC:

RBAC Configuration:

RBAC CONCEPTS:
┌─────────────────────────────────────────────────────────────┐
│ Role / ClusterRole:                                         │
│   Define WHAT actions are allowed                           │
│   Role = namespace-scoped                                   │
│   ClusterRole = cluster-wide                                │
│                                                             │
│ RoleBinding / ClusterRoleBinding:                           │
│   Bind roles to WHO (users, groups, service accounts)       │
└─────────────────────────────────────────────────────────────┘

LEAST PRIVILEGE ROLE EXAMPLE:
# Role for application deployment
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: deployment-manager
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]

---
# Bind to service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: deployment-manager-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: cicd-deployer
  namespace: production
roleRef:
  kind: Role
  name: deployment-manager
  apiGroup: rbac.authorization.k8s.io

DANGEROUS PERMISSIONS TO AVOID:
┌─────────────────────────────────────────────────────────────┐
│ High Risk - Equivalent to cluster-admin:                    │
│ - * on * (all verbs on all resources)                       │
│ - create pods (can run privileged containers)               │
│ - create/patch deployments, daemonsets, etc.                │
│                                                             │
│ Privilege Escalation Paths:                                 │
│ - pods/exec (execute into pods)                             │
│ - secrets (read all secrets)                                │
│ - create serviceaccounts + rolebindings                     │
│ - create pods with service account                          │
│                                                             │
│ Audit Question: "Can this role gain more access?"           │
└─────────────────────────────────────────────────────────────┘

Pod Security:

Pod Security Standards:

POD SECURITY ADMISSION (PSA):
┌─────────────────────────────────────────────────────────────┐
│ Built-in Kubernetes admission controller                    │
│ Enforces Pod Security Standards                             │
│ Three profiles: Privileged, Baseline, Restricted            │
│                                                             │
│ Enable on namespace:                                        │
│ kubectl label namespace production \                        │
│   pod-security.kubernetes.io/enforce=restricted \           │
│   pod-security.kubernetes.io/audit=restricted \             │
│   pod-security.kubernetes.io/warn=restricted                │
└─────────────────────────────────────────────────────────────┘

SECURITY PROFILES:
┌─────────────────────────────────────────────────────────────┐
│ PRIVILEGED: No restrictions (system workloads)              │
│                                                             │
│ BASELINE: Minimally restrictive                             │
│ - No privileged containers                                  │
│ - No hostNetwork, hostPID, hostIPC                          │
│ - No hostPath volumes                                       │
│ - Restricted capabilities                                   │
│                                                             │
│ RESTRICTED: Heavily restricted (best practice)              │
│ - Must run as non-root                                      │
│ - Must drop ALL capabilities                                │
│ - No privilege escalation                                   │
│ - Seccomp profile required                                  │
│ - Read-only root filesystem                                 │
└─────────────────────────────────────────────────────────────┘

SECURE POD SPECIFICATION:
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:v1.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
      requests:
        memory: "64Mi"
        cpu: "250m"
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}
  automountServiceAccountToken: false

Key insight: Pod Security Standards replaced PodSecurityPolicy. Enforce "restricted" profile for all production workloads.

5) Managed Kubernetes and Network Policies

Managed Kubernetes services reduce operational burden but still require security configuration:

Amazon EKS Security:

EKS ARCHITECTURE:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  AWS MANAGED                      CUSTOMER MANAGED          │
│  ┌─────────────────────┐         ┌─────────────────────┐    │
│  │   Control Plane     │         │    Worker Nodes     │    │
│  │   ┌─────────────┐   │         │   ┌─────────────┐   │    │
│  │   │ API Server  │   │◄───────►│   │   kubelet   │   │    │
│  │   │ etcd        │   │         │   │   Pods      │   │    │
│  │   │ Controllers │   │         │   │   Runtime   │   │    │
│  │   └─────────────┘   │         │   └─────────────┘   │    │
│  └─────────────────────┘         └─────────────────────┘    │
│                                                             │
│  AWS Responsibility:              Your Responsibility:      │
│  - Control plane availability     - Node security           │
│  - Control plane patching         - Pod security            │
│  - etcd encryption               - Network policies         │
│  - API server security           - IAM roles for pods       │
│                                  - Application security     │
└─────────────────────────────────────────────────────────────┘

EKS SECURITY CONFIGURATION:
┌─────────────────────────────────────────────────────────────┐
│ Cluster Security:                                           │
│ - Private endpoint access                                   │
│ - Envelope encryption for secrets                           │
│ - Control plane logging                                     │
│                                                             │
│ aws eks create-cluster \                                    │
│   --name my-cluster \                                       │
│   --resources-vpc-config \                                  │
│     endpointPrivateAccess=true,endpointPublicAccess=false \ │
│   --encryption-config \                                     │
│     '[{"resources":["secrets"],"provider":{"keyArn":"..."}}]'│
└─────────────────────────────────────────────────────────────┘

IAM ROLES FOR SERVICE ACCOUNTS (IRSA):
┌─────────────────────────────────────────────────────────────┐
│ Pod-level IAM without node-level credentials                │
│                                                             │
│ 1. Create IAM role with trust policy:                       │
│    {                                                        │
│      "Principal": {                                         │
│        "Federated": "arn:aws:iam::...:oidc-provider/..."    │
│      },                                                     │
│      "Condition": {                                         │
│        "StringEquals": {                                    │
│          "...:sub": "system:serviceaccount:ns:sa-name"      │
│        }                                                    │
│      }                                                      │
│    }                                                        │
│                                                             │
│ 2. Annotate service account:                                │
│    metadata:                                                │
│      annotations:                                           │
│        eks.amazonaws.com/role-arn: arn:aws:iam::...:role/...│
│                                                             │
│ 3. Pod assumes role via OIDC, not node credentials          │
└─────────────────────────────────────────────────────────────┘

Network Policies:

Kubernetes Network Policies:

DEFAULT: NO NETWORK POLICIES = ALL PODS CAN COMMUNICATE

WITH NETWORK POLICIES:
┌─────────────────────────────────────────────────────────────┐
│ Control traffic flow between pods and external endpoints    │
│ Requires CNI that supports NetworkPolicy (Calico, Cilium)   │
└─────────────────────────────────────────────────────────────┘

DEFAULT DENY ALL:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}  # Apply to all pods
  policyTypes:
  - Ingress
  - Egress

ALLOW SPECIFIC TRAFFIC:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

ALLOW EGRESS TO SPECIFIC SERVICES:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Egress
  egress:
  # Allow DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
  # Allow database
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  # Allow AWS services via VPC endpoints
  - to:
    - ipBlock:
        cidr: 10.0.0.0/16
    ports:
    - protocol: TCP
      port: 443

NETWORK POLICY STRATEGY:
┌─────────────────────────────────────────────────────────────┐
│ 1. Start with default deny in each namespace                │
│ 2. Explicitly allow required communication                  │
│ 3. Use labels consistently for policy targeting             │
│ 4. Allow DNS (or nothing works)                             │
│ 5. Test policies before enforcing                           │
│ 6. Monitor denied traffic                                   │
└─────────────────────────────────────────────────────────────┘

Key insight: Network policies are namespaced. A default-deny policy must be created in each namespace you want to protect.

Real-World Context

Case Study: Tesla Kubernetes Cryptomining

In 2018, attackers compromised Tesla's Kubernetes cluster via an unsecured Kubernetes dashboard exposed to the internet without authentication. Once inside, they deployed cryptomining containers using Tesla's cloud resources. The attackers used sophisticated evasion techniques: mining software connected to an unlisted mining pool, CPU usage was throttled to avoid detection, and traffic was hidden behind CloudFlare. This incident highlighted multiple security failures: exposed dashboard, missing authentication, no network policies limiting egress, and insufficient monitoring for anomalous resource usage.

Case Study: Vulnerable Container Image Supply Chain

Researchers found that 51% of images in Docker Hub contained at least one critical vulnerability. Popular base images like python:latest contained hundreds of CVEs. Organizations pulling these images directly into production inherit all vulnerabilities. The solution: use minimal base images (Alpine, distroless), maintain your own vetted base images, scan all images before deployment, and continuously monitor for new CVEs affecting deployed images.

Container/Kubernetes Security Checklist:

Container Security Checklist:

IMAGE SECURITY:
□ Use minimal base images (Alpine, distroless)
□ Pin image versions with SHA256 digest
□ Scan images in CI/CD pipeline
□ Block deployment of images with critical CVEs
□ Sign images and verify signatures
□ Use private registry with access controls
□ Enable image scanning in registry

CONTAINER RUNTIME:
□ Run containers as non-root
□ Drop all capabilities, add only needed
□ Enable read-only root filesystem
□ Set resource limits (CPU, memory, PIDs)
□ Enable seccomp profile
□ Never run privileged containers
□ Never mount Docker socket

KUBERNETES SECURITY:
□ Enable RBAC with least privilege
□ Use namespaces for isolation
□ Enforce Pod Security Standards (restricted)
□ Enable network policies (default deny)
□ Encrypt secrets in etcd
□ Use IAM roles for service accounts
□ Disable automount of service account tokens
□ Keep Kubernetes version updated

CLUSTER ACCESS:
□ Private API endpoint (no public access)
□ OIDC authentication for users
□ No direct kubectl for developers (use CI/CD)
□ Audit logging enabled
□ Regular RBAC reviews

MONITORING:
□ Container runtime security (Falco)
□ Image vulnerability monitoring
□ Network policy logging
□ API audit logs
□ Resource usage anomaly detection

Container security requires attention throughout the lifecycle: build, ship, and run. Each phase has unique security requirements.

Guided Lab: Secure Kubernetes Deployment

In this lab, you'll deploy a secure application to Kubernetes with proper security controls.

Lab Environment:

  • Kubernetes cluster (EKS, Minikube, or Kind)
  • kubectl configured
  • Docker for image building
  • Trivy for scanning

Exercise Steps:

  1. Create secure Dockerfile with non-root user
  2. Build and scan image with Trivy
  3. Push to ECR with scanning enabled
  4. Create namespace with Pod Security Standards
  5. Create service account with IRSA
  6. Deploy pod with security context
  7. Apply network policies
  8. Test security controls
  9. Verify pod cannot escalate privileges

Reflection Questions:

  • What happens when you try to deploy a privileged pod?
  • How do network policies change application behavior?
  • What's the difference between EKS node IAM and IRSA?

Week Outcome Check

By the end of this week, you should be able to:

  • Explain container isolation mechanisms and their limitations
  • Build secure container images with minimal attack surface
  • Implement image scanning in CI/CD pipelines
  • Configure container runtime security controls
  • Design Kubernetes RBAC with least privilege
  • Apply Pod Security Standards to namespaces
  • Create network policies for pod-to-pod communication
  • Configure managed Kubernetes security (EKS)

🎯 Hands-On Labs (Free & Essential)

Secure containers and Kubernetes from image build to production deployment.

🐋 TryHackMe: Docker Security

What you'll do: Exploit and secure Docker containers—escape attempts, privileged containers, and image vulnerabilities.
Why it matters: Misconfigured containers can provide host-level access to attackers.
Time estimate: 2-3 hours

Start Docker Security Lab →

☸️ Kubernetes Goat: K8s Security Practice

What you'll do: Attack and defend intentionally vulnerable Kubernetes clusters—learn real attack vectors.
Why it matters: Kubernetes security is complex—hands-on exploitation teaches defense.
Time estimate: 3-4 hours

Open Kubernetes Goat →

🧪 HackTheBox: Container Escape Techniques

What you'll do: Practice container breakout techniques and learn to prevent them (free tier challenges).
Why it matters: Container escapes turn application compromises into infrastructure compromises.
Time estimate: 2-3 hours

Open HTB Container Module →

💡 Lab Strategy: Kubernetes Goat is essential—it simulates real cluster vulnerabilities you'll encounter in production.

Resources

Lab

Complete the following lab exercises to practice container and Kubernetes security.

Part 1: Secure Dockerfile (LO5)

Create a secure Dockerfile: (a) use minimal base image, (b) multi-stage build, (c) non-root user, (d) no secrets, (e) scan with Trivy and fix issues.

Deliverable: Dockerfile and Trivy scan report showing 0 critical/high vulnerabilities.

Part 2: Container Runtime Security (LO5)

Configure secure container runtime: (a) run without privileges, (b) drop capabilities, (c) read-only filesystem, (d) resource limits, (e) test that security controls work.

Deliverable: Docker run command with security options and test results.

Part 3: Kubernetes RBAC (LO5)

Configure RBAC for an application: (a) create service account, (b) create least-privilege role, (c) bind role to service account, (d) verify permissions work as expected.

Deliverable: RBAC YAML manifests and permission test results.

Part 4: Pod Security (LO5)

Enforce pod security: (a) apply restricted PSS to namespace, (b) create secure pod specification, (c) attempt to deploy insecure pod and verify rejection, (d) deploy secure pod successfully.

Deliverable: Namespace labels, pod YAML, and evidence of policy enforcement.

Part 5: Network Policies (LO5)

Implement network segmentation: (a) create default-deny policy, (b) allow specific pod-to-pod communication, (c) allow egress to specific services, (d) test that unauthorized traffic is blocked.

Deliverable: NetworkPolicy YAMLs and network connectivity test results.

Checkpoint Questions

  1. How do containers provide isolation? What are namespaces, cgroups, and capabilities?
  2. What makes a container image secure? List five Dockerfile best practices for security.
  3. Why is running containers as root dangerous? How do you run as non-root in Docker and Kubernetes?
  4. Explain Kubernetes RBAC. What is the difference between Role and ClusterRole?
  5. What are Pod Security Standards? Compare Privileged, Baseline, and Restricted profiles.
  6. How do network policies work in Kubernetes? Why must you start with default-deny?

Week 06 Quiz

Test your understanding of Container and Kubernetes Security.

Format: 10 multiple-choice questions. Passing score: 70%. Time: Untimed.

Take Quiz

Weekly Reflection

Containers and Kubernetes provide powerful capabilities but require deliberate security configuration. This week explored securing containerized workloads from build to runtime.

Reflect on the following in 200-300 words:

A strong reflection demonstrates understanding of container security as a lifecycle concern requiring controls at build, ship, and run phases.

Verified Resources & Videos

← Previous: Week 05 Next: Week 07 →