Bảo mật Container và quét lỗ hổng hình ảnh với Trivy và Clair

Trong chu trình phát triển phần mềm Cloud-Native năm 2026, một container image không đơn thuần là tệp thực thi của ứng dụng, mà là một tập hợp phân lớp phức tạp gồm nhân hệ điều hành thu nhỏ (Userland OS packages như glibc, openssl, libcurl), hàng trăm thư viện mã nguồn mở phụ thuộc (npm, pip, Go modules, Cargo, Maven) và các tệp cấu hình hạ tầng. Thống kê thực tế từ các cuộc kiểm toán chuỗi cung ứng phần mềm theo tiêu chuẩn NIST SP 800-190 cho thấy hơn 78% lỗ hổng bảo mật nghiêm trọng (CRITICAL / HIGH CVEs) trên môi trường Kubernetes Production không nằm ở mã nguồn nghiệp vụ do lập trình viên viết ra, mà ẩn náu trong các tầng Base Image lỗi thời hoặc các gói thư viện chuyển tiếp (Transitive Dependencies) chưa được kiểm duyệt.

Triển khai chiến lược bảo mật container và quét lỗ hổng với Trivy Clair xuyên suốt từ khâu đóng gói trên máy trạm, kiểm cổng tự động trên đường ống CI/CD, lưu trữ tại Container Registry cho tới kiểm soát thực thi trên cụm Kubernetes là yêu cầu bắt buộc của mọi kiến trúc DevSecOps hiện đại. Bài viết chuyên sâu này mổ xẻ cơ chế phân tích tầng ảnh chuẩn OCI, đặt lên bàn cân hai công cụ mã nguồn mở hàng đầu hiện nay là Aqua Security Trivy v0.58+ và Quay Clair v4.8+, đồng thời cung cấp quy trình thực chiến kết hợp Multi-stage Distroless/Wolfi, danh mục thành phần phần mềm SBOM (CycloneDX 1.6), lọc cảnh báo giả OpenVEX, ký số Sigstore Cosign và chặn triển khai bằng Kyverno Admission Controller.


Mục Lục Nội Dung

1. Giải phẫu Bề mặt Tấn công Chuỗi cung ứng Container (OCI Image Supply Chain) năm 2026

1.1. Ma trận rủi ro từ Base Image nguyên khối, Transitive Dependencies và rò rỉ Secret giữa các Layer

Để hiểu tại sao một container tưởng chừng vô hại lại trở thành bàn đạp cho kẻ tấn công chiếm quyền điều khiển máy chủ vật lý, chúng ta cần nhìn vào cấu trúc lưu trữ của một ảnh chuẩn OCI (Open Container Initiative). Mỗi chỉ thị FROM, RUN, COPY, ADD trong Dockerfile tạo ra một tầng hệ thống tệp chỉ đọc (Immutable Tarball Layer) được định danh bằng mã băm mật mã sha256 và xếp chồng lên nhau thông qua trình điều khiển OverlayFS.

Cấu trúc phân lớp này tạo ra 3 bề mặt tấn công đặc thù trong chuỗi cung ứng phần mềm:

  1. Di sản lỗ hổng từ Base Image đa năng (debian, ubuntu, node:latest): Các Base Image tiêu chuẩn đi kèm hàng trăm tiện ích hệ thống không cần thiết cho môi trường Production như trình biên dịch gcc, trình tải tệp curl/wget, trình thông dịch /bin/bash và các thư viện hệ thống lâu ngày chưa vá. Khi ứng dụng web bị khai thác lỗ hổng thực thi lệnh từ xa (RCE), kẻ tấn công tận dụng ngay /bin/sh và curl có sẵn trong container để tải mã độc đào tiền ảo hoặc công cụ rà quét mạng nội bộ (Living-off-the-Land).
  2. Lỗ hổng ẩn sâu trong Cây phụ thuộc chuyển tiếp (Transitive Dependencies): Một dịch vụ Python FastAPI hoặc Node.js chỉ khai báo trực tiếp 15 thư viện trong pyproject.toml hoặc package.json, nhưng cây phụ thuộc thực tế trong uv.lock hoặc pnpm-lock.yaml có thể kéo theo hơn 400 gói phụ trợ cấp 2, cấp 3 chứa các lỗ hổng thực thi mã tùy ý đã được công bố mã định danh CVE (Common Vulnerabilities and Exposures).
  3. Ảo tưởng xóa tệp nhạy cảm giữa các Layer (Layer Secret Persistence): Một sai lầm kinh điển là sao chép khóa SSH bí mật (id_ed25519), tệp .npmrc hoặc .env vào container ở lệnh RUN thứ nhất để tải gói phần mềm nội bộ, sau đó chạy RUN rm -f /root/.ssh/id_ed25519 ở tầng tiếp theo. Trong kiến trúc OverlayFS, lệnh rm chỉ tạo ra một tệp đánh dấu ẩn (Whiteout File .wh.id_ed25519) ở tầng trên cùng, trong khi toàn bộ nội dung khóa bí mật vẫn nằm nguyên vẹn dưới dạng văn bản rõ trong tệp .tar.gz của tầng bên dưới và có thể bị trích xuất chỉ bằng một lệnh docker save hoặc trivy image --scanners secret.

1.2. Mô hình Phòng thủ 3 Tầng theo chuẩn NIST SP 800-190: Pre-Commit/CI Gate, Registry Continuous Scan và Runtime Admission

