Skip to main content
CMDashCybersecurity Maturity Dashboard
← Back to Google Cloud Platform

Google Cloud Platform — Integration Setup

GCP had no configuration evidence in CMDash before this integration. The only Google Cloud integration was Chronicle, which is a SIEM — it reads detections and alerts and cannot answer "is this bucket public?". AWS and Azure at least had a paid findings product that could be connected; GCP had nothing. This reads your project's configuration directly, with a read-only service account.

Prerequisites

  • A GCP project
  • Permission to create a service account and grant project-level roles
  • The following APIs enabled on the project: Cloud Resource Manager, IAM, Cloud Storage, and Cloud KMS (only if you want key-rotation checks)
  • CMDash Essentials tier or above

Step 1: Create a service account

  1. IAM & Admin → Service Accounts → Create service account.
  2. Name it cmdash-config-reader.
  3. Skip the "Grant this service account access" step for now — Step 2 does it more precisely.
  4. Done.

Step 2: Grant read-only roles

Grant these three predefined roles at the project level:

RoleWhy
roles/viewerLists buckets and their IAM configuration
roles/iam.securityReviewerReads the project IAM policy (for audit-log configuration) and lists service-account keys
roles/cloudkms.viewerLists KMS key rings and keys. Omit this if you do not want key-rotation checks

Least privilege: all three are read-only. Do not grant roles/editor or roles/owner. Note that roles/viewer does not grant access to object contents in Cloud Storage — CMDash reads bucket settings, never your data.

Step 3: Create a JSON key

  1. Select the service account → Keys → Add key → Create new key → JSON.
  2. Download the file. It contains project_id, client_email, and private_key.

This key is a long-lived credential. CMDash's own GCP check flags user-managed service-account keys as a finding, and that includes this one. That is not a contradiction — it is the honest trade-off of the integration, and it is the reason the credential is scoped as narrowly as Step 2 allows. Store it as you would any production secret, and rotate it on the schedule below.

Step 4: Connect in CMDash

  1. Navigate to Integrations in your CMDash dashboard.
  2. Find Google Cloud Platform and click Connect.
  3. Enter, from the JSON key file:
    • Project ID — project_id. Required.
    • Client email — client_email
    • Private key — private_key, the entire PEM block including the -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- lines
    • KMS locations — optional list (e.g. global, us-central1). See below.
  4. Click Save & Test Connection.

Why a per-connection service account: GCP service accounts authenticate by signing a JWT with their own RSA private key and exchanging it for a token. The key belongs to your project, so it lives on the connection rather than in CMDash's server-wide config.

CMDash requests the token with the cloud-platform.read-only scope. Even if the service account is over-granted in IAM, the token CMDash holds cannot mutate anything — the scope ceiling applies on top of the role. That is a stronger promise than "we only send GET requests".

What Syncs

CheckGCP APIControl it evidences
Bucket enforces public access preventionstorage.buckets.listCC6.6, SC-7, A.5.10
Bucket uses uniform bucket-level accessstorage.buckets.listCC6.6, A.5.10
Data Access audit logs enabled for all servicesprojects.getIamPolicyAU-2, A.8.15, CC7.2
Service account has no user-managed keysserviceAccounts.list → keys.listCC6.1, AC-2, A.5.15
Cloud KMS key has a rotation periodkeyRings.list → cryptoKeys.listSC-12, A.8.24

