Static Outbound IP example for Cloud Run applications
This repository contains an example of a Google Cloud Run application that runs an SSH tunnel through a GCE instance within the container to route outbound requests of the Cloud Run application through the static IP of the GCE instance.
Before you begin
Launch Google Cloud Shell (recommended, as it has al the tools required pre-installed).
Clone this repository and
Create a tunnel instance on GCE
Create a set of ssh key pairs so that your container can SSH into the VM.
ssh-keygen -q -f ssh_key
Note that the private SSH key, which is a secret, will be bundled into the container image, which can be compromised if anyone gets access to your source code/build system. You can also use other means of delivering this key to the container in the runtime (e.g. by downloading from a GCS bucket, or using a secrets manager).
Create a Google Compute Engine instance (
us-central1with name "tunnel"):
gcloud compute instances create "tunnel" \ --zone=us-central1-b \ --machine-type=f1-micro
(Optional) You can go to the Cloud Console and promote this VM’s ephemeral IP address to be a "static IP address". But, long as you don't delete this VM, its IP address will not change.
Upload the SSH public key (not a secret) to the VM to authenticate as user "tunnel":
gcloud compute instances add-metadata "tunnel" \ --zone=us-central1-b --metadata-from-file ssh-keys=<(echo "tunnel:$(cat ssh_key.pub)")
(Optional) Inspect the application source code
Take time to understand:
entrypoint.sh: runs a SSH client (as SOCKS5 TCP proxy server via GCE VM) and the
flaskPython application server.
This script sets
HTTPS_PROXY=socks5://localhost:5000environment variable to the Python app to use the proxy. However, this
HTTPS_PROXYenvironment variable works the same way on many other languages, including Go as well.
HTTPS_PROXYenvironment variable you don't need to update your code to use the SOCKS5 proxy.
__init__.pywaits for the SSH port-forwarding server configured via
HTTPS_PROXYto be accessible.
app.py: starts a flask app querying https://ifconfig.me/ip and sends its result back.
Deploy Cloud Run application
Set up $PROJECT variable in your shell to your current project.
PROJECT="$(gcloud config get-value core/project -q)"
Build and push the container image to Google Conatiner Registry.
gcloud builds submit --tag gcr.io/$PROJECT/sample-tunnel
EXTERNAL_IPaddress of the Compute Engine VM named "tunnel" you created earlier:
gcloud compute instances list --filter=name=tunnel
Deploy to Cloud Run, by setting GCE_IP environment variable to the IP of the VM:
gcloud beta run deploy sample-tunnel \ --set-env-vars="GCE_IP=x.y.z.t" \ --platform=managed \ --region us-central1 \ --allow-unauthenticated \ --image=gcr.io/$PROJECT/sample-tunnel
Query the application
When you visit the application’s public URL, you will see that the IP address that it used to query https://ifconfig.me/ip is the IP address of the GCE instance.
You can visit the application’s
/exit endpoint to crash the Cloud Run
container instance, which would normally have a new dynamic IP assigned to the
instance, but in this case it remains the same as Cloud Run holds onto the
Don't forget to check out the accompanying blog post: https://ahmet.im/blog/cloud-run-static-ip/