Skip to content

Module 5: Hardening & Optimization (Gia cố & Tối ưu hóa)

🎓 Instructor Profile

Kỹ sư Raizo (Phó CTO HPN) - Chuyên gia DevSecOps với tư duy "Zero Trust" (Không tin bất cứ ai). Cùng Giáo sư Tom xây dựng những pháo đài Container bất khả xâm phạm.

Chào mừng đến với Module quan trọng nhất đối với một kỹ sư Production. Bạn có thể build image nhanh, chạy compose giỏi, nhưng nếu để lộ một lỗ hổng bảo mật, toàn bộ hệ thống của công ty có thể sụp đổ chỉ sau một đêm.

Container rất mạnh, nhưng mặc định nó KHÔNG an toàn tuyệt đối. Chúng ta phải gia cố (Harden) nó.


🛡️ Phần 1: The Root Trap (Cạm bẫy quyền Root)

Sự thật trần trụi

Theo mặc định, mọi tiến trình trong Docker Container đều chạy với UID 0 (Root User).

Rủi ro chết người (Critical Risk) ☠️

Container chia sẻ Kernel với máy Host. Nếu Hacker khai thác được một lỗ hổng trong ứng dụng (VD: SQL Injection, Remote Code Execution) để chiếm quyền điều khiển Container -> Hắn lập tức có quyền Root trong Container. Nếu Kernel Linux có lỗ hổng chưa kịp vá (Kernel Exploit), Hacker có thể thực hiện kỹ thuật Container Breakout (Vượt ngục) và chiếm quyền Root của toàn bộ máy chủ vật lý.

👉 Quy tắc vàng: "Drop Root". Hãy biến Container thành một nhà tù không có chìa khóa.


⚖️ Phần 2: Implementing Least Privilege (Thực thi quyền tối thiểu)

Nguyên tắc Least Privilege: Chỉ cấp vừa đủ quyền để ứng dụng chạy, không hơn không kém.

Kỹ thuật trong Dockerfile

Thay vì để Docker tự quyết định, hãy chủ động tạo User thường.

dockerfile
# 1. Chọn Base Image
FROM node:18-alpine

# 2. Tạo Group và User hệ thống (System User)
# -S: System user (không cần login)
# -G: Thuộc group nào
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 3. Phân quyền sở hữu file cho user mới
# Nếu không có dòng này, file copy vào sẽ thuộc quyền root -> appuser không đọc/ghi được
COPY --chown=appuser:appgroup package*.json ./
RUN npm install
COPY --chown=appuser:appgroup . .

# 4. 🛑 DROP ROOT: Chuyển sang user thường từ dòng này
USER appuser

CMD ["node", "index.js"]

⚠️ Lưu ý về Port

User thường (Non-root) không thể bind vào các Privileged Ports (Cổng < 1024).

  • ❌ Không thể lắng nghe port 80.
  • ✅ Phải đổi ứng dụng sang lắng nghe port > 1024 (VD: 3000, 8080).

📉 Phần 3: Resource Quotas (Hạn ngạch tài nguyên)

Tư duy @[/observability]: Sẽ ra sao nếu code của bạn có bug Memory Leak (Rò rỉ bộ nhớ)? Container sẽ ăn dần RAM cho đến khi HẾT SẠCH RAM của máy chủ vật lý -> Máy chủ bị treo -> Tất cả dịch vụ khác chết theo (Domino Effect).

OOM Killer (Kẻ sát nhân giấu mặt)

Linux Kernel có một cơ chế tự vệ là Out Of Memory (OOM) Killer. Khi hết RAM, nó sẽ chọn tiến trình nào "ăn hại" nhất và bắn bỏ (Kill) không thương tiếc để cứu hệ thống.

Giải pháp: Limit Resource

Luôn đặt giới hạn cho Container để khoanh vùng thiệt hại.

yaml
# docker-compose.yml
services:
  web:
    image: my-app
    deploy:
      resources:
        limits:
          cpus: '0.50'    # Chỉ được dùng tối đa 50% của 1 core CPU
          memory: 512M    # Chỉ được dùng tối đa 512MB RAM
        reservations:
          memory: 128M    # Đảm bảo luôn có ít nhất 128MB để chạy

🧹 Phần 4: Image Hygiene (Vệ sinh Image)

Image càng chứa nhiều thứ, Bề mặt tấn công (Attack Surface) càng rộng.

1. Quét lỗ hổng (Vulnerability Scanning)

Sử dụng Docker Scout (công cụ mới thay thế Docker Scan) hoặc Trivy.

bash
# Xem nhanh các lỗi bảo mật
docker scout quickview node:14-alpine

Bạn sẽ thấy danh sách các lỗ hổng CVE (Common Vulnerabilities and Exposures) được phân loại: Critical, High, Medium. 👉 Hành động: Nếu thấy Critical, hãy nâng cấp Base Image ngay lập tức (VD: từ node:14 lên node:18).

2. Distroless Images (Cấp độ Paranoid) 🕵️

Ngay cả alpine cũng chứa các công cụ như sh, ls, cat, vi. Nếu Hacker vào được, hắn có thể dùng các công cụ này để thám thính và phá hoại.

Google Distroless Images là các image đã bị lột bỏ hoàn toàn Shell và các tiện ích Linux. Chỉ chứa đúng Runtime (Java, Node, Python) và App của bạn.

  • Ưu điểm: Hacker vào được shell -> Không gõ được lệnh gì cả.
  • Nhược điểm: Khó debug (vì không exec vào được).

🔒 Phần 5: Read-Only Filesystem

Để ngăn chặn Hacker cài Backdoor (Cửa hậu) hoặc sửa đổi mã nguồn ngay trên môi trường Production, hãy khóa quyền ghi.

yaml
services:
  web:
    image: my-app
    read_only: true  # 🧱 Biến container thành pháo đài "Nội bất xuất, ngoại bất nhập"
    volumes:
      - /tmp         # Chỉ cho phép ghi vào các vùng đệm cần thiết

🏆 Challenge: The Hardening (Thử thách Gia cố)

Nhiệm vụ:

  1. Tạo một Dockerfile chạy Nginx.
  2. Cấu hình để Nginx chạy dưới quyền user nginx (có sẵn trong image), thay vì root.
  3. Đổi port Nginx từ 80 sang 8080 (trong file config Nginx và Dockerfile).
  4. Giới hạn RAM 64MB.
  5. Thêm healthcheck để đảm bảo server còn sống.

Nếu bạn làm được điều này, bạn đã lọt vào top 10% Developer quan tâm đến bảo mật thực sự! 🎖️