Encryption
TSC mapping: CC6.1 (Logical access security — data at rest), CC6.7 (Data transmission and removal controls)
Auditors require evidence that customer data is encrypted at rest and in transit, that keys are managed with proper access controls and rotation, and that secrets are not stored in plaintext.
1. Cloud KMS — Key Management
Cloud KMS provides software and hardware (Cloud HSM) key management. Use Customer-Managed Encryption Keys (CMEK) for any data requiring auditable key control.
Create a key ring and CMEK
# Enable the Cloud KMS API
gcloud services enable cloudkms.googleapis.com \
--project=<project-id>
# Create a key ring in the same region as your data
gcloud kms keyrings create soc2-keyring \
--location=us-central1 \
--project=<project-id>
# Create a symmetric encryption key (for CMEK use)
gcloud kms keys create cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--purpose=encryption \
--rotation-period=7776000s \
--next-rotation-time=$(date -u -d "+90 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+90d +%Y-%m-%dT%H:%M:%SZ) \
--project=<project-id>
# Verify rotation schedule
gcloud kms keys describe cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--project=<project-id> \
--format="table(name,rotationPeriod,nextRotationTime)"
Restrict KMS key access — least privilege
# Grant only the required service account permission to use the key
gcloud kms keys add-iam-policy-binding cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--member="serviceAccount:storage-sa@<project-id>.iam.gserviceaccount.com" \
--role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
--project=<project-id>
# Deny key destruction to all except the Key Admin role
gcloud kms keys get-iam-policy cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--project=<project-id>
Reference: Cloud KMS documentation → · Cloud KMS key rotation →
2. Cloud Storage — Encryption at Rest
Cloud Storage always encrypts data at rest with AES-256 (Google-managed key by default). For SOC 2, use CMEK for auditable key control.
Enable CMEK on a Cloud Storage bucket
# Grant the Cloud Storage service account permission to use the key
STORAGE_SA=$(gcloud storage service-agent --project=<project-id>)
gcloud kms keys add-iam-policy-binding cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--member="serviceAccount:${STORAGE_SA}" \
--role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
--project=<project-id>
# Create a bucket with CMEK
gcloud storage buckets create gs://prod-sensitive-data \
--project=<project-id> \
--location=us-central1 \
--uniform-bucket-level-access \
--default-encryption-key=projects/<project-id>/locations/us-central1/keyRings/soc2-keyring/cryptoKeys/cmek-prod
# Update an existing bucket to use CMEK
gcloud storage buckets update gs://existing-bucket \
--default-encryption-key=projects/<project-id>/locations/us-central1/keyRings/soc2-keyring/cryptoKeys/cmek-prod
Block public access on all buckets
# Disable public access on a specific bucket
gcloud storage buckets update gs://prod-sensitive-data \
--no-public-access-prevention=false
# Enforce public access prevention org-wide via Organization Policy
gcloud resource-manager org-policies set-policy \
--organization=<org-id> \
- <<'EOF'
name: organizations/<org-id>/policies/storage.publicAccessPrevention
spec:
rules:
- enforce: true
EOF
# Verify bucket encryption and public access settings
gcloud storage buckets describe gs://prod-sensitive-data \
--format="table(defaultKmsKeyName,iamConfiguration.publicAccessPrevention,iamConfiguration.uniformBucketLevelAccess.enabled)"
Reference: Cloud Storage encryption → · Customer-managed encryption keys for GCS →
3. Persistent Disk — Encryption at Rest
All Compute Engine Persistent Disks are encrypted by default (Google-managed). Use CMEK for auditable control.
# Grant Compute Engine service account access to the KMS key
CE_SA="service-<project-number>@compute-system.iam.gserviceaccount.com"
gcloud kms keys add-iam-policy-binding cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--member="serviceAccount:${CE_SA}" \
--role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
--project=<project-id>
# Create a VM with CMEK-encrypted boot disk
gcloud compute instances create vm-prod \
--project=<project-id> \
--zone=us-central1-a \
--machine-type=n2-standard-2 \
--image-family=debian-12 \
--image-project=debian-cloud \
--disk="boot=yes,device-name=boot,kms-key=projects/<project-id>/locations/us-central1/keyRings/soc2-keyring/cryptoKeys/cmek-prod" \
--no-address
# Create a standalone encrypted disk
gcloud compute disks create data-disk-prod \
--project=<project-id> \
--zone=us-central1-a \
--size=100GB \
--type=pd-ssd \
--kms-key=projects/<project-id>/locations/us-central1/keyRings/soc2-keyring/cryptoKeys/cmek-prod
# Audit disks without CMEK
gcloud compute disks list \
--project=<project-id> \
--format="table(name,zone,diskEncryptionKey.kmsKeyName)" | \
grep -v "kmsKeyName" # Disks with no KMS key are using Google-managed keys
Reference: Persistent Disk encryption →
4. Cloud SQL — Encryption at Rest
Cloud SQL instances are encrypted by default. Enable CMEK for auditable key management and to satisfy CC6.1 requirements.
# Grant Cloud SQL service account access to the KMS key
SQL_SA=$(gcloud sql instances describe <instance-name> \
--project=<project-id> \
--format="value(serviceAccountEmailAddress)" 2>/dev/null || \
echo "service-<project-number>@gcp-sa-cloud-sql.iam.gserviceaccount.com")
gcloud kms keys add-iam-policy-binding cmek-prod \
--keyring=soc2-keyring \
--location=us-central1 \
--member="serviceAccount:${SQL_SA}" \
--role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
--project=<project-id>
# Create a Cloud SQL PostgreSQL instance with CMEK
gcloud sql instances create prod-pg \
--project=<project-id> \
--database-version=POSTGRES_15 \
--region=us-central1 \
--tier=db-n1-standard-2 \
--disk-encryption-key=projects/<project-id>/locations/us-central1/keyRings/soc2-keyring/cryptoKeys/cmek-prod \
--no-assign-ip \
--network=projects/<project-id>/global/networks/prod-vpc \
--backup-start-time=03:00 \
--enable-bin-log
# Verify encryption key assignment
gcloud sql instances describe prod-pg \
--project=<project-id> \
--format="value(diskEncryptionConfiguration.kmsKeyName)"
Enable Cloud SQL audit logs:
gcloud sql instances patch prod-pg \
--project=<project-id> \
--database-flags=log_connections=on,log_disconnections=on,log_duration=on,log_min_duration_statement=1000
Reference: Cloud SQL CMEK → · Cloud SQL best practices →
5. Secret Manager — Secret Storage and Rotation
Store all credentials, API keys, and connection strings in Secret Manager. Never store them in environment variables, source code, or startup scripts.
# Enable Secret Manager API
gcloud services enable secretmanager.googleapis.com \
--project=<project-id>
# Create a secret (stored encrypted with CMEK or Google-managed key)
gcloud secrets create prod-db-password \
--project=<project-id> \
--replication-policy=user-managed \
--locations=us-central1
# Add the secret value
echo -n "my-strong-password" | \
gcloud secrets versions add prod-db-password \
--data-file=- \
--project=<project-id>
# Grant a service account access to read a specific secret
gcloud secrets add-iam-policy-binding prod-db-password \
--project=<project-id> \
--member="serviceAccount:app-sa@<project-id>.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# Access a secret from application code (using the default credential chain)
gcloud secrets versions access latest \
--secret=prod-db-password \
--project=<project-id>
Automatic rotation using Cloud Functions or Cloud Run:
# Create a Pub/Sub topic for rotation notifications
gcloud pubsub topics create secret-rotation \
--project=<project-id>
# Configure rotation on the secret
gcloud secrets update prod-db-password \
--project=<project-id> \
--add-topics=projects/<project-id>/topics/secret-rotation \
--next-rotation-time=$(date -u -d "+30 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+30d +%Y-%m-%dT%H:%M:%SZ) \
--rotation-period=2592000s # 30 days
# List all secrets and their rotation status
gcloud secrets list \
--project=<project-id> \
--format="table(name,rotation.nextRotationTime,replication.userManaged.replicas[0].location)"
Reference: Secret Manager documentation → · Secret rotation →
6. Other Services — Encryption Checklist
| Service | Control | How to verify |
|---|---|---|
| BigQuery | CMEK on datasets | bq show --format=prettyjson <project>:<dataset> | jq .defaultEncryptionConfiguration |
| Pub/Sub | CMEK on topics | gcloud pubsub topics describe <topic> --format="value(kmsKeyName)" |
| Cloud Spanner | CMEK on instance | gcloud spanner instances describe <instance> --format="value(encryptionConfig.kmsKeyName)" |
| Memorystore (Redis) | Transit encryption | gcloud redis instances describe <name> --region=<region> --format="value(transitEncryptionMode)" |
| Cloud Composer | CMEK for DAGs and logs | gcloud composer environments describe <name> --format="value(config.encryptionConfig.kmsKeyName)" |
| Artifact Registry | CMEK for container images | gcloud artifacts repositories describe <repo> --location=<loc> --format="value(kmsKeyName)" |
SOC 2 Evidence for Encryption
| Evidence item | How to collect |
|---|---|
| KMS key list with rotation schedule | gcloud kms keys list --keyring=soc2-keyring --location=us-central1 |
| Cloud Storage bucket encryption config | gcloud storage buckets describe gs://<bucket> --format="value(defaultKmsKeyName)" |
| Persistent Disk CMEK status | gcloud compute disks list --format="table(name,diskEncryptionKey.kmsKeyName)" |
| Cloud SQL CMEK status | gcloud sql instances describe <name> --format="value(diskEncryptionConfiguration.kmsKeyName)" |
| Secret Manager rotation policy | gcloud secrets describe <secret> --format="value(rotation)" |
| SCC encryption findings | GCP Console → Security Command Center → Findings → Category: CRYPTOGRAPHIC_KEY |
| Organization Policy — storage.publicAccessPrevention | gcloud resource-manager org-policies describe storage.publicAccessPrevention --organization=<org-id> |