Một lỗ hổng CVE mới có thể được công bố vào lúc 2 giờ sáng — tức là 3 tuần sau khi container image đã vượt qua bài kiểm tra CI/CD và đang chạy trên Production. Do đó, tiêu chuẩn NIST SP 800-190 (Application Container Security Guide) khẳng định việc quét lỗ hổng tại một điểm duy nhất là không bao giờ đủ. Hệ thống bảo mật container và quét lỗ hổng với Trivy Clair phải được thiết kế thành 3 lớp phòng thủ liên hoàn:

  • Tầng 1 — Chốt chặn Build & CI/CD Gate (Shift-Left với Trivy CLI): Quét trực tiếp Dockerfile, tệp khóa phụ thuộc (go.sum, Cargo.lock, uv.lock), rò rỉ Secret và toàn bộ OCI Image vừa đóng gói ngay trên GitHub Actions / GitLab CI. Nếu phát hiện lỗ hổng mức CRITICAL hoặc HIGH đã có bản vá (--ignore-unfixed), pipeline lập tức trả về mã lỗi khác 0 và hủy bỏ quá trình đẩy ảnh lên Registry.
  • Tầng 2 — Giám sát Liên tục tại Kho lưu trữ (Registry Indexing với Clair v4 / Harbor): Lưu trữ biểu đồ thành phần của toàn bộ các ảnh trong kho lưu trữ doanh nghiệp (Quay, Harbor) vào cơ sở dữ liệu PostgreSQL và đối chiếu tự động mỗi khi cơ sở dữ liệu lỗ hổng toàn cầu (NVD, Red Hat OVAL, Debian Security Tracker, GitHub Advisory) cập nhật CVE mới, ngay cả khi không có bất kỳ lệnh build mới nào diễn ra.
  • Tầng 3 — Kiểm soát Triển khai & Giám sát Runtime trên Kubernetes (Kyverno + trivy-operator): Xác minh chữ ký số Sigstore Cosign và chứng nhận SBOM Attestation tại cổng Admission Webhook trước khi cho phép khởi tạo Pod, đồng thời dùng trivy-operator quét định kỳ các workload đang chạy thực tế trên cụm.

Sơ đồ dưới đây minh họa kiến trúc phòng thủ chuỗi cung ứng Container 3 tầng tích hợp Trivy, Clair v4, Cosign và Kyverno:

flowchart LR
    subgraph Tier1["Tầng 1: Shift-Left CI/CD Gate (Trivy v0.58+)"]
        Code["Mã nguồn + Dockerfile (Multi-Stage Distroless/Wolfi)"]
        TrivyCI["Trivy CLI Scanner (CVE + Secret + Misconfig + OpenVEX)"]
        CosignSign["Sigstore Cosign OIDC (Ký số Ảnh + Đính kèm SBOM CycloneDX 1.6)"]
        Code --> TrivyCI
        TrivyCI -->|"Pass (0 Critical/High)"| CosignSign
    end

    subgraph Tier2["Tầng 2: Registry Continuous Scan (Clair v4.8+)"]
        Registry["Enterprise OCI Registry (Harbor / Red Hat Quay)"]
        ClairIdx["Clair v4 Indexer (Giải nén Layer -> IndexManifest)"]
        ClairMatch["Clair v4 Matcher & Notifier (Cập nhật NVD/OVAL Liên tục -> Webhook)"]
        CosignSign -->|"Push Image + .sig + .att"| Registry
        Registry --> ClairIdx
        ClairIdx --> ClairMatch
    end

    subgraph Tier3["Tầng 3: Kubernetes v1.32+ Runtime Protection"]
        Kyverno["Kyverno Admission Controller (Xác minh Chữ ký Cosign & SBOM)"]
        K8sCluster["Pod Thực thi An toàn (Non-Root 65532 + ReadOnlyRootFS)"]
        TrivyOp["Trivy Operator In-Cluster (Xuất CRD VulnerabilityReport Liên tục)"]
        Registry -->|"Pull Image qua Digest"| Kyverno
        Kyverno -->|"Chữ ký Hợp lệ"| K8sCluster
        K8sCluster --> TrivyOp
    end


2. Đặt lên bàn cân Kiến trúc: Aqua Security Trivy v0.58+ vs Quay Clair v4.8+

2.1. Kiến trúc Stateless CLI Đa năng của Trivy: Quét CVE, IaC Misconfig, Secret Leak và Khởi tạo SBOM

