Best Practices for Container Security

Container security is not optional—every layer, from the image to runtime, needs to be hardened.

1. What You'll Learn


2. A Story About a Security Audit

(1) Pain Point: 28 security audit alerts

Charlie's team received a security audit report: 28 alerts included "Containers running as root" (high risk), "Image contains 15 high-risk CVEs" (high risk), and "Secrets stored in environment variables" (medium risk). The audit score was only 45; they must raise it to 80 or higher to achieve compliance.

(2) A Systematic Approach to Security Hardening

Charlie led his team in addressing the issues one by one: non-root users, Trivy scans to fix CVEs, switching to Docker Secrets, and using seccomp to restrict system calls.

BASH
# Security hardening: non-root + minimal capabilities
docker run -d \
  --user 1000:1000 \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  --read-only \
  myapp:1.0

(3) Results: Audit Score 45→92

Following systematic hardening, the audit score improved from 45 to 92, and all high-risk issues were resolved.


3. Container Security Layered Model

(1) Layered Security

100%
graph TB
    L6["6. Secret Management<br/>Docker Secret / Vault"] --> L5["5. Cybersecurity<br/>Network isolation / TLS"]
    L5 --> L4["4. Runtime Security<br/>seccomp / AppArmor / capabilities"]
    L4 --> L3["3. Mirror Security<br/>Scan / Signature / Minimize"]
    L3 --> L2["2. Docker Daemon Security<br/>TLS / User Permissions"]
    L2 --> L1["1. Host Security<br/>OS Reinforcement / Kernel Update"]

(1) Security Level Comparison

Level Reinforcement Measures Effect
0 Default configuration ❌ Numerous alerts
1 Non-root + latest tag ⚠️ Basic security
2 + Mirror Scan + Fixed Version ✅ Moderate Security
3 + Minimum Capabilities + seccomp ✅ Good Security
4 + DCT + Secret Management ✅ High Security
5 + Network Isolation + Audit Logs ✅ Maximum Security

4. Mirror Vulnerability Scanning

▶ Example: Trivy Image Scan (Difficulty: ⭐⭐)

BASH
# Install Trivy (Linux)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Scan an image for vulnerabilities
trivy image nginx:1.25-alpine

# Scan with severity filter
trivy image --severity HIGH,CRITICAL nginx:1.25-alpine

# Scan and output JSON
trivy image --format json --output report.json myapp:1.0
💻 Output (excerpt):

TEXT
nginx:1.25-alpine (alpine 3.19.0)
=========================
Total: 5 (HIGH: 2, CRITICAL: 0)

┌──────────┬────────────┬──────────┬──────────┬─────────────────┐
│ Library  │ Vulnerability│ Severity │ Version  │ Fixed Version   │
├──────────┼────────────┼──────────┼──────────┼─────────────────┤
│ libcurl  │ CVE-2023-xxxx│ HIGH     │ 8.4.0    │ 8.5.0           │
│ openssl  │ CVE-2023-yyyy│ HIGH     │ 3.1.3    │ 3.1.4           │
└──────────┴────────────┴──────────┴──────────┴─────────────────┘

▶ Example: Docker Scout scan (Difficulty: ⭐⭐)

BASH
# Docker Scout (Docker Desktop built-in)
docker scout quickview nginx:latest

# Detailed CVE report
docker scout cves nginx:latest

# Compare two images
docker scout compare --from nginx:1.24 --to nginx:1.25

5. The Principle of Least Privilege

(1) Linux Capabilities

Capability Function Common Requirements
NET_BIND_SERVICE Bound to port <1024 Web services (80/443)
NET_RAW Raw Network Packets ping / Network Debugging
CHOWN Change File Owner File Management
SETUID / SETGID Switch User Some System Tools
ALL All Abilities ❌ Do Not Use

▶ Example: --cap-drop Least Privilege (Difficulty ⭐⭐⭐)

BASH
# Drop ALL capabilities, add only what's needed
docker run -d \
  --name secure-app \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  -p 80:8080 \
  myapp:1.0
Parameter Function
--cap-drop ALL Remove all Linux capabilities
--cap-add NET_BIND_SERVICE Add the ability to bind only to low ports
--security-opt no-new-privileges Privilege escalation prohibited (setuid/setgid)
--read-only Read-only file system (requires tmpfs)
🔒 Security: --privileged grants containers nearly all host privileges—never use this in a production environment. Using --cap-drop ALL + --cap-add on an as-needed basis embodies the principle of least privilege.**


6. Runtime Security

▶ Example: seccomp Restricts System Calls (Difficulty: ⭐⭐⭐)

BASH
# Run with a custom seccomp profile
docker run -d \
  --security-opt seccomp=chrome.json \
  --name browser \
  chromium:latest

(1) Docker Default seccomp

Docker comes with a default seccomp configuration file that disables approximately 44 dangerous system calls (such as mount, keyctl, and add_key). Most applications run normally with the default configuration.

▶ Example: Docker Content Trust Signature (Difficulty: ⭐⭐)

BASH
# Enable DCT for the current session
export DOCKER_CONTENT_TRUST=1