How the numbers are computed

  • Every check is tri-state: pass, fail, or unknown. An API that returned 403, or a field GCP did not report, is unknown — never a failure. Unknown is excluded from both sides of every percentage, so a missing role produces not measured, not 0%.
  • publicAccessPrevention: inherited is recorded as unknown, not as a failure. inherited means the bucket defers to the organisation policy, which may well enforce prevention — but the buckets API does not say. Calling it a failure would flag buckets that an org policy already protects, which is a fabricated finding.
  • Absent Data Access audit configuration is a genuine failure, not unknown. GCP disables Data Access logs by default for everything except BigQuery, so the absence is the default state, and the default is the finding. All three of ADMIN_READ, DATA_READ and DATA_WRITE must be enabled on allServices to pass.
  • A user-managed service-account key is a failure. These never expire and are the most common source of leaked GCP access. An empty key list is the passing state.
  • KMS rotation is only checked in the locations you list. Key rings are per-location and there is no cross-location list API. If you configure no locations, no KMS check runs at all and keyRotationCoverage reads as unmeasured. CMDash does not emit a passing record in that case — claiming your keys were inspected and found rotating would be a claim nobody made.
  • Asymmetric KMS keys are skipped, not failed. Only ENCRYPT_DECRYPT keys support automatic rotation; asking a signing key to rotate is a category error, not a finding.

Troubleshooting

  • "GCP private key could not be used to sign the assertion": the private key was pasted without its PEM header/footer, or the newlines were mangled. Paste the whole private_key value from the JSON file. CMDash handles the \n escaping in the JSON automatically.
  • Token endpoint returns 400 invalid_grant: the service-account key was deleted or disabled in GCP, or the clock on your side is significantly wrong (the signed JWT carries a timestamp).
  • Connection test fails with 403: the service account exists but has no roles/viewer on the project, or the Cloud Resource Manager API is not enabled.
  • Only the audit-log check is unknown: roles/iam.securityReviewer is missing — roles/viewer alone cannot read the project IAM policy.
  • Only the KMS checks are missing: either KMS locations is empty (by design — nothing is claimed) or roles/cloudkms.viewer was not granted.
  • Service-account key checks are unknown for every account: roles/iam.securityReviewer is missing.
  • Bucket count is lower than the console shows: the 500-bucket ceiling truncated the walk.

Rate Limits & Sync Behavior

SettingValue
Sync frequencyEvery 4 hours by default (syncInterval is configurable per connection)
AuthSelf-signed JWT (RS256) exchanged for a 1-hour access token, per sync
Request volume~3 calls, plus one per service account (key list) and one per KMS key ring
Ceilings500 buckets, 200 service accounts per sync; beyond either the walk truncates and the sync log carries a warning
Rate limit handlingRouted through CMDash's shared token-bucket limiter at a conservative 300 req/min
Circuit breakerRepeated failures open the breaker and the sync is skipped
Failed sync retryJob queue retries 3 times with exponential backoff (starting at 30s) — see docs/developers/dead-letter.md

Credential Rotation

Service-account JSON keys do not expire. Rotate at least annually.

  1. In GCP: Service Accounts → cmdash-config-reader → Keys → Add key. Both keys work simultaneously.
  2. In CMDash: Settings → Integrations → Google Cloud Platform → Edit, enter the new client email and private key, Save.
  3. Confirm a successful sync, then delete the old key in GCP.

Known Limitations

  • One project per connection. Folders and organisations are not walked. A multi-project estate needs one connection per project, and coverage numbers are per connection.
  • KMS coverage is limited to the locations you list. See above. This is the limitation most likely to mislead, because an empty list produces no failures — which looks like a pass and is not one. CMDash reports it as unmeasured; read it that way.
  • No encryption-at-rest check. GCP encrypts all data at rest by default with no way to turn it off, so a check would be a guaranteed pass that tells you nothing. encryptionAtRestCoverage reads as unmeasured for GCP rather than as a vacuous 100%.
  • No password-policy signal. GCP delegates that to Google Workspace or your external IdP; it is not a project-level setting.
  • No firewall-rule analysis. Open-ingress auditing is an unbounded walk whose only possible answer is a truncated one.
  • Bucket IAM policies are not evaluated. The bucket's public-access-prevention and uniform-access settings are read, not the individual IAM bindings on it.

Ready to see your posture?

Start a 30-day free trial of any paid plan — a card is required, and it converts unless you cancel — or sign up for Free with no card at all.