Tạo và quản lý SBOM cho Docker Image với Syft và Grype: Giữ sạch chuỗi cung ứng container

Bạn có thực sự biết bên trong Docker Image của mình chứa những gì?

Phần lớn chúng ta bắt đầu với Docker bằng một mục tiêu đơn giản: viết Dockerfile sao cho app chạy được, build xong image rồi đẩy lên Docker Hub hoặc AWS ECR. Nhìn vào một file Dockerfile khởi đầu với FROM node:18-bullseye-slim, image trông có vẻ gọn gàng. Thế nhưng, nó kéo theo hơn 90 package Debian (như libssl, libc6, curl) cùng toàn bộ thư viện npm lồng nhau mà bạn chưa từng tự tay cài đặt.

Năm 2021, lỗ hổng Log4j (CVE-2021-44228) bùng nổ. Câu hỏi đầu tiên tech lead hoặc đội SecOps quăng vào nhóm chat thường là: “Production của mình có image nào dính Log4j không?”. Nếu lúc đó bạn phải SSH vào từng server, exec vào từng container đang chạy để gõ lệnh tìm file .jar, bạn đã đi sau sự cố hàng giờ đồng hồ.

Bộ ba: SBOM, Syft và Grype

SBOM (Software Bill of Materials) là gì?

SBOM đóng vai trò hệt như bảng thành phần dinh dưỡng in sau hộp bánh hay hóa đơn linh kiện khi ráp máy tính. Trong thế giới container, SBOM liệt kê rành mạch mọi thành phần: tên package, phiên bản chính xác, tác giả, license đi kèm và mã băm SHA-256. Danh mục này bao gồm cả package tầng OS (như apt, apk, rpm) lẫn thư viện tầng ứng dụng (npm, pip, maven, go module).

Thực tế hiện nay chuẩn hóa theo hai định dạng chính:

  • SPDX (Software Package Data Exchange): Chuẩn mở của Linux Foundation, tối ưu cho việc rà soát bản quyền và giấy phép mã nguồn.
  • CycloneDX: Chuẩn do cộng đồng OWASP dẫn dắt, thiết kế chuyên biệt cho kiểm soát rủi ro bảo mật và phân tích chuỗi cung ứng.

Cặp bài trùng từ Anchore: Syft và Grype

Thay vì tự viết script phân tích layer phức tạp, cộng đồng DevSecOps ưu tiên bộ đôi mã nguồn mở từ Anchore:

  • Syft: Công cụ CLI quét image, filesystem hoặc repo code rồi bóc tách thành file SBOM chuẩn chỉ trong 2 đến 4 giây.
  • Grype: Scanner chuyên đọc trực tiếp file SBOM do Syft xuất ra. Grype đối chiếu dữ liệu đó với các nguồn CVE (NVD, GitHub Advisories, Alpine SecDB) liên tục cập nhật để chỉ điểm chính xác package dính lỗi.

Thực hành: Tạo và rà soát SBOM từ A đến Z

Bước 1: Cài đặt Syft và Grype

Trên máy Linux hoặc macOS, bạn có thể cài nhanh bằng script chính thức:

# Cài đặt Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

# Cài đặt Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

# Kiểm tra phiên bản
syft version
grype version

Nếu dùng macOS có sẵn Homebrew, bạn chỉ cần gõ brew install syft grype.

Bước 2: Chuẩn bị Docker Image mẫu

Hãy tạo một app Node.js nhỏ có chứa một dependency cũ cố tình dính lỗ hổng để kiểm chứng:

mkdir sbom-demo && cd sbom-demo
npm init -y
npm install express [email protected]

Tạo file Dockerfile:

FROM node:18-bullseye-slim
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
CMD ["node", "index.js"]

Tiến hành build image trên máy local:

docker build -t my-app:1.0.0 .

Bước 3: Trích xuất SBOM bằng Syft

Muốn xem lướt các package ngay trên terminal, bạn chỉ cần truyền tên image:

syft my-app:1.0.0

Bảng hiển thị sẽ liệt kê tên package, phiên bản hiện tại cùng định dạng (deb, npm). Tiếp theo, hãy trích xuất file SBOM hoàn chỉnh theo chuẩn CycloneDX JSON:

syft my-app:1.0.0 -o cyclonedx-json > sbom-my-app.json

File kết quả thường dài vài nghìn dòng với dung lượng tầm 300KB đến 1MB. Cấu trúc JSON chứa cây phụ thuộc rất sâu. Để đọc hoặc tìm kiếm các trường như components, hashes hay thông tin bản quyền, bạn có thể dán nhanh nội dung vào toolcraft.app/vi/tools/developer/json-formatter để format và thu gọn nhánh dữ liệu trực quan hơn.

Trong trường hợp dự án quy định dùng chuẩn SPDX, bạn đổi cờ format sang spdx-json:

syft my-app:1.0.0 -o spdx-json > sbom-my-app.spdx.json

Bước 4: Quét lỗ hổng với Grype

Điểm mạnh của việc lưu file SBOM: bạn không cần giữ hay kéo lại image 500MB mỗi lần muốn kiểm tra an toàn. Bạn chỉ cần đưa file JSON vài trăm KB cho Grype:

grype sbom:./sbom-my-app.json

Lệnh chạy mất chưa đầy 2 giây. Grype trả về bảng phân tích chi tiết:

NAME    INSTALLED  FIXED-IN  TYPE  VULNERABILITY   SEVERITY
lodash  4.17.20    4.17.21   npm   CVE-2021-23337  High
lodash  4.17.20    4.17.21   npm   CVE-2020-28500  Medium

Kết quả chỉ rõ: [email protected] dính lỗi bảo mật High (Command Injection via template) và bạn chỉ việc bump package lên 4.17.21 để vá triệt để.

Bước 5: Chặn image chứa lỗ hổng trong GitHub Actions

Đừng chờ đến lúc deploy xong mới quét thủ công. Cách tốt nhất là biến bước kiểm tra này thành quality gate bắt buộc trong pipeline CI/CD.

File workflow dưới đây tự động tạo SBOM và đánh fail pipeline nếu phát hiện lỗi từ mức High trở lên:

name: Docker Security Pipeline

on:
  push:
    branches: [ "main" ]

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker Image
        run: docker build -t my-app:${{ github.sha }} .

      - name: Generate SBOM with Syft
        uses: anchore/sbom-action@v0
        with:
          image: my-app:${{ github.sha }}
          format: cyclonedx-json
          output-file: sbom.json

      - name: Scan Vulnerabilities with Grype
        uses: anchore/scan-action@v4
        with:
          sbom: sbom.json
          fail-build: true
          severity-cutoff: high

Chỉ cần ai đó vô tình add một thư viện chứa CVE nghiêm trọng, pull request sẽ bị chặn đỏ ngay tại cổng CI, không thể lọt ra production.

Lời kết

Đưa Syft và Grype vào quy trình build image biến việc quản lý bảo mật từ thế bị động thành chủ động. Mỗi commit giờ đây đều có một file SBOM đi kèm như giấy khai sinh minh bạch. Khi lỗ hổng zero-day xuất hiện, bạn chỉ mất vài giây query file SBOM thay vì cuống cuồng lục lọi từng cụm server.

Share: