2.3 – Pipeline Config & Secrets | 2.4 – Securing the Deployment Pipeline
2.3 – Managing Pipeline Configuration and Secrets
Secret Manager
Key Concepts
- Store versioned, encrypted secrets (passwords, API keys, TLS certs, SSH keys)
- Each secret has versions — can rotate without changing name
- IAM at secret level — granular access control
- Audit every access via Cloud Audit Logs
# Create a secret
echo -n "my-db-password" | gcloud secrets create db-password \
--data-file=- \
--replication-policy=automatic
# Add a new version
echo -n "new-password-v2" | gcloud secrets versions add db-password --data-file=-
# Access latest version
gcloud secrets versions access latest --secret=db-password
# Grant access to a SA
gcloud secrets add-iam-policy-binding db-password \
--member="serviceAccount:SA@PROJECT.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
Secret Rotation
# Enable automatic rotation (sends Pub/Sub notification)
gcloud secrets update db-password \
--rotation-period="7776000s" \ # 90 days
--next-rotation-time="2024-06-01T00:00:00Z"
Secret Replication
| Policy |
Use case |
automatic |
GCP manages replication across regions |
user-managed |
You specify which regions (for data residency) |
Key Management Service (Cloud KMS)
Key Concepts
- Manage cryptographic keys for encryption/decryption and signing
- CMEK (Customer-Managed Encryption Keys): use your own keys to encrypt GCP resources
- Key rings group keys by location
- Key versions: primary version used for encryption; older versions retained for decryption
Key Types
| Type |
Use case |
| Symmetric (AES-256-GCM) |
Encrypt/decrypt data |
| Asymmetric (RSA/EC) |
Sign/verify, encrypt (small payloads) |
| HMAC |
Message authentication |
| HSM-backed |
Hardware security module (higher assurance) |
# Create key ring and key
gcloud kms keyrings create my-keyring --location=us-central1
gcloud kms keys create my-key \
--keyring=my-keyring \
--location=us-central1 \
--purpose=encryption
# Encrypt a file
gcloud kms encrypt \
--key=my-key --keyring=my-keyring --location=us-central1 \
--plaintext-file=secret.txt \
--ciphertext-file=secret.enc
# Use CMEK for a GCS bucket
gsutil kms encryption -k \
projects/PROJECT/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key \
gs://my-bucket
CMEK vs CSEK vs Google-Managed
| Type |
Who manages key |
Use case |
| Google-managed |
Google |
Default, minimal overhead |
| CMEK (Cloud KMS) |
You, in GCP |
Compliance, audit, revocation |
| CSEK (Customer-Supplied) |
You, outside GCP |
Maximum control; you supply key per request |
Certificate Manager
- Manage TLS/SSL certificates for GCP load balancers and services
- Supports: Google-managed certs (auto-renew), self-managed, Certificate Authority Service
- Replaces the older
ManagedCertificate resource for GKE Ingress
# Create a Google-managed cert
gcloud certificate-manager certificates create my-cert \
--domains="app.example.com" \
--project=PROJECT_ID
Parameter Manager (New)
- Lightweight config/parameter store (different from Secret Manager)
- For non-sensitive configuration parameters (feature flags, env-specific config)
- Versions supported; IAM-controlled
- Use when you need structured config (JSON/YAML parameters) vs raw secret blobs
Workload Identity Federation (WIF)
Why WIF?
- Eliminate SA JSON key files — the #1 source of credential leaks
- External workloads (GitHub Actions, GitLab CI, AWS, Azure, on-prem) authenticate to GCP using their own identity tokens
- GCP STS exchanges the external token for a short-lived GCP access token
WIF Architecture
GitHub Actions job → OIDC token (issued by GitHub)
→ Google STS (Secure Token Service) → validates OIDC token
→ Short-lived access token → used to call GCP APIs
Setup for GitHub Actions
# Create workload identity pool
gcloud iam workload-identity-pools create github-pool \
--location=global \
--display-name="GitHub Actions Pool"
# Create OIDC provider
gcloud iam workload-identity-pools providers create-oidc github-provider \
--workload-identity-pool=github-pool \
--location=global \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
--attribute-condition="assertion.repository=='org/repo'"
# Bind SA to WIF pool/provider
gcloud iam service-accounts add-iam-policy-binding deploy-sa@PROJECT.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUM/locations/global/workloadIdentityPools/github-pool/attribute.repository/org/repo"
# GitHub Actions workflow
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/NUM/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: 'deploy-sa@PROJECT.iam.gserviceaccount.com'
WIF for GKE (different concept)
- Uses GKE Workload Identity (not the same as external WIF)
- Maps Kubernetes ServiceAccount → Google ServiceAccount via annotation
- No JSON key file ever touches the pod
# Enable workload identity on cluster
gcloud container clusters update CLUSTER \
--workload-pool=PROJECT_ID.svc.id.goog
# Annotate K8s SA to bind to Google SA
kubectl annotate serviceaccount k8s-sa \
iam.gke.io/gcp-service-account=gsa@PROJECT.iam.gserviceaccount.com
Build-Time vs Runtime Secret Injection
| Timing |
Method |
Risk |
| Build-time |
Inject during docker build — secret baked in image |
⚠️ HIGH — secret in image layer history |
| Build-time (safe) |
Pass as env var to build step only (not COPY’d into image) |
Medium — visible in build logs |
| Runtime (recommended) |
App reads from Secret Manager / env var at startup |
✅ LOW |
| Runtime (K8s) |
CSI Secret Store driver mounts Secret Manager secrets as files |
✅ LOW |
# Safe: Secret Manager access at runtime in Cloud Build
steps:
- name: 'gcr.io/cloud-builders/gcloud'
secretEnv: ['DB_PASSWORD']
script: |
echo "Connecting to DB with $$DB_PASSWORD"
availableSecrets:
secretManager:
- versionName: projects/PROJECT/secrets/db-password/versions/latest
env: 'DB_PASSWORD'
2.4 – Securing the Deployment Pipeline
Artifact Analysis & Vulnerability Scanning
- Automatic scanning on push to Artifact Registry (enable on repo)
- Scans for: OS vulnerabilities (CVEs), language packages (npm, pip, Maven)
- Results available in: Cloud Console, gcloud, API, Pub/Sub notifications
# Enable vulnerability scanning on a repo
gcloud artifacts repositories update my-repo \
--location=us-central1 \
--enable-vulnerability-scanning
# View vulnerabilities
gcloud artifacts docker images list-vulnerabilities \
us-central1-docker.pkg.dev/PROJECT/my-repo/app:v1.0.0
# Check for critical CVEs in CI
gcloud artifacts docker images scan IMAGE_URI \
--format="json" | jq '.response.scan.discoveryOccurrence'
Binary Authorization
Concepts
- Policy enforcement: only deploy images that satisfy configured policies
- Attestations: digital signatures proving an image passed a required check (e.g., vulnerability scan, QA approval)
- Attestors: entities that create attestations
- Break-glass: emergency bypass (all bypasses logged)
Policy Modes
| Mode |
Behavior |
| Enforce |
Block deployments that don’t meet policy |
| Dry-run |
Log violations but allow deployment |
| Audit (CV) |
Continuous validation — monitors running pods periodically |
Pipeline Integration
Cloud Build builds image →
Artifact Analysis scans for vulns →
Custom attestor creates attestation (if scan passed) →
Cloud Deploy rollout to GKE/Cloud Run →
Binary Authorization checks attestation →
✅ Deploy OR ❌ Block
# Binary Authorization policy
defaultAdmissionRule:
evaluationMode: ALWAYS_DENY # Block anything not explicitly allowed
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
admissionWhitelistPatterns:
- namePattern: gcr.io/google_containers/*
clusterAdmissionRules:
us-central1.prod-cluster:
evaluationMode: REQUIRE_ATTESTATION
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
requireAttestationsBy:
- projects/PROJECT/attestors/qa-attestor
- projects/PROJECT/attestors/vuln-scan-attestor
# Create attestor
gcloud container binauthz attestors create qa-attestor \
--attestation-authority-note=NOTE_ID \
--attestation-authority-note-project=PROJECT_ID
# Create attestation for an image
gcloud container binauthz attestations sign-and-create \
--attestor=qa-attestor \
--artifact-url=DIGEST_URL \
--keyversion=KEY_VERSION
SLSA Framework (Supply-chain Levels for Software Artifacts)
SLSA Levels
| Level |
Requirements |
Cloud Build Status |
| L0 |
No guarantees |
— |
| L1 |
Provenance exists |
Cloud Build auto-generates |
| L2 |
Provenance signed; hosted build platform |
Cloud Build (managed) |
| L3 |
Provenance unforgeable; isolated builds |
Cloud Build (hardened config) |
Build Provenance
- Cloud Build automatically generates SLSA-compliant provenance for container images
- Provenance = verifiable metadata: who built, when, from what source, with what config
- Stored in Artifact Analysis alongside the image
# View provenance for an image
gcloud artifacts docker images describe \
us-central1-docker.pkg.dev/PROJECT/repo/app@sha256:DIGEST \
--show-provenance
SLSA Check with Binary Authorization CV
# Create CV policy that requires SLSA L3 provenance
gcloud container binauthz policy create-cv-policy \
--platform-policy=platforms/gke/policies/slsa-check-policy.yaml
IAM Policies for CI/CD Environments
Principle: Least Privilege per Stage
| Pipeline Stage |
SA Needed |
Roles |
| Cloud Build (build+test) |
build-sa |
roles/artifactregistry.writer, roles/secretmanager.secretAccessor |
| Cloud Deploy (orchestrate) |
deploy-sa |
roles/clouddeploy.releaser |
| Cloud Deploy (deploy to GKE) |
gke-deploy-sa |
roles/container.developer |
| Production deployment |
Separate SA |
Minimal; requireApproval: true in pipeline |
Separate SAs per Environment
# Dev SA - broad permissions
gcloud iam service-accounts create build-dev-sa
# Prod SA - minimal, tightly scoped
gcloud iam service-accounts create deploy-prod-sa
# Apply IAM condition: only allow prod SA in prod project
gcloud projects add-iam-policy-binding prod-project \
--member="serviceAccount:deploy-prod-sa@PROJECT.iam.gserviceaccount.com" \
--role="roles/container.developer" \
--condition="resource.name.startsWith('projects/prod-project')"
Exam Tips
- Secret Manager = secret storage + versioning + rotation + audit log
- Cloud KMS = cryptographic key management; used for CMEK
- Workload Identity Federation = keyless auth for external workloads (GitHub Actions, etc.)
- Binary Authorization = policy enforcement at deploy time using attestations
- SLSA L2 = Cloud Build out-of-the-box; L3 requires additional hardening
- Artifact Analysis = vulnerability scanning; results used by Binary Authorization
- Never inject secrets at build time into image layers — use runtime Secret Manager access
- Break-glass in Binary Authorization = emergency bypass + audit log entry