Docker đã trở thành standard cho việc deploy applications. Nhưng container không phải là sandbox — nếu cấu hình sai, attacker có thể thoát ra khỏi container và kiểm soát host. Bài viết này hướng dẫn các best practices để bảo mật Docker container từ image creation đến production deployment.
Tại sao Docker Security quan trọng?
Container chia sẻ kernel với host. Khác với VM, container không có hardware isolation mạnh. Một container bị compromise có thể:
- Truy cập host filesystem.
- Escalate privileges thành root.
- Khai thác container runtime vulnerabilities.
- Lan truyền sang containers khác.
Theo Palo Alto Networks, 94% container images có已知 vulnerabilities. Không phải vì Docker không bảo mật — mà vì người dùng không cấu hình đúng.
1. Image Security
Dùng Official Images
Official images trên Docker Hub được scan thường xuyên và maintain bởi Docker team.
# ✅ Tốt
FROM node:20-alpine
# ❌ Tránh
FROM some-random-user/node:latest
Dùng Minimal Base Images
Image càng nhỏ, attack surface càng ít. Alpine Linux chỉ ~5MB so với Ubuntu ~80MB.
# ✅ Tốt - Alpine
FROM node:20-alpine
# ✅ Tốt - Distroless
FROM gcr.io/distroless/nodejs20
# ❌ Tránh - Full OS
FROM ubuntu:22.04
Pin Image Versions
Không dùng latest — nó thay đổi theo thời gian, không predictable.
# ❌ Tránh - latest thay đổi
FROM node:latest
# ✅ Tốt - Pin version
FROM node:20.11.0-alpine3.19
# ✅ Tốt - Pin major version (auto update patch)
FROM node:20-alpine3.19
Scan Images trước khi deploy
# Docker Scout (built-in)
docker scout cves myapp:latest
# Trivy (open source)
docker run --rm aquasec/trivy image myapp:latest
# Hadolint (Dockerfile linting)
docker run --rm hadolint/hadolint Dockerfile
2. Dockerfile Best Practices
Không dùng Root User trong Container
Mặc định, container chạy với root. Tạo user riêng để giới hạn damage nếu bị compromise.
# Tạo non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# Chuyển sang user
USER appuser
# Hoặc dùng numeric UID
RUN chown -R 1000:1000 /app
Copy chỉ những gì cần thiết
Không copy các file không cần thiết vào image — giảm kích thước và attack surface.
# ❌ Tránh - Copy toàn bộ project
COPY . /app
# ✅ Tốt - Chỉ copy cần thiết
COPY package*.json /app/
COPY src/ /app/src/
COPY dist/ /app/dist/
Dùng Multi-stage Builds
Build artifacts trong stage riêng, chỉ copy output vào final image. Không có compiler, build tools trong production image.
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
# Chạy với non-root user
RUN chown -R 1000:1000 /app
USER 1000
CMD ["node", "dist/index.js"]
Health Check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
3. Runtime Security (Docker Run)
Chạy với Non-Root
# Chạy container với user khác
docker run -u 1000 myapp:latest
# Hoặc dùng --user với username
docker run -u appuser myapp:latest
Read-only Root Filesystem
Ngăn container ghi vào filesystem — chỉ cho phép ghi vào volumes.
docker run --read-only myapp:latest
Giới hạn Capabilities
Linux capabilities cho phép fine-grained control. Drop all, chỉ add cái cần thiết.
# Drop all capabilities, chỉ add NET_BIND_SERVICE
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp:latest
# Nếu không cần any capability
docker run --cap-drop ALL myapp:latest
Seccomp Profile
Seccomp giới hạn syscalls mà container có thể thực hiện.
# Dùng default seccomp profile (block dangerous syscalls)
docker run --security-opt seccomp=profile.json myapp:latest
# Hoặc dùng unconfined (TRÁNH trong production)
docker run --security-opt seccomp=unconfined myapp:latest
AppArmor Profile
# Dùng AppArmor profile
docker run --security-opt apparmor=docker-default myapp:latest
No New Privileges
Ngăn container escalate privileges (setuid binaries).
docker run --security-opt=no-new-privileges:true myapp:latest
Resource Limits
# Giới hạn CPU và RAM
docker run \
--memory="256m" \
--memory-swap="256m" \
--cpus="0.5" \
--pids-limit=100 \
myapp:latest
4. Network Security
Không map port ra ngoài nếu không cần
# ❌ Tránh - Map tất cả ports
docker run -P myapp:latest
# ✅ Tốt - Chỉ map port cần thiết
docker run -p 8080:3000 myapp:latest
Network Segmentation
# Tạo network riêng cho app
docker network create --driver bridge myapp-network
# Chạy container trong network riêng
docker run --network=myapp-network myapp:latest
# Database chỉ accessible từ app, không từ bên ngoài
docker run --network=myapp-network db:latest
Internal Network
# Database không có port mapped - chỉ accessible từ containers cùng network
docker run -d --network=myapp-network --name db postgres:16
# Không có -p 5432:5432
5. Secrets Management
Không bao giờ put secrets (passwords, API keys) trong Dockerfile hoặc environment variables plaintext.
# ❌ Tránh - Secrets trong ENV
ENV DATABASE_PASSWORD=secret123
# ❌ Tránh - Secrets trong CMD
CMD mysql -psecret123
# ✅ Tốt - Dùng Docker secrets (Swarm) hoặc external secrets
# Docker Secrets (Swarm mode)
echo "secret123" | docker secret create db_password -
docker service create --secret db_password myapp:latest
# ✅ Tốt - Dùng secret management service
# HashiCorp Vault, AWS Secrets Manager, Azure Key Vault
6. Logging và Monitoring
Docker Daemon Logging
# Configure log rotation
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Container Logging
# Xem logs
docker logs mycontainer
# Xem logs real-time
docker logs -f mycontainer
# Export logs
docker logs mycontainer > app.log
Container Runtime Security (Falco)
Falco là runtime security tool, detect suspicious activities trong containers.
# Cài đặt Falco
apt install -y falco
# Hoặc chạy như container
docker run -d --name falco \
--privileged \
-v /var/run/docker.sock:/var/run/docker.sock \
falcosecurity/falco:latest
7. Docker Bench Security
Docker Bench là script audit Docker host theo CIS benchmarks.
# Chạy Docker Bench
docker run -it --net host \
--pid host \
--cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc \
aquasec/docker-bench:latest
Output sẽ show các security issues theo categories: Host Configuration, Docker Daemon Configuration, Container Images, Runtime Policies…
8. Docker Compose Security
# docker-compose.yml
version: "3.9"
services:
app:
image: myapp:latest
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
resources:
limits:
cpus: '0.5'
memory: 256M
networks:
- app-network
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
networks:
app-network:
driver: bridge
9. Security Checklist
- [ ] Image: Dùng official images, pin versions.
- [ ] Scan: Scan images với Trivy hoặc Docker Scout trước deploy.
- [ ] User: Chạy container với non-root user.
- [ ] Read-only: Mount filesystem read-only, chỉ allow writes to volumes.
- [ ] Capabilities: Drop ALL capabilities, add only what needed.
- [ ] Network: Dùng internal networks, không expose ports không cần.
- [ ] Secrets: Không put secrets trong Dockerfile/ENV.
- [ ] Resources: Set CPU/RAM limits.
- [ ] Audit: Chạy Docker Bench định kỳ.
Kết Luận
Docker security không phải là setup once và quên. Đó là quy trình liên tục: scan images, update regularly, audit configs, monitor runtime.
- Use minimal images (Alpine, Distroless).
- Never run as root, drop capabilities.
- Scan images before deploy.
- Use secrets management, not ENV.
- Monitor with Falco, audit with Docker Bench.
Kết hợp với SSH Hardening và WireGuard VPN để có security stack hoàn chỉnh.