# Push a signed image
docker push myorg/myapp:v1.0

# Pull: DCT verifies the signature
docker pull myorg/myapp:v1.0

# Pull unsigned image: DENIED (with DCT enabled)
docker pull myorg/myapp:unsigned
# Error: No trust data for unsigned

7. Secret Security Management

(1) Comparison of Three Secret Management Methods

Method Security Level Visibility Applicable Scenarios
Environment Variables (-e) ❌ Low docker inspect Visible Development Environment
Volume Files (-v) ⚠️ In Progress Visible to Host Files Transition Plan
Docker Secret ✅ High Encrypted storage, runtime mounting Swarm production environment
External Vault ✅ Highest Centralized Management + Auditing Enterprise-grade

8. CIS Docker Benchmark

▶ Example: Docker Bench Security Baseline Check (Difficulty: ⭐⭐)

BASH
# Run CIS Docker Benchmark check
docker run --rm --net host --pid host \
  --userns host --cap-drop audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /etc:/etc:ro \
  -v /lib/systemd/system:/lib/systemd/system:ro \
  -v /usr/bin/containerd:/usr/bin/containerd:ro \
  -v /usr/bin/runc:/usr/bin/runc:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker/docker-bench-security
💻 Output (excerpt):

TEXT
[INFO] 1 - Host Configuration
[INFO] 1.1  - Ensure Docker is up to date
[PASS] 1.2  - Ensure only trusted users are allowed to control Docker daemon
[WARN] 1.4  - Ensure a separate partition for containers has been created

[INFO] 4 - Container Images
[PASS] 4.1  - Ensure a user for the container has been created
[WARN] 4.7  - Ensure HEALTHCHECK instructions have been added
[WARN] 4.9  - Ensure COPY is used instead of ADD

9. Complete Example: Full-Stack Hardening of Production Containers

BASH
# ============================================
# Complete walkthrough: Production container hardening
# From audit score 45 → 92
# ============================================

# Step 1: Scan for vulnerabilities
trivy image --severity HIGH,CRITICAL myapp:1.0
# Fix: update base image, pin versions

# Step 2: Run with maximum security constraints
docker run -d \
  --name hardened-app \
  --user 1000:1000 \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  --security-opt seccomp=default.json \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid \
  --tmpfs /run:rw,noexec,nosuid \
  -v app-logs:/app/logs \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  --health-cmd="curl -f http://localhost:8080/health || exit 1" \
  --health-interval=30s \
  --health-retries=3 \
  -p 8080:8080 \
  myapp:hardened

# Step 3: Verify security configuration
docker inspect hardened-app --format='
User: {{.Config.User}}
ReadOnly: {{.HostConfig.ReadonlyRootfs}}
Capabilities: {{.HostConfig.CapAdd}}
NoNewPrivileges: {{.HostConfig.SecurityOpt}}
'

# Step 4: Run CIS benchmark
docker run --rm ... docker/docker-bench-security

# Step 5: Re-scan after hardening
trivy image myapp:hardened

❓ FAQ

Q Can Docker containers be completely isolated?
A No. Containers share the host kernel, and kernel vulnerabilities could lead to container escape. However, containers provide process-level isolation (namespace + cgroup + seccomp + AppArmor), which significantly reduces the attack surface. Key points: Do not use the --privileged option, do not run as root, and update the kernel promptly.
Q What are the risks of using --privileged?
A --privileged grants the container nearly all host privileges: access to all devices, the ability to modify kernel parameters, load kernel modules, and bypass seccomp/AppArmor. This is essentially the same as running a program directly on the host. Never use --privileged in a production environment.
Q What tools should I use for image vulnerability scanning?
A Trivy (open source, most popular) or Docker Scout (built into Docker Desktop). Integrate into CI/CD: Perform automatic scans after builds and block deployment if CRITICAL vulnerabilities are detected. We recommend scanning production images daily and rebuilding them promptly after new CVEs are released.
Q What’s the problem with storing secrets in environment variables?
A There are three risks: ① docker inspect Environment variables are visible to anyone; ② Child processes inherit environment variables; ③ Environment variables may be accidentally printed in logs. Docker Secrets (Swarm) are stored in encrypted form and mounted as temporary files at runtime, which is more secure. In non-Swarm environments, use HashiCorp Vault.
Q Is rootless mode secure?
A It is very secure. Rootless Docker runs the entire Docker daemon as a regular user, so even if Docker itself has a vulnerability, an attacker would only have regular user privileges. However, rootless mode has some limitations (no --privileged option, restricted network configuration). Rootless mode is recommended for production environments.

📖 Summary


📝 Exercises

  1. Basic Question (Difficulty: ⭐): Run a Docker Bench Security scan on your local machine and record any WARN-level alerts.
  2. Advanced Challenge (Difficulty ⭐⭐): Use Trivy to scan an existing image and fix all CRITICAL-level CVEs.
  3. Challenge (Difficulty: ⭐⭐⭐): Create a container with seccomp restrictions and verify that the restrictions are in effect (e.g., disabling the mount system call).
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