Skip to content

๐Ÿ”’ Security Best Practices โ€‹

1. Don't Run Containers as Root โ€‹

By default, containers run as root. If your application has a vulnerability, an attacker gains root access directly โ€” essentially handing over the admin key.

dockerfile
# Create a non-root user in the Dockerfile
FROM node:18-slim

# Create the application user
RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .

# Switch to the non-root user
USER appuser

CMD ["node", "server.js"]
bash
# You can also specify a user at runtime
docker run -d --user 1000:1000 myapp

# Check which user a container is running as
docker inspect --format='{{.Config.User}}' myapp
appuser

2. Read-Only File System โ€‹

bash
# Set the container's file system to read-only (prevents tampering)
docker run -d --read-only --tmpfs /tmp --tmpfs /var/run myapp
# /tmp and /var/run use tmpfs (in-memory); mount directories the app needs to write to as volumes

# How to do this in docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
  web:
    image: nginx:alpine
    read_only: true
    tmpfs:
      - /tmp
      - /var/cache/nginx
      - /var/run
EOF

3. Don't Mount the Docker Socket โ€‹

-v /var/run/docker.sock:/var/run/docker.sock seems convenient (you can manage Docker from inside a container), but it effectively grants the container root-level access to the host โ€” an attacker can use Docker to create privileged containers and escape directly to the host machine.

๐Ÿ’ก Tip: ๐Ÿ’ก Exception: CI/CD tools (like Jenkins) and certain orchestration tools need the Docker socket, but it should be restricted to read-only (:ro), and Docker-in-Docker (DinD) or rootless Docker should be used as alternatives.

4. Image Security Scanning โ€‹

bash
# Scan an image for vulnerabilities using Docker Scout (built into Docker Desktop)
docker scout cves myapp:latest

# Scan using Trivy (open source, recommended)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image myapp:latest

# Example scan output:
# myapp:latest (debian 12.4)
# Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 1, CRITICAL: 0)
#
# โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
# โ”‚  Library   โ”‚ Vulnerability โ”‚ Severity โ”‚  Fixed Version      โ”‚
# โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
# โ”‚  openssl   โ”‚ CVE-2024-xxx โ”‚ HIGH     โ”‚  3.0.13-1~deb12u1   โ”‚
# โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

# Integrate scanning into CI/CD (block builds on high/critical vulnerabilities)
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest