Back to blog
Best PracticesCloud SecurityGCPMonitoring & LoggingOperations & Compliance

Encrypting GCP Log Buckets With Customer-Managed Keys (CMEK)

Learn why GCP log buckets should use customer-managed KMS keys, the risks of default encryption, and how to enforce CMEK with gcloud, Terraform, and policy-as-code.

TL;DR

This check flags GCP Cloud Logging buckets that rely on default Google-managed encryption instead of a customer-managed KMS key (CMEK). Without CMEK you lose control over key rotation, access, and revocation for your logs. Fix it by creating a KMS key and binding it to the log bucket with gcloud logging buckets update --cmek-kms-key-name.

Logs are one of the highest-value targets in any cloud environment. They record who did what, when, and from where. If an attacker or an over-privileged insider can read raw log data, they can map your infrastructure, harvest tokens that leaked into request logs, and figure out exactly how to move laterally. That is why controlling who can decrypt your logs matters just as much as controlling who can read them through the API.

The Log Bucket Not Encrypted With CMEK check looks at your Cloud Logging buckets and reports any that use Google's default encryption instead of a customer-managed encryption key. By default GCP encrypts logs at rest, but the key is owned and managed entirely by Google. With CMEK, you own the key, you decide its rotation schedule, and you can cut off access to the underlying data by disabling or destroying the key.


What this check detects

Cloud Logging stores entries in log buckets. Every project has two default buckets, _Default and _Required, and you can create additional user-defined buckets to route specific logs. Each bucket has a CMEK setting that is either empty (Google-managed encryption) or points to a Cloud KMS key.

This check fails when a log bucket has no CMEK key configured. In practice that means the bucket's cmekSettings field is absent or null, so the data is protected only by keys that Google controls on your behalf.

Note: Default Google-managed encryption is not "no encryption." Your logs are always encrypted at rest. The gap CMEK closes is control: key rotation policy, access auditing on the key itself, and the ability to revoke decryption by disabling the key.


Why it matters

The difference between Google-managed encryption and CMEK is about who holds the off switch. Consider a few concrete scenarios.

Compliance mandates explicit key ownership

Frameworks like PCI DSS, HIPAA, FedRAMP, and many internal data-handling policies require that organizations demonstrate control over the keys protecting sensitive data. Auditors increasingly expect to see customer-managed keys with documented rotation and access policies. "Google handles it" is rarely an acceptable answer for regulated log data that may contain PII or transaction details.

You cannot revoke access you do not control

Imagine you discover a credential compromise and you need to guarantee that an attacker can no longer read historical logs, even if they somehow retain storage-level access. With CMEK you can disable the key version and decryption stops immediately. With Google-managed keys you have no equivalent lever.

Centralized key governance and separation of duties

CMEK lets you put key administration in the hands of a dedicated security team while application teams continue to write logs. The team that can read logs and the team that controls the encryption key can be kept separate, which limits the blast radius if a single account is compromised.

Warning: CMEK protects log data going forward, but it does not retroactively re-encrypt entries already written to a bucket. Apply CMEK to a bucket before sensitive logs start flowing into it, ideally at creation time.


How to fix it

Remediation has three parts: create a KMS key, grant the Cloud Logging service account permission to use it, and bind the key to the log bucket.

1. Create a key ring and key

Pick a location that matches your log bucket's region. CMEK keys must live in the same location as the resource they protect, or in a multi-region that contains it.

gcloud kms keyrings create logging-keyring \
  --location=us-central1

gcloud kms keys create logging-cmek \
  --location=us-central1 \
  --keyring=logging-keyring \
  --purpose=encryption \
  --rotation-period=90d \
  --next-rotation-time=$(date -u -d "+90 days" +%Y-%m-%dT%H:%M:%SZ)

2. Grant the Logging service account access to the key

Cloud Logging uses a per-project service account to encrypt and decrypt data. Find it, then grant it the encrypter/decrypter role on your key.

# Get the Logging CMEK service account for your project
LOG_SA=$(gcloud logging cmek-settings describe \
  --project=PROJECT_ID \
  --format="value(serviceAccountId)")

echo "Logging service account: $LOG_SA"

# Grant it permission to use the key
gcloud kms keys add-iam-policy-binding logging-cmek \
  --location=us-central1 \
  --keyring=logging-keyring \
  --member="serviceAccount:${LOG_SA}" \
  --role="roles/cloudkms.cryptoKeyEncrypterDecrypter"

Note: If you skip the IAM binding, the bucket update will fail or logs will stop being ingested, because Logging cannot use a key it has no rights to. Always grant the role before binding the key.

3. Bind the key to the log bucket

