Attestation: Cơ Chế Ký Số Mật Mã và Artifact Analysis
Tại sao quan trọng trong production
Attestation là trái tim của Binary Authorization. Nhưng trong thực tế production, người ta thường chỉ biết "tạo attestation bằng gcloud rồi deploy" mà không hiểu:
- Attestation thực sự là gì về mặt kỹ thuật (một JSON document với chữ ký mật mã)
- Signature binding — attestation ký trên cái gì và tại sao phải là digest chứ không phải tag
- Verification path — tại deploy time, enforcer tìm attestation ở đâu và verify như thế nào
- Failure modes — tại sao attestation có thể fail dù được tạo đúng quy trình
Hiểu cơ chế này giúp debug tại sao policy reject một image dù CI đã "sign" nó, và thiết kế pipeline đúng từ đầu.
Internal Model: Anatomy của Một Attestation
Attestation Là Một Artifact Analysis Occurrence
Về mặt kỹ thuật, attestation là một occurrence trong Artifact Analysis (Cloud Container Analysis API). Artifact Analysis dùng mô hình note/occurrence:
Note: "loại" metadata (định nghĩa một attestor)
└── Occurrence: instance cụ thể (attestation cho một image cụ thể)Một occurrence cho attestation có structure:
{
"resourceUri": "https://gcr.io/my-project/my-app@sha256:abc123...",
"noteName": "projects/attestor-project/notes/qa-approval-note",
"kind": "ATTESTATION",
"attestation": {
"serializedPayload": "<base64-encoded-payload>",
"signatures": [
{
"signature": "<base64-encoded-signature>",
"publicKeyId": "projects/.../cryptoKeyVersions/1"
}
]
}
}Giải mã từng trường:
resourceUri: URI của image, bắt buộc phải dùng digest (@sha256:...), không thể dùng tag. Đây là key để Binary Authorization lookup attestation — nó query bằng digest của image đang được deploy.
noteName: Trỏ đến Artifact Analysis note của attestor tương ứng. Binary Authorization biết note nào để query dựa vào cấu hình của attestor trong policy.
serializedPayload: Payload được ký, encode base64. Khi decode, đây là một JSON document theo format Binary Authorization.
signatures: Mảng chữ ký. Mỗi signature là chữ ký mật mã của serializedPayload và ID của public key tương ứng.
Payload Format: Cái Gì Được Ký
Payload (trước khi base64 encode) có format:
{
"critical": {
"type": "Google cloud binauthz container signature",
"image": {
"docker-manifest-digest": "sha256:abc123def456..."
},
"identity": {
"docker-reference": "gcr.io/my-project/my-app"
}
}
}docker-manifest-digest: SHA256 digest của image manifest. Đây là định danh bất biến của image — không thể bị thay thế mà không thay đổi digest.
docker-reference: Registry path của image (không có digest). Đây là context — biết image này đến từ registry nào.
Tại sao thiết kế như vậy? Payload binding cả digest (để uniquely identify image content) lẫn registry path (để context). Nếu chỉ ký digest, một image từ untrusted registry có thể có cùng digest với image trusted (collision attack, dù cực kỳ khó trong thực tế). Kết hợp registry path + digest tạo ra binding mạnh hơn.
Cryptographic Signing: Cơ Chế Bên Trong
PKIX Signing với Cloud KMS
Đây là flow signing khi dùng Cloud KMS (khuyến nghị cho production):
1. Tạo payload JSON
2. Base64-encode payload
3. Gọi Cloud KMS API để ký:
- Input: base64-encoded payload (hoặc hash của nó)
- KMS signing key: ECDSA P-256
- Output: ECDSA signature bytes
4. Base64-encode signature
5. Tạo Artifact Analysis occurrence với payload + signatureCode flow khi dùng gcloud (underlying steps):
# Tạo payload
PAYLOAD_PATH=/tmp/generated_payload.json
IMAGE_DIGEST="sha256:abc123..."
IMAGE_PATH="gcr.io/my-project/my-app"
cat > ${PAYLOAD_PATH} << EOF
{
"critical": {
"type": "Google cloud binauthz container signature",
"image": {
"docker-manifest-digest": "${IMAGE_DIGEST}"
},
"identity": {
"docker-reference": "${IMAGE_PATH}"
}
}
}
EOF
# Sign payload với Cloud KMS
gcloud kms asymmetric-sign \
--location=us-central1 \
--keyring=binauthz-keyring \
--key=signing-key \
--version=1 \
--digest-algorithm=sha256 \
--input-file=${PAYLOAD_PATH} \
--signature-file=/tmp/signature.bin
# Tạo attestation
gcloud container binauthz attestations create \
--project=${ATTESTATION_PROJECT} \
--artifact-url="${IMAGE_PATH}@${IMAGE_DIGEST}" \
--attestor=projects/${ATTESTOR_PROJECT}/attestors/${ATTESTOR_ID} \
--signature-file=/tmp/signature.bin \
--public-key-id="${KEY_URI}"Key ID trong occurrence: publicKeyId trong occurrence phải khớp với ID của một public key được đăng ký trong attestor. Binary Authorization dùng publicKeyId để biết dùng public key nào để verify. Nếu không khớp → verification fail ngay cả khi signature về mặt mật mã là valid.
PKIX vs PGP: Sự Khác Biệt Bản Chất
PKIX (X.509):
- Key được identify bằng X.509 certificate hoặc raw public key PEM
- Tích hợp native với Cloud KMS
- Public key ID là
projects/.../cryptoKeyVersions/N - Là lựa chọn khuyến nghị cho mọi implementation mới
PGP:
- Dùng fingerprint của PGP key làm ID
- Không tích hợp với Cloud KMS — key phải lưu locally hoặc trong secret manager
- Legacy format, Binary Authorization vẫn support nhưng không phát triển thêm
- Không phù hợp cho automated CI/CD vì cần PGP key management riêng
Tại sao PKIX được ưu tiên? Cloud KMS cung cấp:
- Hardware-backed key storage (HSM option)
- Audit logging cho mọi signing operation
- Key rotation và versioning
- IAM control ai có thể dùng key để sign
- Không cần expose private key ra ngoài Google infrastructure
Artifact Analysis: Storage Layer Cho Attestation
Note/Occurrence Model: Tại Sao Artifact Analysis?
Artifact Analysis (Container Analysis) ban đầu được thiết kế để lưu vulnerability findings. Binary Authorization tái sử dụng infrastructure này vì:
- Immutable storage: Occurrences không thể thay đổi sau khi tạo (chỉ có thể tạo mới hoặc xóa)
- Content-addressable lookup: Query theo image digest, không theo tag
- IAM integration: Ai có thể đọc/ghi occurrences được kiểm soát bởi IAM
- VPC Service Controls: Có thể đưa vào perimeter để chống exfiltration
- Audit trail: Mọi thao tác đều được log trong Cloud Audit Logs
Cấu Trúc Lưu Trữ và Query Path
Khi Binary Authorization enforcer cần verify attestation cho một image:
1. Enforcer nhận image digest từ Pod spec
2. Query Artifact Analysis:
GET /v1/projects/{ATTESTATION_PROJECT}/occurrences?filter=
resourceUrl="https://{IMAGE_PATH}@sha256:{DIGEST}"
AND noteProjectId="{NOTE_PROJECT}"
AND noteId="{NOTE_ID}"
AND kind="ATTESTATION"
3. Nhận danh sách occurrences
4. Với mỗi occurrence:
- Lấy signature và publicKeyId
- Tìm public key trong attestor matching publicKeyId
- Verify: ECDSA.verify(payload, signature, publicKey)
5. Nếu ít nhất một signature verify thành công → attestor này satisfied
6. Lặp lại cho tất cả attestors trong policy ruleTại sao query theo note? Mỗi attestor gắn với đúng một note. Note là "category" của attestation. Khi query, Binary Authorization lọc chỉ occurrences thuộc note của attestor đang được verify — tránh nhầm lẫn attestation từ attestor khác.
Cross-Project Attestation Lookup
Trong multi-project setup:
Deployment Project → Policy → Attestors (Attestor Project)
↓ (note reference)
Artifact Analysis (Note Project)
↓ (occurrence)
Attestations (Attestation Project)Binary Authorization service account cần có permission để:
- Đọc attestors từ attestor project
- Đọc notes từ note project
- Đọc occurrences từ attestation project
IAM roles:
# Grant Binary Authorization service account read access
BINAUTHZ_SA="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding ${ATTESTATION_PROJECT} \
--member="serviceAccount:${BINAUTHZ_SA}" \
--role="roles/containeranalysis.occurrences.viewer"
gcloud projects add-iam-policy-binding ${ATTESTOR_PROJECT} \
--member="serviceAccount:${BINAUTHZ_SA}" \
--role="roles/binaryauthorization.attestorsViewer"Cloud Build Integration: Automated Attestation
Cloud Build Provenance
Cloud Build tự động tạo build provenance — một dạng attestation đặc biệt được tạo bởi infrastructure Cloud Build, không thể bị giả mạo bởi user code trong build pipeline.
Cloud Build provenance chứa:
- Source code repository và commit SHA
- Build configuration (cloudbuild.yaml hoặc trigger config)
- Build environment (Cloud Build worker)
- Output artifacts (image digests)
- Timestamps
Khác với user-created attestation, Cloud Build provenance được ký bởi Google — không phải bởi user's key. Điều này có nghĩa:
# Attestor cho Cloud Build provenance không cần user key
# Đây là attestor đặc biệt "built-by-cloud-build"
gcloud container binauthz attestors create built-by-cloud-build \
--attestation-authority-note=projects/${PROJECT_ID}/notes/built-by-cloud-buildBinary Authorization có thể verify không chỉ "image được build" mà cả "image được build từ source code ở đây, với config này, trong Cloud Build environment" — không thể giả mạo bằng cách submit image từ local.
SLSA Provenance
SLSA (Supply chain Levels for Software Artifacts) là framework cao hơn. Cloud Build tạo SLSA provenance theo SLSA level 1 (và hướng đến level 3). Continuous Validation có thể verify SLSA provenance qua SLSA check (xem Chương 34.04).
SLSA provenance structure (ví dụ):
{
"builder": {
"id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2"
},
"buildType": "https://cloudbuild.googleapis.com/CloudBuildYaml@v0.1",
"invocation": {
"configSource": {
"uri": "https://github.com/my-org/my-repo",
"digest": {"sha1": "commitSHA"},
"entryPoint": "cloudbuild.yaml"
}
},
"metadata": {
"buildStartedOn": "2024-01-15T10:00:00Z",
"buildFinishedOn": "2024-01-15T10:05:00Z"
},
"materials": [
{
"uri": "https://github.com/my-org/my-repo",
"digest": {"sha1": "commitSHA"}
}
]
}Constraints, Trade-offs và Failure Modes
Timing Issue: Attestation Propagation Lag
Artifact Analysis có eventual consistency. Sau khi tạo occurrence, có thể mất từ vài giây đến 1-2 phút để Binary Authorization enforcer thấy nó. Nếu CI pipeline tạo attestation xong ngay lập tức trigger deploy, có thể xảy ra:
T=0: CI tạo attestation occurrence
T=1: CI trigger kubectl apply
T=2: Binary Authorization enforcer query Artifact Analysis
→ Occurrence chưa visible
→ Deploy bị BLOCK dù attestation hợp lệGiải pháp: Thêm sleep 30 hoặc implement retry với exponential backoff trong CI pipeline sau khi tạo attestation. Alternative: dùng gcloud container binauthz attestations list để verify attestation visible trước khi deploy.
Attestation Scope Confusion: Tag vs Digest
Đây là lỗi cực kỳ phổ biến. Attestation gắn với digest của image, không phải tag. Nhưng khi deploy, nếu Pod spec dùng tag (image: gcr.io/my-project/app:v1.2.3), có hai vấn đề:
Vấn đề 1: Binary Authorization phải resolve tag sang digest Binary Authorization phải gọi Container Registry/Artifact Registry API để resolve tag → digest trước khi lookup attestation. Nếu registry không accessible hoặc tag bị move, lookup fail.
Vấn đề 2: Tag là mutable Giữa thời điểm CI tạo attestation (với digest A) và thời điểm deploy (resolve tag → digest), tag có thể đã bị repoint sang image khác (digest B). Binary Authorization có thể allow deploy digest B vì nó không kiểm tra lại attestation sau khi resolve — nó dùng digest vừa resolve để lookup.
Mental model đúng: Attestation là về content (digest), không phải label (tag). Một tag là một con trỏ mutable. Attaching trust to a mutable pointer là security anti-pattern.
Giải pháp duy nhất thực sự an toàn: Pin digest trong manifest:
containers:
- name: app
# KHÔNG an toàn:
image: gcr.io/my-project/app:v1.2.3
# AN TOÀN:
image: gcr.io/my-project/app@sha256:abc123def456789...Key Rotation: Attestations Cũ Và Key Mới
Khi rotate signing key, attestations cũ (được ký bằng key cũ) vẫn cần verify được. Nếu xóa public key cũ khỏi attestor, tất cả deployments của image cũ sẽ fail.
Pattern đúng cho key rotation:
- Tạo key version mới (Cloud KMS tự increment version)
- Thêm public key của version mới vào attestor (attestor có thể có nhiều public keys)
- Bắt đầu ký attestation mới với key version mới
- Để key cũ còn trong attestor cho đến khi tất cả images được ký với key cũ đã được re-signed hoặc không còn trong production
- Sau đó disable/remove key cũ
# Thêm key version mới mà không xóa cũ
gcloud container binauthz attestors public-keys add \
--attestor=${ATTESTOR_ID} \
--keyversion="projects/${PROJECT_ID}/locations/us-central1/keyRings/binauthz-keyring/cryptoKeys/signing-key/cryptoKeyVersions/2"Attestation Replay Attack
Một attestation đã tạo cho image X có thể bị "replay" nếu attacker tìm cách tạo image X với digest giống hệt nhưng ở registry khác. Trên thực tế cực kỳ khó (SHA-256 collision resistance), nhưng cần hiểu:
- Attestation bind với
docker-manifest-digest+docker-reference(registry path) - Nếu policy allowlist quá rộng (wildcard registry), image từ registry khác với cùng digest có thể pass
Phòng ngừa: Dùng Artifact Registry (không phải Docker Hub hay registry external) và cấu hình allowlist chặt chẽ.
GCP-native Implementation: Tạo Attestation Trong CI/CD
Với Cloud Build
# cloudbuild.yaml
steps:
# Bước 1: Build image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$SHORT_SHA', '.']
# Bước 2: Push image
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/my-app:$SHORT_SHA']
# Bước 3: Lấy digest
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker inspect --format='{{index .RepoDigests 0}}' \
gcr.io/$PROJECT_ID/my-app:$SHORT_SHA \
| cut -d@ -f2 > /workspace/image-digest.txt
# Bước 4: Tạo attestation
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: 'bash'
args:
- '-c'
- |
IMAGE_DIGEST=$(cat /workspace/image-digest.txt)
IMAGE_PATH="gcr.io/$PROJECT_ID/my-app"
gcloud container binauthz attestations sign-and-create \
--artifact-url="${IMAGE_PATH}@${IMAGE_DIGEST}" \
--attestor="projects/$PROJECT_ID/attestors/qa-attestor" \
--keyversion="projects/$PROJECT_ID/locations/us-central1/keyRings/binauthz-keyring/cryptoKeys/signing-key/cryptoKeyVersions/1"
# Wait for propagation
sleep 30Lưu ý sign-and-create — đây là gcloud command tổng hợp thực hiện cả signing và tạo occurrence trong một bước.
Verify Attestation Tồn Tại
# Kiểm tra attestation có được tạo đúng không
IMAGE_WITH_DIGEST="gcr.io/my-project/my-app@sha256:abc123..."
gcloud container binauthz attestations list \
--artifact-url="${IMAGE_WITH_DIGEST}" \
--attestor=qa-attestor \
--attestor-project=my-attestor-project
# Output kỳ vọng: dòng listing occurrence ID, creation time
# Nếu empty → attestation chưa được tạo hoặc chưa propagateTài liệu tham khảo
- Attestations Overview — workflow tổng thể
- Create Attestations — hướng dẫn tạo attestation
- Cloud Build Integration — CI/CD pipeline
- Artifact Analysis Overview — storage layer
- Artifact Analysis Metadata Storage — note/occurrence model
- Deploy only images built by Cloud Build — Cloud Build provenance attestation
- Using Container Image Digests — tại sao digest quan trọng