Trivy (do Aqua Security phát triển và duy trì mã nguồn mở) được thiết kế theo triết lý một tệp nhị phân đơn lẻ (Single Stateless Binary), không yêu cầu cài đặt cơ sở dữ liệu ngoài hay dịch vụ nền phức tạp khi chạy trên máy trạm của kỹ sư hoặc trong các runner tạm thời của CI/CD. Ở phiên bản Trivy v0.58+, công cụ này hoạt động bằng cách tải về một gói cơ sở dữ liệu lỗ hổng đã được biên dịch và nén tối ưu (trivy-db định dạng BoltDB lưu trên OCI Registry) chỉ mất vài giây, sau đó thực hiện 4 bộ quét (Scanners) trong cùng một lần duyệt hệ thống tệp:

  1. vuln (Vulnerability Scanner): Nhận diện chính xác hệ điều hành cơ sở (Alpine apk, Debian/Ubuntu dpkg, RHEL/Rocky rpm, Chainguard Wolfi) và các trình quản lý gói ngôn ngữ lập trình (pip/uv, npm/pnpm/yarn, Go binary metadata, Cargo, Composer, Jar/War).
  2. misconfig (IaC & Configuration Scanner): Tích hợp bộ quy tắc kiểm tra lỗi cấu hình bảo mật trên Dockerfile, Kubernetes YAML manifests, Helm Charts và Terraform HCL (dựa trên bộ máy chính sách Rego).
  3. secret (Secret Detection): Rà quét toàn bộ các layer của container image để phát hiện khóa AWS IAM, GitHub Personal Access Token, khóa riêng RSA/Ed25519 hoặc chuỗi kết nối cơ sở dữ liệu bị bỏ quên.
  4. license & SBOM Generator: Phân tích rủi ro bản quyền phần mềm (GPL/AGPL) và xuất trực tiếp danh mục thành phần phần mềm theo chuẩn CycloneDX 1.6 hoặc SPDX 2.3.

2.2. Kiến trúc Vi dịch vụ hướng Sự kiện của Clair v4 (Indexer, Matcher, Notifier) cho Container Registry quy mô lớn

Ngược lại với mô hình dòng lệnh linh hoạt của Trivy, Clair v4.8+ (được phát triển bởi dự án Quay / Red Hat) là một hệ thống dịch vụ phân tán chuyên dụng để gắn kết sâu vào các hệ thống Enterprise Container Registry lưu trữ hàng trăm ngàn container image. Thay vì tải lại và giải nén toàn bộ các layer của ảnh mỗi khi cần kiểm tra lỗ hổng mới, Clair v4 chia kiến trúc thành 3 dịch vụ độc lập giao tiếp qua cơ sở dữ liệu PostgreSQL 17:

  • Indexer (Trình Lập chỉ mục): Khi một container image mới được đẩy lên Registry, Indexer chỉ tải các layer chưa từng xuất hiện (nhờ cơ chế chống trùng lặp theo mã băm sha256 của layer), trích xuất danh sách gói phần mềm, thư viện và tệp phân phối, sau đó đóng gói thành một bản ghi IndexReport bất biến lưu vào PostgreSQL. Quá trình nặng nề này chỉ diễn ra đúng một lần duy nhất cho mỗi image digest.
  • Matcher (Trình Đối chiếu Lỗ hổng): Chạy các trình cập nhật nền (Updaters) để đồng bộ liên tục dữ liệu lỗ hổng từ Red Hat Security Data (OVAL v2), Debian, Ubuntu, Alpine, AWS ALAS, Oracle Linux và NVD. Khi người dùng hoặc hệ thống gọi API kiểm tra một ảnh, Matcher chỉ cần thực hiện phép kết nối dữ liệu (SQL Join) tốc độ cao giữa IndexReport đã lưu sẵn và bảng lỗ hổng mới nhất để sinh ra VulnerabilityReport trong vài chục mili-giây mà không cần chạm lại vào tệp ảnh gốc.
  • Notifier (Trình Thông báo Sự kiện): Theo dõi sự thay đổi giữa các lần cập nhật cơ sở dữ liệu CVE; nếu một CVE nghiêm trọng vừa được công bố làm ảnh hưởng tới bất kỳ IndexReport nào đang lưu trong Registry, Notifier tự động bắn cảnh báo qua Webhook, AMQP hoặc STOMP tới hệ thống SIEM/Jira của đội ngũ bảo mật.

Bảng dưới đây tổng hợp tiêu chí lựa chọn và phối hợp giữa Trivy v0.58+ và Clair v4.8+ trong kiến trúc thực tế:

Tiêu chí Kỹ thuật Aqua Security Trivy (v0.58+) Red Hat / Quay Clair (v4.8+)
Mô hình Kiến trúc CLI độc lập (Stateless) + Chế độ Client/Server + Kubernetes Operator (trivy-operator). Hệ thống Microservices có trạng thái (Indexer, Matcher, Notifier) + PostgreSQL Backend.
Vị trí Tối ưu trong Pipeline Máy trạm Developer, chốt chặn CI/CD Pipeline Gate và quét Runtime trong cụm Kubernetes. Tích hợp lõi cho Enterprise Container Registry (Red Hat Quay, Harbor) quản lý hàng vạn ảnh.
Phạm vi Quét Bảo mật Toàn diện: OS CVEs, Language Packages, IaC Misconfigurations, Hardcoded Secrets, Licenses. Chuyên sâu: OS Packages và Language Dependencies thông qua kiến trúc lưu trữ IndexReport.
Khởi tạo SBOM & Lọc VEX Hỗ trợ xuất trực tiếp CycloneDX 1.6 / SPDX 2.3 và lọc cảnh báo giả bằng OpenVEX (--vex). Nhận diện thành phần qua IndexReport; thường kết hợp công cụ ngoài để ký SBOM Attestation.
Chi phí Vận hành Hạ tầng Cực thấp (Chạy ngay trong job CI hoặc cài đặt qua 1 Helm Chart trên Kubernetes). Trung bình – Cao (Cần vận hành cụm PostgreSQL HA và quản lý kết nối API với Registry).

3. Hardening Container từ Gốc và Triển khai Trivy chặn Lỗ hổng CRITICAL/HIGH trên CI/CD

