GKE Binary Authorization - deployment odrzucony przez brak poświadczenia (attestation)
Diagnoza i naprawa odrzuconego deployment w GKE spowodowanego przez Binary Authorization: brak poświadczenia, niezgodność klucza attestor, użycie tag zamiast digest, brak anotacji break-glass.
denied by attestor lub Denied by default admission rule. No attestations found. Pipeline CI/CD jest zablokowany, a kubectl apply zwraca odmowę. Obraz jest gotowy w rejestrze, ale Binary Authorization nie przepuszcza deployment - żadne poświadczenie (attestation) nie potwierdza, że obraz przeszedł wymaganą weryfikację bezpieczeństwa.
Ten runbook opisuje diagnozę i naprawę problemu, w którym GKE Binary Authorization blokuje deployment z powodu brakującego lub niepoprawnego poświadczenia obrazu. Szczegółowe omówienie architektury GCP zgodnej z PCI DSS i roli Binary Authorization w zabezpieczaniu supply chain znajdziesz w artykule Infrastruktura GCP dla fintechu - PCI DSS, SOC2 i Google Cloud.
Objaw
Deployment na klaster GKE z włączoną polityką Binary Authorization jest odrzucany. Błąd pojawia się w output kubectl apply lub w logach pipeline CI/CD:
# kubectl apply zwraca błąd admission webhook
kubectl apply -f deployment.yaml
# Error from server (VIOLATES_POLICY): error when creating "deployment.yaml":
# admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request:
# Image gcr.io/my-project/my-app@sha256:abc123... denied by attestor
# projects/my-project/attestors/build-attestor: No attestations found
# Alternatywny komunikat przy default deny rule
kubectl apply -f deployment.yaml
# Error from server: error when creating "deployment.yaml":
# admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request:
# Denied by default admission rule. No attestations found that were valid
# and signed by a key trusted by the attestor
# Sprawdzenie statusu deployment
kubectl get events --sort-by='.lastTimestamp' | grep -i denied
# LAST SEEN TYPE REASON OBJECT MESSAGE
# 2m Warning Denied pod/my-app-7b9f4d-xk2lm Denied by BinAuthz
# Logi Admission Controller
kubectl logs -n kube-system -l app=binauthz-admission-controller --tail=20
# "msg":"image policy webhook denied","image":"gcr.io/my-project/my-app:v1.2.3",
# "reason":"no valid attestation found for image"
Problem dotyczy wyłącznie klastrów z aktywną polityką Binary Authorization. Obraz jest prawidłowo zbudowany i dostępny w rejestrze - blokada następuje na etapie admission control.
Przyczyna
Binary Authorization odrzuca deployment z czterech głównych powodów:
-
Obraz nie jest podpisany/zaatestowany przez wymagany attestor. Pipeline CI/CD nie wykonał kroku poświadczenia po zbudowaniu obrazu, lub krok poświadczenia nie powiódł się cicho (silent failure). Jest to najczęstsza przyczyna - szczególnie po dodaniu nowego attestor do polityki bez aktualizacji pipeline.
-
Niezgodny klucz lub wersja klucza attestor. Attestor jest skonfigurowany z kluczem KMS, który został zrotowany lub usunięty. Poświadczenie zostało podpisane starą wersją klucza, a polityka wymaga aktualnej wersji. Zdarza się po planowej rotacji kluczy KMS bez aktualizacji istniejących poświadczeń.
-
Niezgodność digest obrazu - użyto tag zamiast digest. Binary Authorization operuje na immutable digest (sha256). Jeśli deployment odwołuje się do obrazu przez tag (np.
:latest,:v1.2.3), a poświadczenie zostało utworzone dla konkretnego digest - weryfikacja nie przejdzie, bo tag mógł zostać nadpisany. -
Brak break-glass annotation dla wdrożeń awaryjnych. W sytuacji awaryjnej (hotfix produkcyjny, krytyczna podatność) deployment musi zawierać specjalną anotację, która pozwala ominąć politykę Binary Authorization. Bez niej nawet pilne poprawki są blokowane.
Rozwiązanie
Krok 1: Sprawdź aktywną politykę Binary Authorization na klastrze
# Wyświetl politykę Binary Authorization dla projektu
gcloud container binauthz policy export --project=my-project
# Przykładowy output:
# admissionWhitelistPatterns:
# - namePattern: gcr.io/google_containers/*
# - namePattern: gcr.io/google-containers/*
# defaultAdmissionRule:
# enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
# evaluationMode: REQUIRE_ATTESTATION
# requireAttestationsBy:
# - projects/my-project/attestors/build-attestor
# - projects/my-project/attestors/security-scan-attestor
# clusterAdmissionRules:
# europe-west1-b.production-cluster:
# enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
# evaluationMode: REQUIRE_ATTESTATION
# requireAttestationsBy:
# - projects/my-project/attestors/build-attestor
# - projects/my-project/attestors/security-scan-attestor
# - projects/my-project/attestors/qa-attestor
# Sprawdź konfigurację attestor
gcloud container binauthz attestors describe build-attestor \
--project=my-project \
--format="yaml(name, userOwnedGrafeasNote, publicKeys)"
Krok 2: Zweryfikuj czy poświadczenie istnieje dla danego obrazu
# Pobierz pełny digest obrazu z rejestru
gcloud container images describe gcr.io/my-project/my-app:v1.2.3 \
--format="value(image_summary.digest)"
# Output: sha256:a1b2c3d4e5f6...
# Wylistuj atestacje dla tego digest
gcloud container binauthz attestations list \
--project=my-project \
--attestor=build-attestor \
--attestor-project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6..."
# Jeśli lista jest pusta - poświadczenie nie istnieje
# Jeśli poświadczenie istnieje ale deployment jest odrzucany - sprawdź klucz
# Sprawdź atestacje od security-scan-attestor (jeśli polityka wymaga obu)
gcloud container binauthz attestations list \
--project=my-project \
--attestor=security-scan-attestor \
--attestor-project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6..."
Krok 3: Utwórz brakujące poświadczenie
# Pobierz identyfikator klucza publicznego attestor
gcloud container binauthz attestors describe build-attestor \
--project=my-project \
--format="json(userOwnedGrafeasNote.publicKeys)"
# Utwórz atestację przy użyciu klucza KMS
gcloud container binauthz attestations sign-and-create \
--project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6..." \
--attestor=build-attestor \
--attestor-project=my-project \
--keyversion-project=my-project \
--keyversion-location=europe-west1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=1
# Potwierdź utworzenie atestacji
gcloud container binauthz attestations list \
--project=my-project \
--attestor=build-attestor \
--attestor-project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6..."
# Expected: atestacja widoczna na liście
Krok 4: Napraw odwołania do obrazów - użyj digest zamiast tag
# ZŁE - deployment odwołuje się do tag
# Tag może wskazywać na inny digest niż zaatestowany
spec:
containers:
- name: my-app
image: gcr.io/my-project/my-app:v1.2.3
# DOBRZE - deployment odwołuje się do immutable digest
spec:
containers:
- name: my-app
image: gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6...
# Automatyczna zamiana tag na digest w manifeście
IMAGE_DIGEST=$(gcloud container images describe \
gcr.io/my-project/my-app:v1.2.3 \
--format="value(image_summary.fully_qualified_digest)")
sed -i "s|gcr.io/my-project/my-app:v1.2.3|${IMAGE_DIGEST}|g" deployment.yaml
# Zastosuj poprawiony manifest
kubectl apply -f deployment.yaml
Krok 5: Wdrożenie awaryjne z break-glass (tylko sytuacje krytyczne)
# Break-glass annotation pozwala ominąć Binary Authorization
# UWAGA: generuje alert w Cloud Audit Logs - wymaga post-mortem
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-emergency
annotations:
alpha.image-policy.k8s.io/break-glass: "true"
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
annotations:
alpha.image-policy.k8s.io/break-glass: "true"
spec:
containers:
- name: my-app
image: gcr.io/my-project/my-app:v1.2.3-hotfix
# Wdróż z break-glass
kubectl apply -f deployment-emergency.yaml
# NATYCHMIAST po wdrożeniu: utwórz atestację dla hotfix obrazu
# i wdróż ponownie bez break-glass (compliance requirement)
gcloud container binauthz attestations sign-and-create \
--project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:hotfix-digest..." \
--attestor=build-attestor \
--attestor-project=my-project \
--keyversion-project=my-project \
--keyversion-location=europe-west1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=1
Krok 6: Napraw pipeline CI/CD - dodaj krok poświadczenia
# Przykład kroku atestacji w Cloud Build (cloudbuild.yaml)
steps:
# ... build i push steps ...
- id: 'create-attestation'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
IMAGE_DIGEST=$(gcloud container images describe \
gcr.io/${PROJECT_ID}/my-app:${SHORT_SHA} \
--format="value(image_summary.digest)")
gcloud container binauthz attestations sign-and-create \
--project=${PROJECT_ID} \
--artifact-url="gcr.io/${PROJECT_ID}/my-app@$${IMAGE_DIGEST}" \
--attestor=build-attestor \
--attestor-project=${PROJECT_ID} \
--keyversion-project=${PROJECT_ID} \
--keyversion-location=europe-west1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=1
waitFor: ['push-image']
Krok 7: Weryfikacja zgodności klucza attestor po rotacji KMS
Jeśli odmowa pojawiła się po rotacji klucza KMS, attestor wymaga aktualizacji nowej wersji klucza:
# Sprawdź aktualne wersje klucza w keyring
gcloud kms keys versions list \
--key=build-attestor-key \
--keyring=binauthz-keys \
--location=europe-west1 \
--project=my-project \
--format="table(name, state, createTime)"
# Jeśli wersja 1 jest DISABLED a wersja 2 jest ENABLED,
# zaktualizuj attestor o nową wersję klucza
gcloud container binauthz attestors public-keys add \
--attestor=build-attestor \
--project=my-project \
--keyversion-project=my-project \
--keyversion-location=europe-west1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=2
# Usuń stary (wyłączony) klucz z attestor
OLD_KEY_ID=$(gcloud container binauthz attestors describe build-attestor \
--project=my-project \
--format="value(userOwnedGrafeasNote.publicKeys[0].id)")
gcloud container binauthz attestors public-keys remove "${OLD_KEY_ID}" \
--attestor=build-attestor \
--project=my-project
# Ponownie zaatestuj obrazy podpisane starym kluczem
# (wymagane tylko dla obrazów aktualnie wdrożonych lub planowanych do wdrożenia)
gcloud container binauthz attestations sign-and-create \
--project=my-project \
--artifact-url="gcr.io/my-project/my-app@sha256:a1b2c3d4e5f6..." \
--attestor=build-attestor \
--attestor-project=my-project \
--keyversion-project=my-project \
--keyversion-location=europe-west1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=2
Walidacja
Po naprawieniu poświadczeń lub pipeline potwierdź, że deployment przechodzi:
# 1. Ponów deployment
kubectl apply -f deployment.yaml
# Expected: deployment.apps/my-app configured (brak błędu admission)
# 2. Sprawdź status pod
kubectl get pods -l app=my-app
# Expected: STATUS = Running
# 3. Potwierdź w Audit Logs brak nowych denial
gcloud logging read \
'resource.type="k8s_cluster" AND
protoPayload.methodName="io.k8s.core.v1.pods.create" AND
protoPayload.response.reason="VIOLATES_POLICY"' \
--project=my-project \
--freshness=10m \
--format="table(timestamp, protoPayload.response.reason)"
# Expected: brak wyników (żadne nowe odrzucenia)
# 4. Sprawdź break-glass events (powinny być 0 w normalnej operacji)
gcloud logging read \
'resource.type="k8s_cluster" AND
protoPayload.request.metadata.annotations."alpha.image-policy.k8s.io/break-glass"="true"' \
--project=my-project \
--freshness=24h
# Expected: brak wyników (break-glass używane tylko w emergencies)
# 5. Zweryfikuj że attestor i klucz KMS są zgodne
gcloud container binauthz attestors describe build-attestor \
--project=my-project \
--format="value(userOwnedGrafeasNote.publicKeys[0].id)"
# Porównaj z wersją klucza używaną w pipeline
Binary Authorization jest krytycznym zabezpieczeniem supply chain wymaganym przez PCI DSS i SOC2. Blokada deployment oznacza, że mechanizm działa prawidłowo - problem jest w procesie poświadczenia, a nie w polityce. Nigdy nie wyłączaj Binary Authorization na produkcji. Zamiast tego napraw pipeline, aby każdy obraz był automatycznie atestowany po przejściu skanowania bezpieczeństwa. Break-glass jest wyłącznie na sytuacje awaryjne i każde jego użycie musi być udokumentowane w post-mortem.
Binary Authorization blokuje deployment?
Umów bezpłatną 30-minutową rozmowę. Zaprojektujemy pipeline z automatycznym poświadczeniem obrazów, konfiguracją attestor i politykami zgodnymi z PCI DSS.