gcloud logging buckets update BUCKET_ID \
  --location=us-central1 \
  --project=PROJECT_ID \
  --cmek-kms-key-name="projects/PROJECT_ID/locations/us-central1/keyRings/logging-keyring/cryptoKeys/logging-cmek"

Verify the setting took effect:

gcloud logging buckets describe BUCKET_ID \
  --location=us-central1 \
  --project=PROJECT_ID \
  --format="value(cmekSettings.kmsKeyName)"

Warning: You cannot apply CMEK to the _Required bucket, and you can only set CMEK on the _Default bucket if it was created after CMEK support was available. For full control, route sensitive logs to dedicated user-defined buckets created with CMEK from the start.

Terraform example

If you manage infrastructure as code, define the key and the bucket together so encryption is never an afterthought.

resource "google_kms_key_ring" "logging" {
  name     = "logging-keyring"
  location = "us-central1"
}

resource "google_kms_crypto_key" "logging" {
  name            = "logging-cmek"
  key_ring        = google_kms_key_ring.logging.id
  rotation_period = "7776000s" # 90 days

  lifecycle {
    prevent_destroy = true
  }
}

# Look up the Logging CMEK service account
data "google_logging_project_cmek_settings" "this" {
  project = var.project_id
}

resource "google_kms_crypto_key_iam_member" "logging" {
  crypto_key_id = google_kms_crypto_key.logging.id
  role          = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
  member        = "serviceAccount:${data.google_logging_project_cmek_settings.this.service_account_id}"
}

resource "google_logging_project_bucket_config" "secure" {
  project        = var.project_id
  location       = "us-central1"
  bucket_id      = "secure-app-logs"
  retention_days = 365

  cmek_settings {
    kms_key_name = google_kms_crypto_key.logging.id
  }

  depends_on = [google_kms_crypto_key_iam_member.logging]
}

Danger: If you disable or destroy a key that a live log bucket depends on, Cloud Logging loses the ability to ingest and read those logs. Existing entries become unreadable and new writes can fail. Use prevent_destroy on the key and never delete a key version that is still bound to an active bucket.


How to prevent it from happening again

Fixing one bucket is easy. Keeping every new bucket compliant across dozens of projects is the real work. Automate it.

Enforce with Organization Policy

The constraints/gcp.restrictNonCmekServices organization policy constraint forces CMEK on supported services, including Cloud Logging. Once enabled, attempts to create or use a non-CMEK log bucket are denied.

cat > cmek-policy.yaml <<'EOF'
name: organizations/ORG_ID/policies/gcp.restrictNonCmekServices
spec:
  rules:
    - values:
        deniedValues:
          - logging.googleapis.com
EOF

gcloud org-policies set-policy cmek-policy.yaml

Gate it in CI/CD with policy-as-code

Catch missing CMEK before resources ever reach production. A Conftest/OPA policy against a Terraform plan keeps non-compliant buckets out of merge.

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "google_logging_project_bucket_config"
  not resource.change.after.cmek_settings
  msg := sprintf("Log bucket '%s' must be encrypted with a CMEK key", [resource.address])
}

Tip: Combine the org policy and the CI gate. The org policy is your backstop that blocks anything created out of band, while the CI check gives developers fast, readable feedback during code review instead of a cryptic API error at apply time.

Continuous monitoring

People still create resources by hand, and policies get exceptions. Run a continuous check like this Lensix scan so a non-CMEK bucket created through the console gets flagged within minutes rather than at your next annual audit.


Best practices

  • Apply CMEK at creation time. Encryption settings on a log bucket affect data going forward, so set the key before any logs land.
  • Rotate keys on a schedule. A 90-day rotation period is a common baseline. Automatic rotation in KMS means you never have to remember to do it manually.
  • Keep keys in a separate, locked-down project. Put KMS resources in a dedicated security project with tight IAM so application teams can write logs but cannot administer the keys.
  • Match key and bucket locations. Region mismatches are the most common cause of failed CMEK bindings. Use a multi-region key only when the bucket is in a matching multi-region.
  • Audit the key, not just the data. Enable Cloud Audit Logs for Cloud KMS so every encrypt, decrypt, and admin action on the key is itself logged.
  • Route sensitive logs to dedicated CMEK buckets. Use log sinks to send high-value logs (audit, access, payment) into buckets you fully control, rather than relying on the shared defaults.

Encryption that someone else controls protects you against a stolen disk. Encryption you control protects you against a stolen credential, a rogue insider, and a failed audit. For logs, that distinction is worth the small amount of setup.

Once CMEK is in place, your log data inherits a clear ownership boundary: Google can run the storage, but only your key can unlock the contents. That is exactly the posture regulators, auditors, and incident responders want to see.