3.1. Tối ưu hóa Base Image: Loại bỏ >90% CVE từ gốc bằng Multi-Stage Build với Distroless / Chainguard Wolfi và Non-Root UID 65532

Cách hiệu quả nhất để xử lý lỗ hổng bảo mật trong container không phải là tốn hàng giờ vá từng gói hệ điều hành thừa thãi, mà là loại bỏ hoàn toàn hệ điều hành cơ sở và trình quản lý gói khỏi ảnh Runtime cuối cùng. Bằng cách áp dụng kỹ thuật đóng gói đa tầng — nguyên lý nền tảng đã được phân tích chi tiết trong bài viết tối ưu hóa Docker Image với Multi-Stage Build và Alpine Linux — kết hợp với ảnh Google Distroless (gcr.io/distroless/static-debian12:nonroot) hoặc Chainguard Wolfi (cgr.dev/chainguard/static:latest), chúng ta thu được một container chỉ chứa đúng tệp nhị phân đã biên dịch tĩnh, chứng chỉ gốc CA (ca-certificates) và dữ liệu múi giờ (tzdata).

Khi không còn /bin/sh, /bin/bash, apt, apk hay curl, bề mặt tấn công hệ điều hành giảm hơn 90%, đồng thời mọi nỗ lực khởi tạo Reverse Shell của kẻ tấn công đều thất bại ngay lập tức với lỗi exec: "/bin/sh": stat /bin/sh: no such file or directory.

Dưới đây là mẫu Dockerfile chuẩn DevSecOps 2026 sử dụng ghim mã băm bất biến (@sha256:...), cờ biên dịch chống tràn bộ nhớ và chạy dưới quyền người dùng không có đặc quyền USER 65532:65532 (nonroot):

# syntax=docker/dockerfile:1.10
# ==============================================================================
# STAGE 1: Builder — Biên dịch tĩnh mã nguồn trong môi trường cô lập
# Ghim chặt cả Tag và SHA-256 Digest để chống tấn công Tag Mutation trên Registry
# ==============================================================================
FROM golang:1.24.1-alpine3.21@sha256:4a9f9b3c8d1e2f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a AS builder

WORKDIR /build

# Chỉ sao chép tệp định nghĩa phụ thuộc trước để tận dụng bộ nhớ đệm BuildKit an toàn
COPY go.mod go.sum ./
RUN go mod download && go mod verify

COPY . .

# Biên dịch nhị phân tĩnh hoàn toàn (CGO_ENABLED=0), xóa bảng ký hiệu gỡ lỗi (-s -w)
# và nhúng thông tin Build VCS để Trivy có thể kiểm tra chính xác phiên bản Go module
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -trimpath -ldflags="-s -w -extldflags '-static'" \
    -o /out/payment-authorizer ./cmd/authorizer

# ==============================================================================
# STAGE 2: Hardened Runtime — Sử dụng Distroless Non-Root (Không Shell, Không Package Manager)
# ==============================================================================
FROM gcr.io/distroless/static-debian12:nonroot@sha256:9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e

WORKDIR /app

# Sao chép tệp thực thi với quyền chỉ đọc (0550) thuộc sở hữu của nonroot (65532:65532)
COPY --from=builder --chown=65532:65532 --chmod=0550 /out/payment-authorizer /app/payment-authorizer

# Bắt buộc thực thi bằng định danh số UID:GID 65532 (Phù hợp với runAsNonRoot: true trên K8s)
USER 65532:65532

EXPOSE 8443

ENTRYPOINT ["/app/payment-authorizer"]

3.2. Thiết lập cổng chặn Trivy trên GitHub Actions / GitLab CI và quản lý False Positives bằng chuẩn OpenVEX (--vex)

Khi tích hợp bước quét lỗ hổng vào quy trình tự động hóa — tương tự luồng xây dựng CI/CD Pipeline với GitHub Actions và Docker — thách thức lớn nhất mà các đội ngũ kỹ thuật gặp phải là hiện tượng Mệt mỏi vì cảnh báo (Alert Fatigue): một thư viện phụ thuộc có thể chứa một hàm bị lỗi CVE mức HIGH, nhưng ứng dụng của bạn hoàn toàn không bao giờ gọi tới hàm đó (Vulnerable Code Not in Execute Path).

Thay vì tắt toàn bộ công cụ quét hoặc thêm mã CVE vào tệp .trivyignore thiếu ngữ cảnh kiểm toán, chuẩn công nghiệp năm 2026 sử dụng đặc tả OpenVEX (Vulnerability Exploitability eXchange). Tệp tài liệu .openvex.json cho phép kỹ sư bảo mật ghi nhận trạng thái not_affected kèm lý do kỹ thuật chuẩn hóa (vulnerable_code_not_in_execute_path hoặc inline_mitigations_already_exist). Khi chạy lệnh trivy image --vex security/payment-authorizer.openvex.json, Trivy sẽ tự động loại trừ các CVE đã được xác minh không thể khai thác mà vẫn giữ nguyên tính nghiêm ngặt cho mọi lỗ hổng mới phát sinh.

Dưới đây là cấu hình GitHub Actions hoàn chỉnh thực thi kiểm tra Dockerfile (trivy config), quét lỗ hổng ảnh + Secret (trivy image), xuất danh mục SBOM CycloneDX 1.6 và ký số không cần khóa tĩnh (Keyless OIDC Signing) bằng Sigstore Cosign:

