Cloud SQL with private IP only: the Good, the Bad and the Ugly
By: Guillaume Blaquiere
Tags: MySQL, Google Cloud Platform, Networking, Cloud Sql, Database

The Good: Compute Engine connectivity

Because this “private IP security pattern” has been built for legacy architecture (i.e. on-prem VM and private network), the constraint perfectly fits the IaaS world (i.e. Compute Engine + VPC).

I mention Compute Engine but it’s also true with all the services that use Compute Engine instances: Dataflow, Dataproc, GKE,…

Deploy your VM in the same VPC as the Cloud SQL instances private IP.

Use the Cloud SQL instance private IP to reach it from your app which is deployed on Compute Engine.

Open the required firewall rules if needed.

That’s “good”!!

Note: the usage of Cloud SQL proxy in this case can be discussed. It adds an additional security layer by checking the authorization against IAM services and the encrypted communication, but also add additional latency and potential point of failure. It’s matter of tradeoffs.

The Bad: Serverless services connectivity

When we are using the “new world” of the Cloud, the serverless paradigm, the things aren’t so nice with Cloud Run, Cloud Functions and App Engine.

Indeed, when you deploy on serverless platforms, by definition, you don’t have to manage the servers, and thus, the servers aren’t in your VPC. And thus, it’s not possible, out of the box, to reach the Cloud SQL private IP connected to your project’s VPC.

If you dig into Cloud Run, Cloud Functions and App Engine, you can find how to connect your Cloud SQL instance to your serverless service thanks to a built-in feature:

Add the Cloud SQL instance connection name to your configuration and automatically a tunnel is created with the instance start.

In reality, it’s a connection opened by Cloud SQL proxy binary in Unix socket mode. But this solution works only if the Cloud SQL instance has a public IP.

In case of private IP only, and even if a private IP connection mode exist with Cloud SQL proxy, it doesn’t work!

Connect the serverless services to the Cloud SQL private IP

Hopefully a solution exists. You can click on the private IP tab in the Cloud SQL connection tutorials for this (example for Cloud Functions).

In summary, you need to:

Create a serverless VPC connector , in the same region as your serverless service. And, of course, connected to the same VPC as your Cloud SQL instance.

Note that today, all the regions are supported by serverless VPC connector but it wasn’t the case for a while.

, in the same region as your serverless service. And, of course, connected to the same VPC as your Cloud SQL instance. Note that today, all the regions are supported by serverless VPC connector but it wasn’t the case for a while. Deploy your serverless service with this VPC connector , supported by Cloud Run, Cloud Functions and App Engine

, supported by Cloud Run, Cloud Functions and App Engine In your app, use the Cloud SQL private IP (instead of the Unix socket connection)

This solution works but introduces a new network element, an additional cost and a possible new point of failure in the connectivity chain.

That’s “bad”!!

The Ugly: Local environment connectivity

When you want to query your database, sometime in production, through your preferred database IDE installed on your computer, you need to connect your local environment to the Cloud SQL instance, and thus via the private IP.

Because the Cloud SQL instance doesn’t have a public IP, you can’t reach it directly from the internet (your computer), and Cloud SQL proxy also can’t. The solution here is to create a bastion host: a bridge VM between the outside world (public IP) and the inside world (private IP).

For this, create a small Compute Engine instance, a f1-micro for example (very affordable, less than $5 per month), to achieve this.

Security team requirement: All the VM mustn’t have a public IP. Of course, 0.0.0.0/0 firewall rules aren’t allowed, especially on port 22 (ssh)

Bastion host without public IP

New constraints to deal with! But it’s consistent: if you close the door, it’s to for letting to window open!!

So no problem, Google Cloud offers a great and easy solution for this: Identity-Aware Proxy (IAP)

With IAP, you only have to grant a Google Cloud IAP IP range 35.235.240.0/20 to access to your Compute Engine on port 22 in your firewall rules. And thus, it’s not required to open 0.0.0.0/0 (the whole internet) to reach the Compute Engine ssh port!

Then, use the gcloud SDK to connect to your bastion host Compute Engine instance

gcloud compute ssh <Instance Name> --zone=<Your Zone>

And the magic happens! The gcloud SDK detects automatically the lack of public IP of the Compute Engine instance and opens automatically an IAP tunnel to connect in SSH the instance. It’s invisible for the user!

The credential (here the user account, but it could be service account) needs to have enough permission to create an IAP tunnel. The roles are roles/iap.tunnelResourceAccessor to create the tunnel, and roles/compute.instanceAdmin.v1 to acces to the VM

Now, you are connected to the bastion host, but you haven’t yet connected the Cloud SQL instance…

IAP tunneling, SSL port forwarding and Cloud SQL proxy

And there, the “ugly” part starts. We have 2 things to achieve