GCP / Guide 6
Security baseline: identity, detection and encryption
2-step verification, service accounts without keys, Security Command Center, audit logs, Cloud KMS and Secret Manager.
Network controls limit where an attacker can go. Most Google Cloud incidents, though, start with an identity: a leaked service account key, a phished administrator, a personal Gmail account that still has Owner on a project. This guide covers the baseline that sits alongside the network: identity, Security Command Center, audit logs, encryption with Cloud KMS, Secret Manager and the logs you'll need on the day something happens.
Identity comes first
- 2-step verification is required. Google is enforcing 2SV for Google Cloud console and Firebase console access in phases: personal Google accounts first (from May 2025), then Cloud Identity accounts that don't use single sign-on. Federated users satisfy it through their own identity provider's MFA. Don't wait for the deadline: enforce it in the Admin console now, with security keys or passkeys for administrators.
- Sign in through your identity provider. Federate Cloud Identity or Workspace with Microsoft Entra ID, Okta or similar, so joiners, leavers and MFA are handled in one place. Workforce Identity Federation gives console and gcloud access to users from an external identity provider without syncing them into Cloud Identity at all.
- Keep outsiders out. The
iam.allowedPolicyMemberDomainsorganisation policy stops roles being granted to personal Gmail accounts and other domains. - No standing admin access. Privileged Access Manager for Owner and admin roles; two protected super admins (Guide 2).
- Review access quarterly.
Get-GcpIamReport.ps1lists public grants, basic roles, grants to outside domains, powerful default service accounts and user-managed keys across projects.
Service accounts without keys
A downloaded service account key is a password that never expires on its own, works from anywhere, and ends up in repositories and laptops. New organisations block key creation by default; for older ones, set iam.managed.disableServiceAccountKeyCreation and remove the keys you have:
- Workloads on Google Cloud: attach a dedicated service account (never the Compute Engine default one).
- GKE: Workload Identity Federation for GKE maps Kubernetes service accounts to IAM.
- Outside Google Cloud: Workload Identity Federation with the platform's own tokens (GitHub Actions, Azure DevOps, AWS, Azure, or any OIDC or SAML identity provider on-premises).
- People: impersonation, with
--impersonate-service-account.
# User-managed keys on every service account in a project, oldest first
$sas = gcloud iam service-accounts list --project=prj-notes-test --format=json | ConvertFrom-Json
foreach ($sa in $sas) {
gcloud iam service-accounts keys list --iam-account=$($sa.email) --managed-by=user --format=json |
ConvertFrom-Json | Select-Object @{n='Account';e={$sa.email}}, @{n='Key';e={($_.name -split '/')[-1]}}, validAfterTime
}
Security Command Center
Security Command Center (SCC) is Google Cloud's built-in security posture and threat detection service. Activate it at the organisation level so every project is covered.
| Tier | Cost | What you get |
|---|---|---|
| Standard | No extra charge | Basic posture findings (Security Health Analytics for common misconfigurations such as public buckets, open firewall ports and keys), for Google Cloud |
| Premium | Pay as you go or subscription | Standard plus all Security Health Analytics detectors, Event Threat Detection (from logs), Container and VM Threat Detection, attack path simulation, compliance reports (CIS, PCI DSS, NIST and more) |
| Enterprise | Deprecated | The multi-cloud tier is being retired (shutting down 21 May 2027) and migrated to Premium |
A sensible start: Standard everywhere today; Premium for the organisation once the Standard findings are under control, or straight away if you hold regulated data. Route high and critical findings to people with notification configs (Pub/Sub to chat or ticketing), and mute findings that don't apply with a documented mute rule rather than ignoring them.
# Active high and critical findings across the organisation, grouped by category
gcloud scc findings list organizations/123456789012 --location=global `
--filter="state=\"ACTIVE\" AND (severity=\"HIGH\" OR severity=\"CRITICAL\") AND mute!=\"MUTED\"" --format=json |
ConvertFrom-Json | Group-Object { $_.finding.category } | Sort-Object Count -Descending | Select-Object Count, Name
Audit logs
Cloud Audit Logs record who did what, where and when:
| Log | Records | Default |
|---|---|---|
| Admin Activity | Configuration changes: creating a VM, changing IAM | Always on, free, kept 400 days |
| System Event | Google's own actions on your resources (live migration) | Always on, free, kept 400 days |
| Data Access | Reads of configuration and data: who read a secret or an object | Off (except BigQuery); charged when on |
| Policy Denied | Calls blocked by VPC Service Controls or organisation policies | On, can be excluded |
- Turn on Data Access logs where it matters: Secret Manager, Cloud KMS, IAM, and Cloud Storage buckets holding sensitive data. Not everywhere: they're billed by volume.
- Keep a copy centrally: an aggregated log sink at the organisation sends audit logs from every project to a locked log bucket (with a retention lock) or to BigQuery in the logging project.
# Organisation-wide sink of all audit logs to a central log bucket
gcloud logging sinks create sink-org-audit `
logging.googleapis.com/projects/prj-logging/locations/us/buckets/org-audit `
--organization=123456789012 --include-children `
--log-filter='logName:"cloudaudit.googleapis.com"'
Encryption and secrets
Everything in Google Cloud is encrypted at rest by default with Google-managed keys. For data where you need control over who can decrypt, or proof that you can revoke access, use customer-managed encryption keys (CMEK) in Cloud KMS:
- Keep keys in a separate project (
prj-security), so project Owners of the workload can't manage their own keys. - Set a rotation period (90 days to a year); old key versions still decrypt old data.
- Grant
roles/cloudkms.cryptoKeyEncrypterDecrypterto the service agent of the service that uses the key, on that key only. - Key destruction is delayed (30 days by default) and permanent after that. Alert on
DestroyCryptoKeyVersion.
Secrets (database passwords, API keys) belong in Secret Manager, not in metadata, environment variables baked into images, or code. Grant roles/secretmanager.secretAccessor on each secret to the service account that needs it, set up rotation with Pub/Sub notifications, and turn on Data Access logs for Secret Manager so every read is recorded.
Harden the VMs
- Shielded VM (secure boot, vTPM, integrity monitoring) on every VM: enforce with
compute.requireShieldedVm. - OS Login and IAP, not SSH keys in metadata and open ports (Guide 5).
- No external IPs except where you've decided otherwise; Cloud NAT for outbound.
- Patch with VM Manager and scan with SCC's VM vulnerability findings (Guide 7).
- Confidential VMs for workloads that need memory encrypted in use.
A security baseline checklist
| Area | Baseline | How to check |
|---|---|---|
| Identity | 2SV for everyone; federated sign-in; no basic roles for people; outside domains blocked | Get-GcpIamReport |
| Service accounts | No user-managed keys; no default service accounts with Editor | Get-GcpIamReport |
| Management ports | No SSH or RDP from the internet; IAP and OS Login instead | Get-GcpFirewallReport |
| Data exposure | Public access prevention enforced; no public Cloud SQL IPs; VPC Service Controls for sensitive data | Get-GcpBucketPublicAccessReport |
| Organisation policies | Secure-by-default set plus locations, external IPs, OS Login, Shielded VM | gcloud org-policies list |
| Detection | Security Command Center on at organisation level; findings routed to people | SCC console |
| Logging | Organisation audit log sink to a locked bucket; Data Access logs for secrets and keys | gcloud logging sinks list --organization |
| Backup | Backup vaults with enforced retention for production | Guide 8; Get-GcpBackupCoverage |