Docker for Backend & AI Engineers
Trang chủ / Module 8
🔒 Module 8 / 12 · 1 tuần

Image Optimization & Security

Giảm kích thước image tối đa, chạy container không phải root, quét lỗ hổng bảo mật, quản lý secret đúng cách.

SecurityDistrolessScanning
📌 Vì sao module này quan trọng

Một image lớn, chạy bằng root, đầy lỗ hổng chưa vá là rủi ro bảo mật thực sự — container không tự động an toàn chỉ vì "được cô lập". Đây là kỹ năng phân biệt Dockerfile "chạy được" với Dockerfile "sẵn sàng production", áp dụng cùng tinh thần OWASP đã học ở Module 10 khoá AI Engineer nhưng cho tầng hạ tầng container.

🎯 Mục tiêu học tập

  • Áp dụng kỹ thuật giảm kích thước image xuống mức tối thiểu (distroless, alpine)
  • Chạy container bằng non-root user theo nguyên tắc least privilege
  • Quét lỗ hổng bảo mật trong image bằng công cụ tự động
  • Quản lý secret đúng cách, không để lộ trong image hoặc build history

Distroless & Alpine — giảm bề mặt tấn công

Alpine (dựa trên musl libc, không phải glibc) là base image tối giản phổ biến, nhỏ hơn Debian/Ubuntu nhiều lần — nhưng cần lưu ý một số thư viện Python/Node biên dịch sẵn (wheel) có thể không tương thích thẳng với musl, đôi khi cần build lại từ source, làm chậm build.

Distroless (do Google phát triển) đi xa hơn: image runtime không hề có shell, package manager, hay bất kỳ công cụ hệ thống nào — chỉ có đúng runtime cần thiết (ví dụ Python interpreter) và ứng dụng của bạn. Lợi ích bảo mật rất lớn: nếu kẻ tấn công khai thác được lỗ hổng ứng dụng, họ cũng không có shell để chạy lệnh tiếp theo — giảm mạnh khả năng khai thác leo thang. Đánh đổi: khó debug hơn (không exec vào được để chạy lệnh kiểm tra).

# Multi-stage: build ở image đầy đủ, chạy ở distroless
FROM python:3.12-slim AS build
WORKDIR /app
COPY . .
RUN pip install --target=/app/deps -r requirements.txt

FROM gcr.io/distroless/python3-debian12
COPY --from=build /app /app
WORKDIR /app
ENV PYTHONPATH=/app/deps
CMD ["main.py"]

Non-root User — Least Privilege cho Container

Mặc định, tiến trình trong container chạy bằng root — nếu container bị chiếm quyền (qua lỗ hổng ứng dụng), kẻ tấn công có quyền root bên trong container, và trong một số cấu hình sai (ví dụ mount Docker socket vào container), điều này có thể leo thang thành quyền root trên chính máy host. Luôn tạo và chuyển sang user thường:

RUN useradd --create-home --shell /bin/bash appuser
USER appuser
# Mọi lệnh RUN/CMD/ENTRYPOINT sau dòng này chạy bằng appuser, không phải root
# Kiểm tra nhanh user đang chạy trong container
docker exec my-container whoami

Quét lỗ hổng bảo mật — Image Scanning

Base image và dependency có thể chứa lỗ hổng đã biết (CVE) — cần quét định kỳ, tốt nhất là tự động trong CI/CD (Module 10):

# Trivy — công cụ quét image mã nguồn mở phổ biến
trivy image my-app:latest

# Chỉ báo lỗi (exit code khác 0) khi có lỗ hổng mức HIGH/CRITICAL — dùng làm cổng chặn CI
trivy image --severity HIGH,CRITICAL --exit-code 1 my-app:latest

Kết hợp với thói quen cập nhật base image định kỳ (image "mới build hôm qua" có thể đã chứa lỗ hổng mới được công bố hôm nay trong 1 thư viện hệ thống của base image) — nên có job CI định kỳ (không chỉ lúc deploy) rebuild và re-scan image production.

Quản lý Secret đúng cách

⚠️ Sai lầm nghiêm trọng thường gặp

Dùng ARG/ENV trong Dockerfile để truyền secret (API key, password) lúc build — secret này bị lưu vĩnh viễn trong lịch sử layer của image, xem được bằng docker history dù đã "xoá" ở bước sau, và lộ ra nếu image được push lên registry công khai.

# SAI — secret lộ trong layer history
ARG API_KEY
RUN echo $API_KEY > /app/.env

# ĐÚNG — dùng BuildKit secret mount, không lưu vào layer nào
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=api_key \
    API_KEY=$(cat /run/secrets/api_key) some-build-command
# Build với secret truyền vào lúc build, không lưu trong image
docker build --secret id=api_key,src=./api_key.txt -t my-app .

Với secret cần dùng lúc chạy (không phải lúc build) — như API key gọi LLM — luôn truyền qua biến môi trường lúc docker run/Compose (Module 5), không bao giờ bake vào image.

🏋️ Bài tập thực hành

Hardening một image production

Lấy image FastAPI đã xây ở Module 7, áp dụng: chuyển sang non-root user, thử chuyển sang distroless (hoặc alpine nếu distroless không tương thích dependency) và so sánh kích thước trước/sau. Chạy trivy scan, ghi lại số lượng lỗ hổng theo mức độ nghiêm trọng. Xác nhận không có secret nào lộ trong docker history.

📚 Tài nguyên học tập

✅ Tự đánh giá hoàn thành

  • Giảm được kích thước image bằng distroless hoặc alpine, đo được số liệu cụ thể
  • Container chạy bằng non-root user, xác nhận bằng docker exec whoami
  • Chạy được image scanning và đọc hiểu kết quả
  • Không còn secret nào lộ trong Dockerfile hoặc docker history