# GCP Cloud SQL Instance Does Not Require SSL

> A Cloud SQL instance that does not set requireSsl accepts unencrypted client connections, exposing credentials and query traffic on the network path. ZopNight rule RC-137 fires when the discoverer finds no SSL-enforcement marker on an enriched instance, inferred from absence and gated on the always-present tier field. Severity is high, saving $0.

Source: https://zop.dev/integrations/gcp/recommendations/gcp-cloud-sql-instance-does-not-require-ssl
Updated: 2026-08-19

---

## Plaintext between app and database

When `settings.ipConfiguration.requireSsl` is unset, a Cloud SQL instance will happily
accept a client that never negotiates TLS. Every such connection carries the database
password in the authentication handshake and the query results in the clear, readable by
anything positioned on the network path. Individual clients *may* still connect encrypted,
since the setting governs what the server tolerates rather than what clients do. That is
exactly the problem: one misconfigured connection string anywhere in the fleet silently downgrades to
plaintext and nothing complains.

## requireSsl and the marker ZopNight looks for

Detection is inferred from absence, with a guard that keeps the inference honest. The
discoverer enriches every Cloud SQL instance from its settings proto and writes an
SSL-enforcement marker only when `requireSsl` is actually set. The marker has no false
form. The rule first requires the `tier` metadata field, which the enricher stamps from
`settings.tier` on every genuine Cloud SQL instance; if that anchor is missing, the resource
was never enriched and the rule abstains. Only an enriched instance lacking the SSL marker
produces the finding. Reads run under `roles/cloudsql.viewer` (`cloudsql.instances.list`)
with discovery under `roles/cloudasset.viewer`.

## Private IP does not excuse it

A tempting dismissal is "the instance is private, interception is moot." The rule fires on
private-IP instances deliberately. VPC-internal traffic is only as trustworthy as the least
compromised workload inside the VPC. After an initial foothold, lateral movement puts an
attacker on the same network path the plaintext crosses. Encryption in transit is the
control that stays standing when the network boundary has already failed.

## Confirm enforcement on one instance

```bash
gcloud sql instances describe INSTANCE_NAME \
  --format="value(settings.ipConfiguration.requireSsl)"
```

Anything other than `True` reproduces the finding for that instance.

## Rolling out enforcement without an outage

Flipping enforcement before clients are ready is a self-inflicted outage, so sequence it:
download the server CA certificate, update connection strings (`ssl-mode=REQUIRED` for
MySQL, `sslmode=require` for PostgreSQL) and verify every application connects encrypted
while the server still permits both. Only then set the instance to require SSL (or trusted
client certificates for mutual TLS). Done in that order, the change is a non-event; done in
the other order, it is a hard cutoff for every non-TLS client at once.