# File: .github/workflows/container-secops-gate.yml
# Pipeline DevSecOps: Build -> Trivy Scan (CVE + Secret + OpenVEX) -> CycloneDX 1.6 SBOM -> Cosign OIDC Sign
name: Container Supply Chain Security Gate

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

permissions:
  contents: read
  packages: write
  security-events: write
  id-token: write # Bắt buộc để Sigstore Cosign xin cấp chứng chỉ ngắn hạn qua OIDC

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}/payment-authorizer

jobs:
  build-scan-sign:
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v4

      - name: 1. Quét lỗi cấu hình bảo mật trên Dockerfile & K8s Manifests (Trivy Config)
        uses: aquasecurity/[email protected]
        with:
          scan-type: "config"
          scan-ref: "."
          severity: "CRITICAL,HIGH"
          exit-code: "1"

      - name: 2. Đóng gói Container Image chuẩn OCI
        run: |
          docker build --tag "${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}" .

      - name: 3. Quét CVE & Secret Leak với bộ lọc OpenVEX (Chặn đứng Build nếu có lỗi chưa vá)
        uses: aquasecurity/[email protected]
        with:
          image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
          scanners: "vuln,secret,misconfig"
          severity: "CRITICAL,HIGH"
          ignore-unfixed: true
          vex: "security/payment-authorizer.openvex.json"
          format: "sarif"
          output: "trivy-results.sarif"
          exit-code: "1"

      - name: 4. Khởi tạo danh mục thành phần phần mềm SBOM chuẩn CycloneDX v1.6
        uses: aquasecurity/[email protected]
        with:
          image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
          format: "cyclonedx"
          output: "sbom-cyclonedx-1.6.json"

      - name: 5. Đẩy Image lên Registry và Ký số Keyless OIDC + Đính kèm SBOM bằng Cosign
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login "${REGISTRY}" -u "${{ github.actor }}" --password-stdin
          docker push "${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}"
          IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA}")

          # Ký số ảnh bằng danh tính OIDC của GitHub Actions (ghi nhật ký bất biến lên Rekor Transparency Log)
          cosign sign --yes "${IMAGE_DIGEST}"

          # Đính kèm chứng nhận SBOM CycloneDX 1.6 vào đúng mã băm SHA-256 của Image
          cosign attest --yes --predicate sbom-cyclonedx-1.6.json --type cyclonedx "${IMAGE_DIGEST}"


4. Triển khai Clair v4 / Harbor Quét Liên tục và Khởi tạo SBOM (CycloneDX 1.6) kèm Chữ ký Số Cosign

4.1. Cấu hình Clair v4.8+ (config.yaml) tích hợp cơ sở dữ liệu PostgreSQL 17 và Webhook cảnh báo lỗ hổng mới

Khi ảnh container đã vượt qua cổng CI/CD và được lưu trữ tại kho Registry nội bộ (như Red Hat Quay hoặc CNCF Harbor), Clair v4.8+ đảm nhận vai trò canh gác 24/7 trước các lỗ hổng Zero-Day hoặc các CVE mới được công bố sau thời điểm đóng gói ảnh.

Trong môi trường Production, Clair v4 nên được triển khai với xác thực token đối xứng/bất đối xứng (psk hoặc keyserver) giữa Registry và Clair API, sử dụng cơ sở dữ liệu PostgreSQL 17 tách biệt cho Indexer, Matcher và Notifier, đồng thời cấu hình chu kỳ đồng bộ dữ liệu CVE (updater_sets) mỗi 2 giờ. Dưới đây là tệp cấu hình /etc/clair/config.yaml chuẩn Production cho Clair v4.8+:

# File: /etc/clair/config.yaml
# Cấu hình triển khai Quay Clair v4.8+ ở chế độ Combo (Indexer + Matcher + Notifier) trên Production
http_listen_addr: "0.0.0.0:6060"
introspection_addr: "0.0.0.0:8089" # Cổng xuất Prometheus Metrics (/metrics) và Healthz (/healthz)
log_level: "info"

# 1. Cấu hình Dịch vụ Indexer: Bóc tách Layer và lưu trữ IndexReport vào PostgreSQL 17
indexer:
  connstring: "host=pg-clair-prod.infoteck.internal port=5432 dbname=clair_indexer user=clair_idx sslmode=verify-full"
  scanlock_retry: 10
  layer_scan_concurrency: 8
  migrations: true
  airgap: false

# 2. Cấu hình Dịch vụ Matcher: Đồng bộ nguồn dữ liệu CVE toàn cầu và đối chiếu tốc độ cao
matcher:
  connstring: "host=pg-clair-prod.infoteck.internal port=5432 dbname=clair_matcher user=clair_match sslmode=verify-full"
  max_conn_pool: 50
  migrations: true
  indexer_addr: "https://clair-indexer.infoteck.internal:6060"
  period: "2h0m0s" # Chu kỳ tải bản cập nhật cơ sở dữ liệu lỗ hổng mới
  disable_updaters: false
  updater_sets:
    - "rhel"
    - "debian"
    - "ubuntu"
    - "alpine"
    - "osv"      # Bao gồm Go, PyPI, npm, Rust Cargo, Maven từ cơ sở dữ liệu OSV
    - "wolfi"    # Nguồn dữ liệu bảo mật cho Chainguard Wolfi OS

