Skip to content

Docker Security Audit

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Quét lỗ hổng bảo mật Docker image bằng Trivy
  • Sửa Dockerfile tuân thủ security best practices
  • Triển khai rootless container để giảm attack surface
  • Quản lý secrets an toàn và giới hạn tài nguyên

Mô tả bài tập

Bạn audit bảo mật cho ứng dụng Node.js đã dockerize. Image hiện tại chạy root, không giới hạn resource, hardcode secrets. Nhiệm vụ: fix từng vấn đề.

Yêu cầu

Bài 1: Scan Image với Trivy

bash
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy:latest image --severity HIGH,CRITICAL node:20

Yêu cầu: Scan node:20node:20-alpine. So sánh số lượng CVE giữa hai image.

Bài 2: Fix Dockerfile Security Issues

Dockerfile dưới đây có 4 vấn đề bảo mật. Tìm và sửa tất cả.

dockerfile
FROM node:20
ENV DATABASE_URL=postgres://admin:password@db:5432/app
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]

Yêu cầu: Viết lại: dùng alpine, non-root user, không hardcode secrets, chỉ copy cần thiết.

Bài 3: Rootless Container & Resource Limits

dockerfile
FROM node:20-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]

Yêu cầu: Build image, chạy container với --memory=256m --cpus=0.5 --read-only, xác nhận process chạy user appuser.

Gợi ý

Gợi ý Bài 1

Alpine images thường có ít CVE hơn vì chỉ chứa packages tối thiểu. Dùng --format table để dễ đọc.

Gợi ý Bài 2

4 vấn đề: (1) Full image thay vì alpine, (2) Hardcode secrets trong ENV, (3) Chạy root, (4) COPY toàn bộ source.

Lời giải tham khảo

Xem lời giải
dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --chown=appuser:appgroup src/ ./src/
COPY --chown=appuser:appgroup server.js .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
yaml
services:
  api:
    build: .
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
    security_opt:
      - no-new-privileges:true
    read_only: true
secrets:
  db_password:
    file: ./secrets/db_password.txt