Serial port access on a Compute Engine VM exposes an interactive console that bypasses your firewall and network controls, giving anyone with a key a direct line to the instance. Disable it with gcloud compute instances add-metadata --metadata serial-port-enable=false and enforce a constraints/compute.disableSerialPortAccess org policy.
The interactive serial console is one of those features that sounds harmless until you realize what it actually does. It gives you a text-based connection straight to a VM, the same way a physical serial cable would plug into a server in a data center. That is genuinely useful when an instance has a broken network stack or won't finish booting. It is also a side door into your VM that ignores your VPC firewall rules entirely.
The VM Serial Port Access Enabled check (compute_serialport) flags any Compute Engine instance where interactive serial port access is turned on. Below is what that means, why it is risky, and how to close the door without losing your emergency debugging path.
What this check detects
Lensix raises this finding when a Compute Engine VM has the serial-port-enable metadata key set to true, either on the instance directly or inherited from project-level metadata. When this is enabled, you can connect to the VM's interactive serial console over Google's infrastructure and get a login prompt.
You can spot the setting in instance metadata. A quick way to list affected instances across a project:
gcloud compute instances list \
--format="table(name, zone, metadata.items.filter(key:serial-port-enable).extract(value))"
Any instance returning true in that column is what the check catches. Project-wide metadata matters too, since it applies to every VM that does not override it:
gcloud compute project-info describe \
--format="value(commonInstanceMetadata.items)"
Note: This is the interactive serial console, not the read-only serial port output you get from gcloud compute instances get-serial-port-output. The read-only output is for viewing boot logs and is generally fine. Interactive access lets you type into a login shell.
Why it matters
The core problem is that the interactive serial console does not respect your VPC firewall. Connections come in through Google's serial console gateway at ssh-serialport.googleapis.com, not through your instance's network interface. So all the careful work you did locking down port 22, restricting source ranges, and setting up Identity-Aware Proxy can be sidestepped if someone has the right credentials and serial access is on.
Here is how that plays out in a real attack chain:
- Firewall bypass. An attacker who cannot reach SSH because your firewall blocks their IP can still attempt a serial console session, since that traffic never touches your VPC rules.
- Password-based brute force. If serial access is enabled and a local account has a password set, the serial console presents a password login prompt. That is a brute-force target outside your normal SSH hardening.
- Lateral movement and persistence. An attacker who has already compromised an identity with
compute.instances.setMetadatacan flip serial access on, set a local password through a startup script, and keep a backdoor that survives firewall changes.
Warning: Serial console sessions are not subject to your organization's SSH key management or OS Login policies in the same way network SSH is. If you rely on OS Login for centralized access control, an enabled serial port is a gap in that control.
For compliance, this also shows up in CIS Google Cloud Platform Foundations Benchmark control 4.5, which explicitly recommends that serial port access be disabled. Leaving it on across a fleet is the kind of finding auditors flag and that turns into a remediation ticket with a deadline.
How to fix it
Remediation is straightforward: set serial-port-enable to false. You can do this per instance, per project, or in your infrastructure code. Pick the level that matches how the setting got enabled in the first place.
Fix a single instance
gcloud compute instances add-metadata INSTANCE_NAME \
--zone=ZONE \
--metadata=serial-port-enable=false
Fix it project-wide
If the setting lives in project metadata, remove or disable it there so it stops applying to every VM:
gcloud compute project-info add-metadata \
--metadata=serial-port-enable=false
Danger: Before disabling, confirm no one is mid-session relying on serial access to recover a broken VM. Cutting the serial console on an instance that has lost network connectivity removes your last remote path in. Verify the instance is healthy and reachable over SSH first.
Console steps
- Open the Google Cloud Console and go to Compute Engine > VM instances.
- Click the instance name, then Edit.
- Scroll to Remote access and uncheck Enable connecting to serial ports.
- Click Save.
Fix it in Terraform
If you manage VMs with Terraform, set the metadata key explicitly so the state is enforced on every apply:
resource "google_compute_instance" "app" {
name = "app-server"
machine_type = "e2-medium"
zone = "us-central1-a"
metadata = {
serial-port-enable = "false"
}
# ... boot_disk, network_interface, etc.
}
Tip: Rather than chasing instances one at a time, set serial-port-enable=false in project metadata once and remove any per-instance overrides. New VMs will inherit the safe default automatically.
How to prevent it from happening again
Fixing existing instances is the easy part. Keeping serial access off as your environment grows is what actually reduces risk. The strongest control here is an organization policy constraint that blocks serial access at the platform level.
Enforce the org policy constraint
Google provides a boolean constraint, constraints/compute.disableSerialPortAccess, that prevents serial port access from being enabled anywhere it applies. Set it at the organization level so it covers every project:
gcloud resource-manager org-policies enable-enforce \
compute.disableSerialPortAccess \
--organization=ORGANIZATION_ID
Or define it as code with Terraform so the policy is version-controlled:
resource "google_organization_policy" "disable_serial_port" {
org_id = var.org_id
constraint = "compute.disableSerialPortAccess"
boolean_policy {
enforced = true
}
}
Note: With this constraint enforced, attempts to set serial-port-enable=true are rejected outright. That closes the path where a compromised or careless identity re-enables serial access after you cleaned it up.
Gate it in CI/CD
If teams provision VMs through Terraform, catch the misconfiguration before it merges. A policy-as-code tool like Open Policy Agent (Conftest) or Checkov can fail the pipeline when serial access is enabled:
package main
deny[msg] {
resource := input.resource.google_compute_instance[name]
resource.metadata["serial-port-enable"] == "true"
msg := sprintf("Instance '%s' has serial port access enabled", [name])
}
Wire that into your plan stage so a pull request that turns serial access on never reaches production.
Tip: Pair the CI gate with continuous monitoring in Lensix. The org policy stops new violations, the CI gate catches them in review, and Lensix surfaces anything that slipped through legacy infrastructure or manual console changes.
Best practices
- Default to off, enable temporarily. Keep serial access disabled fleet-wide. When you genuinely need it for recovery, enable it on the one instance, do the work, then disable it again.
- Never set passwords on local Linux accounts. If serial access ever gets turned on, a passwordless account gives no brute-force surface. Rely on SSH keys and OS Login instead.
- Use OS Login and IAP for normal access. Route day-to-day instance access through OS Login with IAM-controlled permissions and IAP tunneling, so access is auditable and centrally managed.
- Log serial console use. Serial console connections are recorded in Cloud Audit Logs. Alert on them, since legitimate use is rare and an unexpected session is worth investigating.
- Review project metadata regularly. Project-level metadata propagates to every VM, so a single mistake there has wide blast radius. Audit it alongside instance settings.
The serial console is a recovery tool, not an access method. Treat it that way: off by default, enabled deliberately and briefly, and blocked at the org level so a stray setting cannot quietly reopen a path around your firewall.