# 3. Cấu hình Dịch vụ Notifier: Bắn Webhook tức thì sang SIEM/SecOps khi phát hiện CVE mới
notifier:
  connstring: "host=pg-clair-prod.infoteck.internal port=5432 dbname=clair_notifier user=clair_notify sslmode=verify-full"
  migrations: true
  indexer_addr: "https://clair-indexer.infoteck.internal:6060"
  matcher_addr: "https://clair-matcher.infoteck.internal:6060"
  poll_interval: "15m0s"
  delivery_interval: "1m0s"
  webhook:
    target: "https://secops-webhook.infoteck.internal/v1/alerts/clair-cve"
    callback: "https://clair.infoteck.internal:6060/notifier/api/v1/notifications/"
    headers:
      X-SecOps-Source: ["clair-v4-production"]

# 4. Xác thực API nội bộ giữa Container Registry (Quay/Harbor) và cụm Clair v4
auth:
  psk:
    key: "${CLAIR_REGISTRY_SHARED_BASE64_KEY}"
    iss:
      - "quay-enterprise-registry"
      - "harbor-core-scanner"

4.2. Chuỗi cung ứng chống giả mạo (SLSA Level 3): Xuất SBOM CycloneDX 1.6 và Ký số Keyless OIDC với Sigstore Cosign

Theo khung tiêu chuẩn bảo mật chuỗi cung ứng SLSA (Supply-chain Levels for Software Artifacts) v1.0 và sắc lệnh an ninh mạng quốc gia Hoa Kỳ, mọi gói phần mềm triển khai vào hạ tầng trọng yếu đều phải đi kèm một bảng kê khai nguyên vật liệu phần mềm (SBOM — Software Bill of Materials) ở định dạng máy đọc được (CycloneDX 1.6) và được ký số mật mã không thể chối bỏ.

Trước đây, việc ký container image bằng Docker Content Trust (Notary v1) rất phức tạp vì đội ngũ DevOps phải tự lưu trữ, bảo vệ và xoay vòng các khóa riêng dài hạn. Năm 2026, bộ công cụ Sigstore (Cosign + Fulcio CA + Rekor Transparency Log) đã cách mạng hóa quy trình này bằng cơ chế Keyless OIDC Signing:

  1. Khi job CI/CD trên GitHub Actions hoặc GitLab CI hoàn tất bước quét Trivy, cosign tạo một cặp khóa bất đối xứng tạm thời (Ephemeral Keypair) chỉ tồn tại trong bộ nhớ RAM và gửi kèm mã định danh OIDC Token của CI Runner tới tổ chức cấp phát chứng chỉ gốc Fulcio.
  2. Fulcio xác minh danh tính của workflow (đúng kho mã nguồn, đúng nhánh main, đúng commit SHA) và cấp một chứng chỉ X.509 có thời hạn sống chỉ 10 phút.
  3. cosign ký lên mã băm sha256 của Container Image cùng tệp đính kèm SBOM CycloneDX 1.6, ghi dấu thời gian xác thực vào sổ cái minh bạch công khai Rekor, sau đó xóa hủy ngay lập tức khóa riêng tạm thời trong RAM. Nhờ đó, không có bất kỳ khóa bí mật dài hạn nào có thể bị kẻ tấn công đánh cắp!

5. Bảo vệ Tầng Runtime Kubernetes v1.32+ với trivy-operator, Kyverno và SecurityContext Nghiêm ngặt

5.1. Chặn đứng Image chưa quét hoặc chưa ký bằng Kyverno Admission Controller (verifyImages)

Mọi công sức quét lỗ hổng trên CI/CD và Registry sẽ trở nên vô nghĩa nếu một quản trị viên vô tình (hoặc kẻ tấn công có quyền kubectl) chạy lệnh kubectl run debug --image=ubuntu:latest để kéo một ảnh chưa kiểm duyệt trực tiếp lên cụm Kubernetes Production — đặc biệt là trên các hạ tầng triển khai cụm Kubernetes High Availability trên Bare-Metal hoặc một nền tảng triển khai kiến trúc Zero Trust Security trong doanh nghiệp.

Để đóng sập cánh cửa này, chúng ta triển khai Kyverno Admission Controller ở chế độ Enforce. Sơ đồ tuần tự dưới đây minh họa toàn bộ vòng đời từ lúc kiểm duyệt trên CI/CD cho đến khi Kyverno xác minh chữ ký số và SBOM trước khi cho phép Kubelet khởi tạo Pod:

sequenceDiagram
    autonumber
    participant CIPipeline as GitHub Actions CI Runner
    participant TrivyCLI as Trivy v0.58+ Scanner
    participant OCIRegistry as Harbor / Quay Registry (Clair v4)
    participant CosignRekor as Sigstore Fulcio & Rekor Log
    participant KyvernoWebhook as K8s Kyverno Admission Webhook

    CIPipeline->>TrivyCLI: 1. Quét Dockerfile, CVE (Critical/High), Secret & Xuất SBOM CycloneDX 1.6
    Note over TrivyCLI: Áp dụng bộ lọc OpenVEX — Kết quả: 0 Critical/High Unfixed -> PASS
    TrivyCLI-->>CIPipeline: 2. Trả mã thoát 0 + Tệp sbom-cyclonedx-1.6.json
    CIPipeline->>OCIRegistry: 3. Push OCI Image và lấy mã băm bất biến (@sha256:...)
    CIPipeline->>CosignRekor: 4. Ký số Keyless OIDC lên Image Digest & Đính kèm SBOM Attestation
    CosignRekor-->>OCIRegistry: 5. Lưu trữ chữ ký (.sig) và chứng nhận SBOM (.att) cùng Image
    Note over OCIRegistry: Clair v4 Indexer giải nén Layer và giám sát CVE mới liên tục 24/7
    CIPipeline->>KyvernoWebhook: 6. Yêu cầu triển khai Pod (kubectl apply / ArgoCD Sync)
    KyvernoWebhook->>OCIRegistry: 7. Kiểm tra chữ ký Cosign OIDC & Xác minh tồn tại SBOM CycloneDX
    OCIRegistry-->>KyvernoWebhook: 8. Xác nhận Chữ ký & SBOM Hợp lệ -> Cho phép khởi tạo Pod

