On Google Compute Engine, the two identities that matter most on a VM are the service account attached to it and the SSH keys that let people in, and both are often left at insecure defaults. A VM running as the default compute service account can hand its token to any code on the box, and metadata-based SSH keys give every key holder sudo through the google-sudoers group. This guide covers how VM identity works (attached service accounts, access scopes and the metadata server), how to replace metadata SSH keys with OS Login, Shielded VM boot integrity, the organisation policy constraints that enforce a baseline, and what to log. It goes deeper on VMs than our Google Cloud IAM and Service Accounts Guide and is written for Google Cloud platform and security engineers.
Key takeaways
- Never run workloads as the default compute service account. [email protected] may hold the Editor role on the project; attach a dedicated user-managed service account per workload.
- Scopes and IAM both apply. Google states that an application's access is limited by both the attached account's IAM roles and the instance's access scopes; use the cloud-platform scope with narrow IAM roles rather than relying on scopes as a control.
- OS Login replaces metadata keys. It ties Linux accounts to Google identities and IAM, grants sudo only to principals with admin login permission, and can be enforced across the organisation.
- Metadata key holders are administrators. The guest agent adds every user provisioned from metadata SSH keys to google-sudoers, which grants sudo on the VM.
- Turn on Secure Boot. vTPM and integrity monitoring are on by default for Shielded VMs, but Google recommends enabling Secure Boot where possible, and integrity failures should alert, not just log.
How identity and access work in Compute Engine
A Compute Engine VM has an attached service account, and code on the VM obtains short-lived OAuth access tokens for that account from the metadata server at metadata.google.internal (requests must carry the Metadata-Flavor: Google header). Google's guidance is that applications should authenticate through the attached service account rather than storing keys or user credentials in the instance, image or code.
| Concept | What it is | Security implication |
|---|---|---|
| Attached service account | One per VM; one account can be attached to many VMs | Changing roles on a shared account affects every VM using it |
| Default compute service account | [email protected], used unless you choose another | May be automatically granted Editor on the project |
| Access scopes | Legacy OAuth scope limits set on the instance | Access is the intersection of scopes and IAM; scopes are not a substitute for IAM |
| Metadata server | Serves instance metadata, tokens and OS Login profiles to the guest | Server-side request forgery to the metadata server exposes the token (MITRE ATT&CK T1552.005) |
| Service Account User role (roles/iam.serviceAccountUser) | Allows attaching an account to a VM (iam.serviceAccounts.actAs) | Granted at project level, it lets a developer launch a VM as the most privileged account in the project |
Choosing the identity: create one user-managed service account per workload, named for its purpose, grant it predefined or custom roles on specific resources, and attach it with the cloud-platform scope so that IAM is the single control. Grant roles/iam.serviceAccountUser on that individual account only to the team or pipeline that deploys the workload. For organisations created after 3 May 2024, the constraint iam.automaticIamGrantsForDefaultServiceAccounts is enforced by default and stops the automatic Editor grant; older organisations should enforce it and remove Editor from existing default accounts after checking what depends on them. See the Google Cloud Organization Policy Guide.
Admin roles and privileged access
SSH and serial console access to a VM are privileged operations. Compute Engine offers two models for Linux SSH:
| Aspect | Metadata-based SSH keys | OS Login |
|---|---|---|
| Where keys live | ssh-keys values in project metadata (all VMs) or instance metadata | The user's OS Login profile, linked to their Google identity |
| Who controls access | Anyone who can set project or instance metadata | IAM: roles/compute.osLogin (login) or roles/compute.osAdminLogin (login with sudo) |
| Admin rights | Guest agent adds every provisioned user to google-sudoers, granting sudo | pam_oslogin_admin checks the adminLogin policy; sudo is granted through /var/google-sudoers.d/ only for authorised admins |
| Lifecycle | Keys persist until someone removes them from metadata | Linux account lifecycle follows the Google identity; removing IAM access removes login |
| Strong authentication | None beyond the key | Optional two-factor verification and SSH certificate options |
| Enforcement | block-project-ssh-keys per instance | enable-oslogin=TRUE metadata; organisation constraint compute.managed.requireOsLogin |
The guest agent's behaviour is documented in Google's open source guest-agent repository: if OS Login is not used, the agent creates local accounts from metadata keys, maintains their authorized_keys files and adds them all to google-sudoers. In other words, a project-wide metadata key is an administrator credential on every VM in the project that does not block project keys. Users with compute.instances.setMetadata or the project-level common metadata permission can add their own key, which makes those permissions as sensitive as root. ATT&CK tracks this persistence pattern as T1098.004 SSH Authorized Keys.
- Prefer OS Login everywhere: enable it at project level with enable-oslogin=TRUE, then enforce compute.managed.requireOsLogin so metadata updates cannot disable it. Grant roles/compute.osLogin broadly to operators and roles/compute.osAdminLogin only through just-in-time elevation.
- Connected users inherit the VM's identity: Google notes that a user connected through OS Login can use all IAM permissions granted to the VM's attached service account. Admin login to a VM running as a powerful account is effectively that account.
- Close the internet path: combine OS Login with Identity-Aware Proxy TCP forwarding so port 22 is never open to 0.0.0.0/0, and deny external IPs with compute.vmExternalIpAccess. See the Identity-Aware Proxy Guide.
- Serial console: enforce compute.disableSerialPortAccess unless a team has a documented break-glass need.
- Windows VMs: OS Login is Linux only. Manage Windows administrator accounts with your directory and a privileged access tool rather than generated local passwords shared by email.
For SSH key lifecycle and CA-based alternatives, see the SSH Key Management Guide.
Integrations, API tokens and service accounts
Most real Compute Engine compromises turn into Google Cloud compromises through the VM's service account token.
- Token theft path: an application flaw that lets an attacker make the server fetch an internal URL can retrieve the access token from the metadata server. The header requirement blocks naive requests, but full request forgery or code execution does not care. The defence is least privilege on the attached account and detection of the token being used from outside Google Cloud.
- No keys on VMs: do not create service account keys and copy them onto VMs, images or startup scripts. Enforce iam.managed.disableServiceAccountKeyCreation and use the attached account. See the Google Cloud Service Account Keys Security Guide.
- Startup scripts and metadata: instance metadata, including startup-script values, is readable by any process on the VM through the metadata server and by any principal with compute read permissions. Never place secrets in metadata; read them from Secret Manager with the attached account.
- Cross-project attachment: keep iam.disableCrossProjectServiceAccountUsage enforced unless attaching a service account from another project is a deliberate design.
- Agents and integrations: monitoring, backup and configuration management agents running on VMs use either the attached account or their own credentials. Inventory which, and give agents their own scoped accounts where the product supports it.
Shielded VM and boot integrity
| Feature | What it does | Default |
|---|---|---|
| Secure Boot | UEFI verification of boot component signatures; boot halts if verification fails | Google recommends enabling it if possible |
| Virtual TPM (vTPM) | Per-VM TPM 2.0-compatible processor that generates and stores keys | Enabled |
| Measured Boot | Records hashes of firmware, bootloader, kernel and drivers | With vTPM |
| Integrity monitoring | Compares each boot against a trusted baseline and reports to Cloud Logging and Security Command Center | Enabled |
Shielded VMs use Google-built, signed UEFI firmware whose signature is verified whether or not Secure Boot is on. Enforce constraints/compute.requireShieldedVm so all new VMs are Shielded VMs. Secure Boot can break custom kernels or unsigned drivers, so test images before enabling it fleet-wide. Boot-level persistence is tracked in ATT&CK as T1542 Pre-OS Boot. For the Linux side of boot signing, see the Linux Secure Boot and Shim Guide; for memory encryption options, see the Confidential Computing Guide.
Logging and detection
| Signal | Source | What to alert on |
|---|---|---|
| compute.instances.setMetadata and compute.projects.setCommonInstanceMetadata | Admin Activity audit logs | Changes to ssh-keys, enable-oslogin, block-project-ssh-keys or startup-script |
| compute.instances.setServiceAccount and instance inserts with a service account | Admin Activity audit logs | VMs created or changed to run as the default account or a privileged account |
| earlyBootReportEvent and lateBootReportEvent | Shielded VM integrity logs in Cloud Logging | Any failed boot integrity check |
| OS Login events | OS Login audit logs | Admin logins outside approved elevation windows |
| Service account token use | Data Access logs and Security Command Center threat detection | The VM account's credentials used from an IP outside Google Cloud |
| Firewall rule changes | Admin Activity audit logs | Rules opening 22 or 3389 to 0.0.0.0/0 |
Admin Activity logs are always on; Data Access logs need enabling for the services you care about. See the Google Cloud Audit Logs and Log Sinks Guide.
Metrics:
- Percentage of running VMs attached to the default compute service account, target zero.
- Number of projects with ssh-keys in project metadata, and projects without OS Login enforced.
- Percentage of VMs with Secure Boot enabled, and count of integrity monitoring failures investigated.
- Number of principals holding roles/compute.osAdminLogin standing (not just in time).
- VMs with external IP addresses or firewall rules exposing SSH or RDP to the internet.
Hardening baseline
- Discover: list VMs by attached service account, projects with metadata SSH keys, VMs with external IPs and Shielded VM settings. Security Command Center's posture findings help here.
- Service accounts: create dedicated accounts per workload, move VMs onto them in a test environment first (check the current documentation for whether the VM must be stopped), then remove Editor from the default account. Roll back by reattaching the previous account.
- OS Login: enable enable-oslogin=TRUE on a pilot project, grant roles/compute.osLogin and roles/compute.osAdminLogin to groups, confirm access, remove project-wide ssh-keys, then enforce compute.managed.requireOsLogin at folder level. Roll back by setting the metadata value to FALSE on the pilot project before enforcing the constraint.
- Network access: remove internet-facing SSH and RDP firewall rules, use IAP TCP forwarding, and enforce compute.vmExternalIpAccess with an allow list for genuine exceptions.
- Boot integrity: enforce compute.requireShieldedVm, enable Secure Boot on tested images and route integrity failures to the SOC.
- Remaining constraints: compute.disableSerialPortAccess, iam.managed.disableServiceAccountKeyCreation and iam.disableCrossProjectServiceAccountUsage.
- Patch and image pipeline: build hardened images from a pipeline, patch through OS management tooling and replace rather than repair long-running VMs. See the Linux Server Hardening Guide.
Small versus large estates: a single project can switch to OS Login and dedicated service accounts in a day. Large organisations should enforce constraints at folder level after a pilot, publish a golden image with Secure Boot, and run just-in-time grants for admin login through their privileged access process.
Framework mapping
| Control | NIST SP 800-53 Rev 5 | CIS Controls v8.1 | MITRE ATT&CK risk addressed |
|---|---|---|---|
| Dedicated, least-privilege service accounts | AC-6 Least Privilege | 5 Account Management; 6 Access Control Management | T1552.005 Cloud Instance Metadata API; T1078.004 Cloud Accounts |
| OS Login, two-factor, no metadata keys | IA-2 Identification and Authentication; AC-17 Remote Access | 6.5 Require MFA for Administrative Access | T1098.004 SSH Authorized Keys; T1021.004 SSH |
| No external IPs, IAP forwarding | SC-7 Boundary Protection | 12 Network Infrastructure Management | T1021.004 SSH |
| Shielded VM, Secure Boot, integrity monitoring | SI-7 Software, Firmware, and Information Integrity | 4 Secure Configuration | T1542 Pre-OS Boot |
| Organisation policy constraints | CM-6 Configuration Settings | 4.1 Establish and Maintain a Secure Configuration Process | Configuration drift |
Common misconfigurations
- Default account with Editor: VMs created from the console with defaults, so any code on them can modify most of the project.
- Project-wide SSH keys: a former contractor's key left in project metadata, giving sudo on every VM that does not block project keys.
- setMetadata treated as harmless: developers granted roles that include instance metadata changes, letting them add their own key or startup script to production VMs.
- Secrets in startup scripts: database passwords or API keys in startup-script metadata, readable from the metadata server and by anyone with compute viewer rights.
- Port 22 open to the internet: firewall rules allowing 0.0.0.0/0 to SSH, often left from a test, instead of IAP forwarding.
- Integrity logs nobody reads: Shielded VM integrity failures logged but never alerted, so a modified bootloader goes unnoticed.
Practitioner checklist
- Attach a dedicated user-managed service account to every VM and remove Editor from the default compute account.
- Grant roles/iam.serviceAccountUser on individual service accounts, never at project level.
- Enable OS Login and enforce compute.managed.requireOsLogin; remove project-wide ssh-keys metadata.
- Grant roles/compute.osAdminLogin just in time, with two-factor verification for administrators.
- Remove internet-facing SSH and RDP rules and use IAP TCP forwarding; enforce compute.vmExternalIpAccess.
- Enforce compute.requireShieldedVm, enable Secure Boot on tested images and alert on integrity failures.
- Keep secrets out of metadata and startup scripts; use Secret Manager.
- Enforce compute.disableSerialPortAccess and service account key creation constraints.
- Alert on metadata, service account and firewall changes in Admin Activity logs.
Standards and references
- Google Cloud: Service accounts on Compute Engine
- Google Cloud: About OS Login
- Google Cloud: Add SSH keys to VMs
- Google Cloud: Shielded VM
- Google Cloud: Guest agent for Google Compute Engine
- Google Cloud: OS Login guest environment
Related NHI Mgmt Group resources: Google Cloud IAM and Service Accounts Guide · Google Cloud Organization Policy Guide · Google Cloud Service Account Keys Security Guide · SSH Key Management Guide · Google Cloud Security Guide