Có một CVE mới trong urllib3. Câu hỏi đầu tiên là: ảnh nào của chúng ta có nó, và phiên bản nào?

Không có SBOM thì cách trả lời là chạy từng ảnh lên rồi pip list — mà nhiều ảnh không có shell, và ảnh cũ có thể đã không còn tag. Có SBOM thì đó là một lệnh đọc metadata.

Bật lên

docker buildx build --sbom=true --provenance=mode=max --tag registry/app:v1 --push .

Kết quả là một index có thêm một manifest đặc biệt:

linux/arm64      anh        manifest  2002 byte
unknown/unknown  dinh kem   manifest  1112 byte

Mục unknown/unknown là chỗ chứa SBOM và provenance — cùng chỗ mà phần 20 đã thấy khi dựng ảnh đa kiến trúc.

Chi phí

Dung lượng
Layer của ảnh 51,97 MB
SBOM + provenance 2 117,9 KB

Khoảng 3,98%. Với ảnh nhỏ hơn thì tỉ lệ cao hơn, vì SBOM tỉ lệ với số gói chứ không tỉ lệ với dung lượng ảnh.

Về thời gian build, tôi không đo được con số sạch: hai lần build của tôi chia sẻ cache và blob nên so sánh trực tiếp không có nghĩa. Phần thêm vào là một lần quét filesystem của ảnh bằng buildkit-syft-scanner, chạy song song với các bước khác.

Trong SBOM có gì

dinh dang: SPDX-2.3 | 130 thanh phan
  goi he dieu hanh (deb)     109
  goi Python (pypi)           13
  khac                         8

Phần Python đầy đủ, kể cả phụ thuộc bắc cầu:

blinker 1.9.0          markupsafe 3.0.3
certifi 2026.7.22      pip 25.0.1
charset-normalizer 3.5.1   requests 2.32.3
click 8.4.2            urllib3 2.7.0
flask 3.0.3            werkzeug 3.1.8
idna 3.19              itsdangerous 2.2.0
jinja2 3.1.6

requirements.txt của tôi chỉ có hai dòng: flaskrequests. SBOM liệt kê mười ba — mười một cái còn lại là phụ thuộc bắc cầu mà bạn không viết ra nhưng vẫn phải chịu trách nhiệm khi chúng có lỗ hổng.

Và trả lời câu hỏi ở đầu bài:

cau hoi: "anh nao dung urllib3?" -> co, phien ban 2.7.0
purl   : cpe:2.3:a:python:urllib3:2.7.0:*:*:*:*:*:*:*

Một lệnh, không cần chạy container, không cần đọc Dockerfile, không cần đoán.

Định danh purl/cpe cũng quan trọng: đó là dạng chuẩn để công cụ quét đối chiếu với cơ sở dữ liệu CVE. SBOM không nói cho bạn biết cái gì có lỗ hổng — nó nói cho bạn biết có gì, rồi công cụ khác đối chiếu.

Trong provenance có gì

Provenance theo chuẩn SLSA ghi lại ảnh được dựng như thế nào:

buildType         : https://github.com/moby/buildkit/.../slsa-definitions
builderPlatform   : linux/arm64
dockerfileVersion : 1.26.0
startedOn         : 2026-08-25T07:25:31.116342379Z
finishedOn        : 2026-08-25T07:25:47.081647053Z
invocationId      : v9lrrad4frp9aafib20gvykvl

Và phần giá trị nhất — nguyên liệu đầu vào kèm digest:

pkg:docker/python@3.12-slim?platform=linux/arm64
  sha256:3ecf5ebe01fef4b6e81be345...
pkg:docker/docker/buildkit-syft-scanner@stable-1
  sha256:ae4f3b554449e7e25548e7d8...

Đây là câu trả lời cho một vấn đề đã nêu ở phần 4: FROM python:3.12-slim hôm nay và sáu tháng sau là hai nội dung khác nhau, vì tag có thể bị đẩy đè. Provenance ghi lại chính xác digest đã được dùng, nên bạn dựng lại được đúng thứ cũ kể cả khi tag đã trỏ đi nơi khác.

Cùng với externalParameters:

{"configSource": {"path": "Dockerfile"},
 "request": {"frontend": "dockerfile.v0", "locals": [{"name": "context"}]}}

Ba mức provenance

Ghi gì
mode=min (mặc định khi push) thông tin cơ bản: thời gian, nền tảng, digest nguyên liệu
mode=max thêm nội dung Dockerfile, tham số build, nguồn
--provenance=false tắt hẳn

mode=max ghi cả nội dung Dockerfile vào attestation. Tiện cho việc truy vết, nhưng cần nhớ: nếu Dockerfile của bạn chứa thứ gì nhạy cảm thì nó đi cùng ảnh. Không phải bí mật — như phần 21 đã đo, --mount=type=secret vẫn sạch — nhưng URL nội bộ, tên máy chủ, cấu trúc hạ tầng thì có.

Khi nào tắt đi

BuildKit bật provenance mặc định khi --push, và một số registry cũ không hiểu định dạng index có attestation. Triệu chứng là lỗi khó hiểu lúc đẩy, hoặc ảnh đẩy lên nhưng công cụ khác không kéo được.

docker buildx build --provenance=false --sbom=false ...

Ngoài ra, nếu bạn dùng --load vào daemon cục bộ thì attestation không có tác dụng gì — nó chỉ có ý nghĩa khi ảnh nằm trong registry.

Dùng thật thì làm gì với nó

SBOM chỉ có giá trị khi có ai đó đọc. Ba việc thực tế:

Trả lời câu hỏi "ai bị ảnh hưởng". Lưu SBOM của mọi ảnh đã phát hành vào một chỗ tìm kiếm được. Khi có CVE, bạn grep thay vì điều tra.

Đối chiếu với cơ sở dữ liệu lỗ hổng. grypetrivy đọc SBOM trực tiếp, nhanh hơn nhiều so với quét lại cả ảnh:

docker buildx imagetools inspect registry/app:v1 --format '{{ json .SBOM }}' > sbom.json
grype sbom:sbom.json

Ghim lại nguyên liệu. Lấy digest image nền từ provenance rồi ghi thẳng vào Dockerfile, để lần build sau cho ra đúng thứ cũ.

phần 10 tôi không quét được CVE vì docker scout đòi đăng nhập tài khoản Docker. Với SBOM thì grypetrivy làm được mà không cần tài khoản nào — đó là lý do thực tế nhất để bật nó.

Thử ba mươi giây

Xem ảnh của bạn có SBOM chưa:

docker buildx imagetools inspect <anh> --format '{{ json .SBOM }}' | head -c 200

Rỗng nghĩa là chưa có. Dựng lại với --sbom=true rồi đếm thành phần:

docker buildx imagetools inspect <anh> --format '{{ json .SBOM }}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); s=d.get('SPDX') or list(d.values())[0]['SPDX']; print(len(s['packages']), 'thanh phan')"

Con số đó thường lớn hơn nhiều so với số dòng trong requirements.txt hay package.json của bạn — và chênh lệch chính là phần bạn không biết mình đang chạy.

Phần sau đo việc chia tầng để build song song, và chỗ nào tách tầng thật sự có tác dụng.