5.2. Giám sát liên tục trong cụm với trivy-operator và Khóa chặt Pod securityContext

Dưới đây là cấu hình hoàn chỉnh gồm hai phần: (1) Chính sách ClusterPolicy của Kyverno ép buộc mọi Pod trong các namespace sản xuất phải được ký bởi đúng workflow CI/CD của tổ chức kèm chứng nhận SBOM, và (2) Manifest Deployment áp dụng chuẩn Pod Security Standards (PSS) mức Restricted (runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], seccompProfile: RuntimeDefault):

# File: k8s-kyverno-cosign-and-hardened-deployment.yaml
# 1. Chính sách Kyverno Admission Controller: Chặn mọi Image chưa ký số Cosign hoặc thiếu SBOM
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: enforce-signed-and-scanned-images
spec:
  validationFailureAction: Enforce
  background: false
  webhookTimeoutSeconds: 15
  rules:
    - name: verify-cosign-oidc-signature-and-sbom
      match:
        any:
          - resources:
              kinds: ["Pod"]
              namespaces: ["checkout", "payments", "production"]
      verifyImages:
        - imageReferences:
            - "ghcr.io/infoteck-org/*"
          mutateDigest: true # Tự động chuyển đổi Tag thành mã băm @sha256 bất biến
          verifyDigest: true
          required: true
          attestors:
            - entries:
                - keyless:
                    issuer: "https://token.actions.githubusercontent.com"
                    subject: "https://github.com/infoteck-org/payment-service/.github/workflows/container-secops-gate.yml@refs/heads/main"
                    rekor:
                      url: "https://rekor.sigstore.dev"
          attestations:
            - type: "https://cyclonedx.org/bom"
              conditions:
                - all:
                    - key: "{{ bomFormat }}"
                      operator: Equals
                      value: "CycloneDX"
---
# 2. Deployment thực thi chuẩn bảo mật Pod Security Standards (PSS) mức Restricted
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-authorizer
  namespace: payments
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: payment-authorizer
  template:
    metadata:
      labels:
        app.kubernetes.io/name: payment-authorizer
    spec:
      automountServiceAccountToken: false # Tắt tự động gắn K8s Token nếu Pod không gọi K8s API
      securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        runAsGroup: 65532
        fsGroup: 65532
        seccompProfile:
          type: RuntimeDefault # Chặn các lời gọi hệ thống (syscalls) nguy hiểm của Linux Kernel
      containers:
        - name: authorizer
          image: ghcr.io/infoteck-org/payment-service/payment-authorizer@sha256:4b8e9a1c2d3f4e5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true # Ngăn chặn tuyệt đối việc ghi mã độc lên hệ thống tệp của Container
            capabilities:
              drop:
                - ALL
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "1000m"
              memory: "512Mi"
          volumeMounts:
            - name: tmp-scratch
              mountPath: /tmp
      volumes:
        - name: tmp-scratch
          emptyDir:
            medium: Memory
            sizeLimit: "64Mi"

Song song với Kyverno ở cổng vào, việc cài đặt trivy-operator trực tiếp vào cụm Kubernetes giúp đội ngũ SRE tự động hóa hoàn toàn việc kiểm toán các Pod đang chạy thông qua các Custom Resource Definitions (VulnerabilityReport, ConfigAuditReport, ExposedSecretReport, SbomReport) và xuất số liệu trực tiếp ra Prometheus:

#!/usr/bin/env bash
# File: install-and-query-trivy-operator.sh
# Triển khai trivy-operator trên Kubernetes v1.32+ và truy vấn báo cáo lỗ hổng thời gian thực
set -Eeuo pipefail

# 1. Cài đặt Aqua Security trivy-operator qua Helm với tính năng xuất Metrics cho Prometheus
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update

helm upgrade --install trivy-operator aqua/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --version 0.25.0 \
  --set="trivy.ignoreUnfixed=true" \
  --set="trivy.severity=CRITICAL\,HIGH" \
  --set="serviceMonitor.enabled=true" \
  --set="operator.sbomGenerationEnabled=true"

# 2. Kiểm tra tổng hợp số lượng CVE mức CRITICAL và HIGH của các Workload trong namespace 'payments'
kubectl get vulnerabilityreports -n payments -o wide

# 3. Trích xuất danh sách chi tiết các gói bị lỗi CRITICAL từ CRD VulnerabilityReport bằng jq
kubectl get vulnerabilityreports -n payments -o json \
  | jq -r '.items[] | .metadata.name as $rpt | .report.vulnerabilities[] | select(.severity=="CRITICAL") | "\($rpt): \(.vulnerabilityID) (\(.resource) installed=\(.installedVersion) fixed=\(.fixedVersion))"'


