PUBLIC_SQL_INSTANCE is a Google Cloud Security Command Center finding that means a Cloud SQL instance lists 0.0.0.0/0 as an authorized network, so any IPv4 address can reach the database port and attempt to log in. Fix it by replacing 0.0.0.0/0 with specific CIDR ranges, or by clearing the list and connecting over private IP or the Cloud SQL Auth Proxy.
What the scanner is actually detecting
This is a configuration finding read from the Cloud SQL Admin API. Nothing connects to the database engine, and no credentials are tested. The resource in the finding is the instance itself (asset type sqladmin.googleapis.com/Instance), and the setting in question is settings.ipConfiguration.authorizedNetworks.
| Scanner | Finding title | What it checks |
|---|---|---|
| Security Command Center, Security Health Analytics (SQL_SCANNER detectors) | Public SQL instance, category PUBLIC_SQL_INSTANCE: “A Cloud SQL database instance accepts connections from all IP addresses.” | The instance’s authorized networks list. Google’s remediation text describes the trigger as 0.0.0.0/0 being an allowed network. Available in the Standard and Premium tiers, with real-time scans. |
| Security Command Center, Security Health Analytics | SQL public IP, category SQL_PUBLIC_IP (related, Premium tier) | Whether the instance has an IP address of type PRIMARY, meaning it has a public address at all. |
Google maps PUBLIC_SQL_INSTANCE to CIS Google Cloud Platform Foundation Benchmark control 6.5 (versions 1.1 through 3.0, and 6.2 in version 1.0), NIST 800-53 R5 AC-3 and PCI DSS v4.0 requirement 1.3.1. The two detectors are independent. Narrowing the authorized networks closes PUBLIC_SQL_INSTANCE but leaves SQL_PUBLIC_IP active as long as the public address exists. If you feed these findings into ticketing or reporting, our guide to exporting GCP Security Command Center findings covers the export options.
Real-world risk
Cloud SQL implements the authorized networks list as a local firewall on the instance VM. With 0.0.0.0/0 in that list, every IPv4 host on the internet can open a TCP connection to the public address on the engine port (3306 for MySQL, 5432 for PostgreSQL, 1433 for SQL Server). Google’s own wording is measured: clients still need valid credentials to log in. That is accurate, and it is also the whole problem, because the only remaining control is the database password.
The exposure becomes serious when it combines with other findings Security Health Analytics can raise on the same instance, such as SQL_NO_ROOT_PASSWORD, SQL_WEAK_ROOT_PASSWORD or SSL_NOT_ENFORCED, or with reused application passwords and any pre-authentication flaw in the engine. On its own the finding is not evidence of a breach. The usual cause is mundane: someone added 0.0.0.0/0 so a laptop, BI tool or CI runner with a changing IP could connect, and the entry was never removed.
How to confirm it
List active findings for a project (an organization ID or folders/ID also works as the parent):
gcloud scc findings list projects/PROJECT_ID
--filter='state="ACTIVE" AND category="PUBLIC_SQL_INSTANCE"'
Then inspect the instance’s IP configuration:
gcloud sql instances describe INSTANCE_NAME
--format="yaml(settings.ipConfiguration,ipAddresses)"
Look for value: 0.0.0.0/0 under authorizedNetworks. ipv4Enabled: true and an address of type PRIMARY mean the instance has a public IP. A privateNetwork value means private IP is already configured, which makes the fix much easier. To check every instance in a project:
for i in $(gcloud sql instances list --format="value(name)"); do
echo "== $i"
gcloud sql instances describe "$i" --format="yaml(settings.ipConfiguration)"
done
Save the current configuration before changing anything, because the rollback depends on it:
gcloud sql instances describe INSTANCE_NAME --format=json > INSTANCE_NAME-backup.json
Before you tighten the list, find out who actually connects. On MySQL, SELECT user, host FROM information_schema.processlist; shows current client hosts. On PostgreSQL, SELECT usename, client_addr, application_name FROM pg_stat_activity; does the same. Run these several times across a business cycle, since batch jobs and reporting tools connect intermittently.
How to fix it
Option 1: replace 0.0.0.0/0 with specific ranges
If clients have fixed egress addresses, list them explicitly. The value you pass replaces the entire list, so include every range you want to keep:
gcloud sql instances patch INSTANCE_NAME
--authorized-networks=203.0.113.10/32,198.51.100.0/24
Cloud SQL does not accept IPv6 authorized networks, and you cannot add RFC 1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), because those are implicitly authorized for private IP connections. In the console, the steps Google gives are: open Cloud SQL Instances, select the instance, click Edit, open Connections, delete 0.0.0.0/0 under Authorized networks, add the specific ranges, click Done, then Save.
If the instance is managed by Terraform, change the code instead, or the next apply will put 0.0.0.0/0 back:
settings {
ip_configuration {
authorized_networks {
name = "office-egress"
value = "203.0.113.0/24"
}
}
}
Option 2: clear the list and use the Cloud SQL Auth Proxy
The Cloud SQL Auth Proxy and the Cloud SQL Language Connectors do not need authorized networks. They authorize callers through IAM (the cloudsql.instances.connect permission, included in the Cloud SQL Client role) and encrypt traffic with TLS 1.3. Clear the list:
gcloud sql instances patch INSTANCE_NAME --clear-authorized-networks
Grant the client’s service account the role and start the proxy on the same machine as the application:
gcloud projects add-iam-policy-binding PROJECT_ID
--member=serviceAccount:SA_EMAIL
--role=roles/cloudsql.client
gcloud sql instances describe INSTANCE_NAME --format='value(connectionName)'
./cloud-sql-proxy --port 3306 INSTANCE_CONNECTION_NAME
The client machine must allow outbound TCP 443 (to the Cloud SQL Admin API) and 3307 (to the instance). The hop between the application and the proxy is not encrypted, which is why Google recommends running the proxy on the workload’s own host.
Option 3: move to private IP and remove the public address
This closes both PUBLIC_SQL_INSTANCE and SQL_PUBLIC_IP. It requires private services access on the VPC network, which Google sets up with an allocated range and a peering connection:
gcloud compute addresses create google-managed-services-VPC_NETWORK_NAME
--global --purpose=VPC_PEERING --prefix-length=16
--network=projects/PROJECT_ID/global/networks/VPC_NETWORK_NAME
gcloud services vpc-peerings connect
--service=servicenetworking.googleapis.com
--ranges=google-managed-services-VPC_NETWORK_NAME
--network=VPC_NETWORK_NAME
Add private IP to the instance first, keeping the public address while clients migrate:
gcloud sql instances patch INSTANCE_NAME
--network=projects/PROJECT_ID/global/networks/VPC_NETWORK_NAME
Google’s documented one-step version uses gcloud beta sql instances patch with –no-assign-ip, –enable-google-private-path and –enforce-new-sql-network-architecture. Either way, read the warnings in the next section first. Once every client uses the private address (proxy users add –private-ip when an instance has both), remove the public IP:
gcloud sql instances patch INSTANCE_NAME --no-assign-ip
Prevent it from coming back
Two boolean organization policy constraints stop new exposure: constraints/sql.restrictAuthorizedNetworks blocks adding authorized networks, and constraints/sql.restrictPublicIp blocks configuring public IP. Neither is retroactive, so existing instances keep working until you fix them.
How to verify the fix and rescan
- Re-run the describe command and confirm 0.0.0.0/0 is gone from authorizedNetworks (or that ipv4Enabled is false after Option 3).
- From a network outside the new list, test the port with nc -vz PUBLIC_IP 3306 (or 5432, 1433). A new connection should time out.
- Do not trust open sessions. Google documents that removing an authorized address does not disconnect existing connections from it; restart the instance if you need them dropped.
- Re-run the gcloud scc findings list command. Security Health Analytics sets a remediated finding to INACTIVE the next time it scans, and this detector supports real-time scans.
- If an external scanner also reported the database port open on the public IP, rescan that address.
What can break and how to roll back
- Unlisted clients. Developer laptops, BI and reporting tools, CI runners and third-party ETL services connecting directly will fail. Because existing sessions survive the change, failures may appear hours later when connection pools recycle.
- Read replicas. Google notes that read replicas connect to the primary over a non-proxied connection that relies on the primary’s authorized networks setting. Replicas in the same region with private IP are not affected. Check replication status after the change.
- Private IP migration. Adding private IP to an existing instance restarts it, causing downtime. It cannot be undone: once configured, private IP connectivity cannot be disabled. You cannot assign private IP to an existing instance in a Shared VPC network, and Cloud Shell cannot reach an instance that has only a private IP.
- Removing public IP. This is allowed only if private IP is configured. Instances using a shared or customer-managed CA server mode restart when public IP is disabled.
To roll back an authorized networks change, re-apply the original list from your backup file with –authorized-networks. To restore a removed public address, use gcloud sql instances patch INSTANCE_NAME –assign-ip and then set the authorized networks again. Re-adding 0.0.0.0/0 should only ever be a short, documented emergency measure.
Common false positive reasons
- “Everyone uses the proxy anyway.” Not a false positive. The proxy does not need the entry, but the entry still lets any IPv4 host reach the engine port directly. Remove it.
- Stale finding. The list was fixed, but the finding stays ACTIVE until the next scan evaluates the instance.
- Configuration drift. Terraform, Deployment Manager or a script re-adds 0.0.0.0/0, so the finding appears to “come back”. Fix the source of truth.
- Intended exposure. A deliberately public database is an accepted risk, not a false positive. Document it with compensating controls (strong passwords, –ssl-mode=ENCRYPTED_ONLY) before muting.
- The inverse problem. Google describes the trigger as 0.0.0.0/0. A list full of very large public ranges may not raise this finding while still exposing the instance widely, so review lists by eye too.
FAQ
Does removing 0.0.0.0/0 also clear SQL_PUBLIC_IP?
No. SQL_PUBLIC_IP checks whether the instance has a public address at all. Only removing the public IP (Option 3) closes it.
Does the Cloud SQL Auth Proxy need an authorized network?
No. Google states the public IP does not need to be added as an authorized network for proxy connections. Access is controlled by IAM instead.
Will changing authorized networks restart my instance?
Google’s documentation says existing connections from a removed address stay open unless you restart the instance, so the list change does not drop live sessions. Adding private IP to an existing instance, by contrast, does restart it.
Can I add a 10.x range to authorized networks?
No. RFC 1918 ranges cannot be added because they are implicitly authorized for private IP connections. Non-RFC 1918 private ranges do need to be listed.
Tracking this finding across many hosts
Cloud configuration findings like this one usually sit alongside host-level findings from network and vulnerability scanners. SITEY, a self-hosted vulnerability management platform, imports findings from 16 scanners and merges duplicates within each scanner (not across scanners). Its AI triage suggests false positives with supporting evidence, and a person makes the final decision.