Hands-on Lab

#Lab: Docker Security Best Practices

Implement security best practices for containers.

#🎯 Objectives

  • Run containers as non-root
  • Scan images for vulnerabilities
  • Implement resource limits
  • Use read-only filesystems

#📋 Prerequisites

  • Docker installed

#⏱️ Duration: 30 minutes


#Task 1: Non-Root Containers (10 min)

#Check Current User

bash
1# Default: runs as root
2docker run --rm alpine whoami
3# Output: root
4
5docker run --rm alpine id
6# Output: uid=0(root) gid=0(root)

#Run as Specific User

bash
1# Run as nobody user
2docker run --rm --user nobody alpine whoami
3
4# Run with specific UID/GID
5docker run --rm --user 1000:1000 alpine id
6
7# Create Dockerfile with non-root user
8cat << 'EOF' > Dockerfile.secure
9FROM alpine:3.18
10RUN addgroup -g 1001 appgroup && \
11    adduser -u 1001 -G appgroup -D appuser
12USER appuser
13CMD ["whoami"]
14EOF
15
16docker build -t secure-app -f Dockerfile.secure .
17docker run --rm secure-app

#Task 2: Resource Limits (5 min)

bash
1# Limit memory
2docker run -d --memory="128m" --name mem-limited nginx
3
4# Limit CPU
5docker run -d --cpus="0.5" --name cpu-limited nginx
6
7# Combined limits
8docker run -d \
9  --memory="256m" \
10  --memory-swap="256m" \
11  --cpus="1" \
12  --name limited nginx
13
14# Check resource usage
15docker stats --no-stream

#Task 3: Read-Only Filesystem (5 min)

bash
1# Read-only root filesystem
2docker run -d \
3  --read-only \
4  --tmpfs /tmp \
5  --tmpfs /var/cache/nginx \
6  --tmpfs /var/run \
7  --name readonly-nginx nginx
8
9# Verify it's read-only
10docker exec readonly-nginx touch /test 2>&1 | head -1
11# Output: touch: /test: Read-only file system
12
13# But /tmp is writable
14docker exec readonly-nginx touch /tmp/test
15docker exec readonly-nginx ls /tmp/test

#Task 4: Drop Capabilities (5 min)

bash
1# Drop all capabilities except needed ones
2docker run -d \
3  --cap-drop=ALL \
4  --cap-add=NET_BIND_SERVICE \
5  --name minimal-caps nginx
6
7# Check capabilities
8docker exec minimal-caps cat /proc/1/status | grep Cap
9
10# Comparison: full capabilities
11docker run --rm alpine cat /proc/1/status | grep Cap

#Task 5: Image Scanning (5 min)

bash
1# Using Docker Scout (if available)
2docker scout cves nginx:latest
3
4# Using Trivy (install first: https://trivy.dev)
5# trivy image nginx:latest
6
7# Check for vulnerabilities in your image
8docker build -t myapp:test .
9# docker scout cves myapp:test

#Security Checklist

  • Containers run as non-root
  • Memory and CPU limits set
  • Read-only filesystem where possible
  • Minimal capabilities
  • Images scanned for vulnerabilities
  • Secrets not baked into images
  • Using specific image tags (not :latest)

#🧹 Cleanup

bash
1docker stop mem-limited cpu-limited limited readonly-nginx minimal-caps 2>/dev/null
2docker rm mem-limited cpu-limited limited readonly-nginx minimal-caps 2>/dev/null
3docker rmi secure-app 2>/dev/null
4rm -f Dockerfile.secure