6. Checklist Kiểm toán Bảo mật Container DevSecOps & Tài liệu Tham khảo Chuẩn Kỹ thuật

6.1. Bảng Checklist 8 Bước Nghiệm thu Bảo mật Chuỗi cung ứng Container trước khi lên Production

Trước khi phê duyệt bất kỳ dịch vụ container hóa nào đi vào môi trường Production, kỹ sư DevSecOps cần đối chiếu danh sách kiểm tra 8 điểm sau:

  1. Loại bỏ Shell và Package Manager ở tầng Runtime: Đã chuyển sang sử dụng Multi-Stage Build với ảnh đích là Distroless (static/base-debian12:nonroot) hoặc Chainguard Wolfi chưa?
  2. Ghim chặt Image Digest (@sha256:...): Mọi chỉ thị FROM trong Dockerfile và trường image: trong Kubernetes Manifest đã được khóa bằng mã băm SHA-256 thay vì dùng nhãn động :latest chưa?
  3. Cổng chặn Trivy trên CI/CD (exit-code: 1): Đường ống CI/CD đã cấu hình chặn tự động các bản build chứa lỗ hổng CRITICAL/HIGH đã có bản vá (--ignore-unfixed) và rò rỉ khóa bí mật (--scanners vuln,secret,misconfig) chưa?
  4. Quản lý cảnh báo giả minh bạch với OpenVEX: Mọi ngoại lệ CVE đã được ghi nhận có thời hạn và lý do kỹ thuật rõ ràng trong tệp .openvex.json thay vì bỏ qua mù quáng chưa?
  5. Khởi tạo và lưu trữ SBOM CycloneDX 1.6: Mỗi bản phát hành đã đính kèm tệp danh mục thành phần phần mềm chuẩn CycloneDX 1.6 để sẵn sàng truy vết trong 60 giây khi có cảnh báo Zero-Day mới chưa?
  6. Quét liên tục tại Registry bằng Clair v4: Kho lưu trữ ảnh nội bộ (Quay/Harbor) đã bật trình đối chiếu liên tục của Clair v4.8+ kết nối Webhook cảnh báo về SIEM chưa?
  7. Ký số Cosign OIDC và Xác minh tại Kyverno: Cụm Kubernetes đã bật ClusterPolicy của Kyverno/Connaisseur để từ chối mọi container image không có chữ ký số hợp lệ từ đường ống CI/CD chưa?
  8. Thực thi Pod Security Standards Restricted: Toàn bộ Pod đã bật runAsNonRoot: true (UID 65532), readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, drop: ["ALL"] và seccompProfile: RuntimeDefault chưa?

6.2. Chiến lược Xử lý Khẩn cấp khi Phát hiện Lỗ hổng Zero-Day trên Môi trường Production

Khi hệ thống Notifier của Clair v4 hoặc trivy-operator phát cảnh báo về một lỗ hổng Zero-Day mức CRITICAL (điểm CVSS v4.0 $\ge 9.0$) vừa xuất hiện trên một thư viện đang chạy trong cụm:

  • Bước 1 (Trong 15 phút đầu): Truy vấn ngay các tài nguyên SbomReport và VulnerabilityReport trên cụm Kubernetes (hoặc cơ sở dữ liệu IndexReport của Clair v4) để lập danh sách chính xác 100% các Pod và Namespace chứa phiên bản thư viện bị ảnh hưởng mà không cần phải quét lại từ đầu.
  • Bước 2 (Trong 30 phút tiếp theo): Đánh giá khả năng khai thác thực tế. Nếu hàm lỗi không nằm trên đường dẫn thực thi hoặc đã bị chặn bởi readOnlyRootFilesystem và CiliumNetworkPolicy, phát hành ngay bản cập nhật OpenVEX tạm thời trong khi chờ bản vá chính thức. Nếu lỗ hổng có thể bị khai thác từ xa và đã có bản vá thượng nguồn, kích hoạt pipeline CI/CD xây dựng lại ảnh mới, ký số Cosign và cuộn bản cập nhật (Rolling Update) qua ArgoCD.

6.3. Tài liệu Tham khảo & Nguồn Chuẩn Kỹ thuật (Primary Technical References)

Các cấu hình, thông số kiến trúc và tiêu chuẩn bảo mật container trong bài viết này được trích dẫn và kiểm chứng từ các nguồn tài liệu gốc dưới đây:

  1. Aqua Security Trivy Official Documentation — Comprehensive Container, Secret, IaC & SBOM Vulnerability Scanner (v0.58+)
  2. Quay Clair v4 Official Documentation — Enterprise Container Vulnerability Indexing & Matching Architecture (Indexer, Matcher, Notifier)
  3. NIST Special Publication 800-190 — Application Container Security Guide (National Institute of Standards and Technology)
  4. OWASP Docker Security Cheat Sheet — Container Hardening, Non-Root Execution & Capability Dropping
  5. OWASP CycloneDX v1.6 Specification — Standard for Software Bill of Materials (SBOM) & Vulnerability Exploitability
  6. Sigstore Cosign Official Documentation — Keyless OIDC Container Signing, Fulcio CA & Rekor Transparency Log
  7. Kyverno Official Documentation — Kubernetes Admission Control & Container Image Signature Verification (verifyImages)