During development and while troubleshooting issues, it is often very useful to test some preconditions in the same execution environment as the microservice while having more maneuverability.
The Cloud Run SSH server aims to provide full linux shell running in a Cloud Run service instance to allow developers and administrators to test and troubleshoot all kinds of scenarios.
- SSH server for Cloud Run gen1 and gen2.
 - Get a text transcript of the SSH session.
 - Download files from the SSH server filesystem.
 - Execute commands from the UI using a catalog with 2 clicks.
 - Automatically log into the SSH server using Cloud Run proxy.
 - Edit files and code using Visual Studio Code.
 - Run Docker containers on top of Cloud Run gen2.
 
- docker-openssh-server: provides the SSH server.
 - webssh: provides web access to the SSH server and a browser based shell to execute commands.
 - Supervisor: provides process control and orchestration.
 - Nginx: provides the HTTP proxy for 
devandappflavors. - Docker: provides containers execution engine.
 - Code Server: provides the web based Visual Studio Code editor.
 
lite: contains the bare minimum tools to perform network troubleshooting.full: contains additional Google Cloud SDK and third-party tools.dev: contains Google Cloud SDK, Docker and Visual Studio Code.app: same aslite, includes Docker and allows to import a container image into the Cloud Run SSH server filesystem.
Tip
Choose lite if all you need to do is network troubleshooting; build-time and size will be reduced.
Note
The app and dev flavors are only supported under Cloud Run gen2 environment.
root: whether to allowsudoto escalate privileges.no-root:sudois disabled; the logged in user won't be able to escalate privileges.
Tip
choose no-root if you are providing this image to others so that installing new software is disabled.
docker pull ghcr.io/gchux/cloud-run-ssh:latest
docker tag ghcr.io/gchux/cloud-run-ssh:latest ${IMAGE_URI_FULL}
docker push ${IMAGE_URI_FULL}Choose from one of the following pre-built image flavors:
- ghcr.io/gchux/cloud-run-ssh:lite-root
 - ghcr.io/gchux/cloud-run-ssh:full-root
 - ghcr.io/gchux/cloud-run-ssh:dev-root
 
Note
Docker image tag latest points to CONTENT_FLAVOR=lite and ACCESS_LEVEL=root
export CONTENT_FLAVOR='...'        # `lite` or `full` depending on the required tooling
export ACCESS_LEVEL='...'          # `root` or `no-root` depending on the required access level
export CREATE_KEY_PAIR='false'     # when using `docker_build`, whether to create a new KEY pair
export PASSWORD_ACCESS='true'      # allows to disable password authentication if KEY based auth is preferred
export SUDO_ACCESS='false'         # `true`/`false` depending on access level selection
export CLOUDSDK_VERSION='...'      # see: https://console.cloud.google.com/storage/browser/cloud-sdk-release
export GCSFUSE_VERSION='...'       # see: https://github.com/GoogleCloudPlatform/gcsfuse/releases
export CSQL_PROXY_VERSION='...'    # see: https://github.com/GoogleCloudPlatform/cloud-sql-proxy/releases
export ALLOYDB_PROXY_VERSION='...' # see: https://github.com/GoogleCloudPlatform/alloydb-auth-proxy/releases
export USQL_VERSION='...'          # see: https://github.com/xo/usql/releases
export SSH_USER='user'             # whatever user you want to use to login into the SSH server
export SSH_PASS='pass'             # whatever password you want to use to login into the SSH server
export WEB_PORT=8022               # whatever TCP port you want to use to serve the WEB SSH terminal
export DEV_PORT=8088               # whatever TCP port you want to use to serve Visual Studio code
export APP_PORT=8080               # whatever TCP port you want to use to serve the APP under test
export IMAGE_NAME='cloud-run-ssh'  # or whatever name you need/require to use for the Docker image
export IMAGE_TAG="${CONTENT_FLAVOR}-${ACCESS_LEVEL}"
export DOCKERFILE="Dockerfile.${IMAGE_TAG}"
export PROJECT_ID='...'            # GCP Project ID ( this is an alphanumeric name, not a number )
export REPO_LOCATION='...'         # Artifact Registry docker repository location
export REPO_NAME='...'             # Artifact Registry docker repository name
export IMAGE_URI_BASE="${REPO_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}"
export IMAGE_URI_FULL="${IMAGE_URI_BASE}/${IMAGE_NAME}:${IMAGE_TAG}"
export BUILD_TAG='...'             # whatever tag you may/need to useor create a copy of env.sample named .env, edit it with your desired values, and source it:
cp -v env.sample .env
# edit `.env` to contain your desired values
source $(pwd)/.envNote
creating a copy of env.sample with your custom configuration is the better approach as you can expand this pattern to multiple env files in order to create various builds.
Populate the variables and run the command:
# pass the full path to your env file to override `.env`
./docker_buildAdjust environment variables as per your requirements:
gcloud builds submit --config $(pwd)/cloudbuild.yaml \
--substitutions "_REPO_LOCATION=${REPO_LOCATION},_REPO_NAME=${REPO_NAME},_IMAGE_NAME=${IMAGE_NAME},_IMAGE_TAG=${IMAGE_TAG},_BUILD_TAG=${BUILD_TAG},_WEB_PORT=${WEB_PORT},_SSH_USER=${SSH_USER},_SSH_PASS=${SSH_PASS},_PASSWORD_ACCESS=${PASSWORD_ACCESS},_SUDO_ACCESS=${SUDO_ACCESS},_CLOUDSDK_VERSION=${CLOUDSDK_VERSION},_GCSFUSE_VERSION=${GCSFUSE_VERSION},_CSQL_PROXY_VERSION=${CSQL_PROXY_VERSION},_ALLOYDB_PROXY_VERSION=${ALLOYDB_PROXY_VERSION},_USQL_VERSION=${USQL_VERSION},_DOCKERFILE=${DOCKERFILE}" \
$(pwd)export SERVICE_NAME='...'
export SERVICE_REGION='...'
export PASSWORD_ACCESS='true|false' # allow users to login using user and password.
export SUDO_ACCESS='true|false'     # allow users to use `sudo` and login as `root`.
export SSH_AUTO_LOGIN='true|false'  # allow going straight to shell; requires `PASSWORD_ACCESS`.
# set PORT to `8888` for `dev` and `app` server flavors.
export CLOUD_RUN_PORT="${WEB_PORT}"
gcloud run deploy ${SERVICE_NAME} \
   --image=${IMAGE_URI_FULL} \
   --region=${SERVICE_REGION} \
   --port=${CLOUD_RUN_PORT} \
   --execution-environment=gen2 \
   --min-instances=0 --max-instances=1 \
   --memory=1Gi --cpu=1 --cpu-boost \
   --timeout=3600s --no-use-http2 \
   --session-affinity --no-cpu-throttling \
   --set-env-vars="SUDO_ACCESS=${SUDO_ACCESS},PASSWORD_ACCESS=${PASSWORD_ACCESS},SSH_AUTO_LOGIN=${SSH_AUTO_LOGIN},LOG_STDOUT=true" \
   --no-allow-unauthenticatedCaution
It is strongly recommended to use --no-allow-unauthenticated in order to prevent unauthorized access to the SSH server.
Important
If gen1 is needed, then both ACCESS_LEVEL, SSH_USER/USER_NAME must be set to root.
- 
gcloud run services proxy ${SERVICE_NAME} --region=${SERVICE_REGION} --port=8080see: https://cloud.google.com/sdk/gcloud/reference/run/services/proxy
 - 
Use a WEB browser, got to:
http://127.0.0.1:8080/ - 
If
SSH_AUTO_LOGINis set tofalse, then fill in the following fields:- Hostname: 
127.0.0.1(fixed) - Port: 
2222(fixed) - Username: 
${SSH_USER} - Password: 
${SSH_PASS} 
 - Hostname: 
 - 
Click
Connect 
Tip
The gcloud proxy command and ALL relevant URLs will be avaiable in Cloud Logging; filter logs using [SSH Server] to find them.
Use any of the logged URLs to log into the SSH Server automatically and go straight to the shell.
- 
Use
PASSWORD_ACCESS=trueif you want to allow users to login with user and password. - 
Use
SUDO_ACCESS=trueif you want to allow users to escalate privileges without allowingrootto login. - 
Use
SSH_AUTO_LOGIN=trueif you want to allow users to be automatically logged in and go straight to theSHELL. 
Important
The option SSH_AUTO_LOGIN=true requires PASSWORD_ACCESS=true, and SUDO_ACCESS=true if the intended user is root.
- 
At container execution time, it is possible to override the following parameters:
- 
SSH_PASS: expose a secret using the environment variableUSER_PASSWORD,- 
alternatively, mount a secret volume for a file containing the password at directory:
/wssh/secrets/2/, and then:- define the environment variable 
USER_PASSWORD_FILEcontaining the exact file path; i/e:/wssh/secrets/2/user_password. 
 - define the environment variable 
 
 - 
 - 
SSH_USER: use the environment variableUSER_NAME:- 
this environment variable may also be defined using a secret.
 - 
it must not be set if the expected user is
root. 
 - 
 - 
WEB_PORT: use the environment variableWEBSSH_PORT.- when using the 
liteflavor, it will be overriden by thePORTenvironment variable. 
 - when using the 
 - 
DEV_PORT: use the environment variableWEBDEV_PORT. - 
APP_PORT: use the environment variableWEBAPP_PORT. 
 - 
 - 
When using public key authentication, you may use the following alternatives to provide Public keys:
- 
Mount a secret volume for the Public key file that should have access at directory
/wssh/secrets/3/, and then:- define the environment variable 
PUBLIC_KEY_FILEcontaining the exact file path; i/e:/wssh/secrets/3/public_key. 
see: https://cloud.google.com/run/docs/configuring/services/secrets
 - define the environment variable 
 - 
Mount a GCS volume containing all Public keys that should have access, and then:
- define its path using the environment variable 
PUBLIC_KEY_DIR. 
see: https://cloud.google.com/run/docs/configuring/services/cloud-storage-volume-mounts
 - define its path using the environment variable 
 - 
Serve an HTTP(S) accessible key file containing all the Public keys that should have access,
- and define its full URL using the environment variable 
PUBLIC_KEY_URL. 
 - and define its full URL using the environment variable 
 - 
Leverage the SSH server
AuthorizedKeysFileconfig by mounting a secret volume at:/wssh/secrets/1/authorized_keys 
 - 
 
Tip
In order to avoid having to handle access management for all users, it is better/simpler to provide user, password, Public key and authorized_keys as secrets via environment variable or volume mounts.
As of version v2.0.0 the Cloud Run SSH Server can also be deployed as a sidecar which allows to individually access and troubleshoot Cloud Run instances running application code.
This operation mode does not require any modifications to the main –ingress– application container so that you can perform the required troubleshooting using its default configuration; this effectively means that any other containers remain immutable.
In order to SSH into the sidecar in any Cloud Run running instance, you'll need:
- The SSH Proxy Server: 
ghcr.io/gchux/cloud-run-ssh:proxy-server-latest - and the SSH client: 
ghcr.io/gchux/cloud-run-ssh:client-latest - Cloud Run service/revision with VPC connectivity:
 
This setup works by creating bastion host ( the SSH Proxy Server ) through which Cloud Run instances are individually accessible but not directly reachable as the SSH Proxy Server cannot route traffic to any of them unless the Cloud Run instances establish a connection first; this connections is called a tunnel.
The flow to create and use an encrypted tunnel to connect to a Cloud Run instance is decribed as follows:
- 
Upon startup, the
Cloud Run SSH server sidecarcreates a TLS tunnel via theSSH Proxy Serverusing theSSH Proxy ServerAPI.- The TLS tunnel created by a Cloud Run instance is identified by a randomly generated 
UUID. This TLS tunnel ID is the Cloud Run instance's identity. - The 
SSH Proxy ServerAPI is served overHTTPSand requires theSSH server sidecarandSSH Clientto provide a verifiable ID token. - The 
Cloud Run SSH server sidecaruses the Cloud Run service identity to generate tokens. - The 
SSH Proxy ServerAPI enforces access controls on the project hosting Cloud Run instances and the identity used by the Cloud Run service. - In order to be reachable, Cloud Run instances must register themselves with the 
SSH Proxy Server; otherwise,SSH Clientscannot connect. 
 - The TLS tunnel created by a Cloud Run instance is identified by a randomly generated 
 - 
The
SSH Proxy Serverregisters the available Cloud Run instance(s), and enables access via the reserved TLS tunnels.- The 
SSH Proxy Serverenforces access controls on the TLS tunnel by restricting access to specific hosts or networks.- Allowed hosts must include the 
CIDRranges assigned to VPC Connectors or Direct VPC. 
 - Allowed hosts must include the 
 
 - The 
 - 
The
SSH Proxy Servermay be hosted wherever a container can be executed, provided that it has access to theMetadata Server; i/e: Compute Engine VM, Kubernetes Engine cluster, etc...- The 
SSH Proxy Serveridentity is a user defined and validUUIDwhich is verified by both theSSH server sidecarand theSSH Clientbefore registering and using the TLS tunnels. - The 
SSH Proxy ServerAPI & Tunnel ports can be adjusted; the default values are:5000for the API, and5555for the Tunnel. 
 - The 
 - 
The
SSH Client( aka the SSH Proxy Visitor ) uses theSSH Proxy ServerAPI to resolve a Cloud Run instance ID into the correct TLS tunnel to be used. - 
The
SSH Clientuses the discovered TLS tunnel to connect to a running Cloud Run instance'sSSH server sidecarvia theSSH Proxy Server.- The 
SSH Proxy Servermust also allow access to the host(s)IPv4/IPv6orCIDRranges where theSSH Clientswill be connecting from. 
 - The 
 - 
Cloud Run SSH server sidearpings theSSH Proxy ServerAPI every 60 seconds to renew its registration.- Similarly, the 
SSH Proxy Serverdrops registrations if a Cloud Run instance has not renewed its registration within 15 minutes. 
 - Similarly, the 
 
Since all networking happens within the VPC and tunnels are encrypted end to end, connecting into running instances is safe and secure.
Important
While the SSH Proxy Server identity is known, the Cloud Run SSH server sidecar identity must remain confidential and only discovered via the SSH Proxy Server API.
A pre-built Docker image is available as: ghcr.io/gchux/cloud-run-ssh:latest.
In addition to the environment variables used when running as the main –ingress– application container, the SSH server sidecar accepts the following:
ENABLE_SSH_PROXY: boolean ; default value isfalse.SSH_PROXY_SERVER_HOST: IPv4 ; theIPv4of the host runnignt theSSH Proxy Server. No default value is assigned.SSH_PROXY_SERVER_API_PORTuint16 ; default value is5000.SSH_PROXY_SERVER_TUNNEL_PORT: uint16 ; default value is5555.SSH_PROXY_SERVER_ID: uuid ; theSSH Proxy Serveridentity that will be verified by the Cloud Run instance before enabling a tunnel. Default value is00000000-0000-0000-0000-000000000000.
A pre-built Docker image is available as: ghcr.io/gchux/cloud-run-ssh:proxy-server-latest.
The SSH Proxy Server requires a YAML configuraiton file which must be mounted at /etc/ssh_proxy_server/config.yaml.
id: uuid (required); theSSH Proxy Serveridentity.project_id: string (required); the GCP Project ID hosting theSSH Proxy Server.access_control.allowed_projects: list[string] (optional); Cloud Run projects allowed to register instances and accept connections. If empty, it allows all projects.access_control.allowed_regions: list[string] (optionsl); Cloud Run regions allowed to register instances and accept connections. If empty, it allows all regions.access_control.allowed_services: list[string] (optional); Cloud Run services allowed to register instances and accept connections. If empty, it allows all services.access_control.allowed_revisions: list[string] (optional); Cloud Run revisions allowed to register instances and accept connections. If empty, it allows all revisions.access_control.allowed_identities: list[email] ; identities allowed to consume theSSH Proxy ServerAPI. Identities are enforced for the Cloud Run instances and theSSH Clients.access_control.allowed_hosts: list[IPv4|IPv6|CIDR] (required); hosts which are allowed to consume theSSH Proxy ServerAPI and Tunnels.- Must include: VPC Connectors or Direct VPC 
CIDRranges, and the allowed subnets whereSSH Clientswill be connecting from. 
- Must include: VPC Connectors or Direct VPC 
 
id: 00000000-0000-0000-0000-000000000000
project_id: my-project-1
access_control:
  allowed_projects:
    - my-project-1
    - my-project-2
  allowed_regions:
    - us-central1
    - us-west4
  allowed_services:
    - my-service-1
    - my-service-2
  allowed_revisions:
    - my-service-1-0001-abcd
    - my-service-2-0001-abcd
  allowed_identities:
    - ssh@my-project-1.iam.gserviceaccount.com
    - ssh@my-project-2.iam.gserviceaccount.com
  allowed_hosts:
    - ::1
    - 127.0.0.1
    - 169.254.0.0/16docker kill cloud-run-ssh-proxy-server
docker rm cloud-run-ssh-proxy-server
docker pull ghcr.io/gchux/cloud-run-ssh:proxy-server-latest
docker tag ghcr.io/gchux/cloud-run-ssh:proxy-server-latest cloud-run-ssh-proxy-server
docker run -d \
        --restart=unless-stopped \
        --name=cloud-run-ssh-proxy-server \
        -p 5555:${SSH_PROXY_SERVER_TUNNEL_PORT} \
        -p 5000:${SSH_PROXY_SERVER_API_PORT} \
        -p 127.0.0.1:8888:8888 \
        -e PROJECT_ID=${PROJECT_ID} \
        -v ./config.yaml:/etc/ssh_proxy_server/config.yaml:ro \
        cloud-run-ssh-proxy-serverNote
Port 8888 exposes the SSH Proxy Server API ( same as ${SSH_PROXY_SERVER_API_PORT} ),
but it does NOT enforce access_control other than allowed_hosts, nor is it served over HTTPS.
It is strongly recommended to no expose port 8888 outisde loopback ( localhost ) interface.
A pre-built Docker image is available as: ghcr.io/gchux/cloud-run-ssh:client-latest
The SSH Client container requies the following environment variables:
PROJECT_ID: string ; GCP Project ID hosting the client.SSH_PROXY_SERVER_HOST: IPv4 ;IPassigned to the host running theSSH Proxy Server.SSH_PROXY_SERVER_API_PORTuint16 ;SSH Proxy ServerAPI port.SSH_PROXY_SERVER_TUNNEL_PORT: uint16 ;SSH Proxy ServerTunnel port.SSH_PROXY_SERVER_ID: uuid ;SSH Proxy Serveridentity.
PROJECT_ID=my-project-1
SSH_PROXY_SERVER_HOST=${SSH_PROXY_SERVER_HOST}
SSH_PROXY_SERVER_API_PORT=5000
SSH_PROXY_SERVER_TUNNEL_PORT=5555
SSH_PROXY_SERVER_ID=00000000-0000-0000-0000-000000000000docker pull ghcr.io/gchux/cloud-run-ssh:client-latest
docker tag ghcr.io/gchux/cloud-run-ssh:client-latest cloud-run-ssh-client:latest
docker run -it --rm \
        --name=cloud-run-ssh-client \
        -e "INSTANCE_ID=${1}" \
        --env-file=ssh.env \
        --network=host \
        cloud-run-ssh-client:latest \
        "${@:1}"A bash script that executes the SSH Client container is also available;
this script requires the 1st argument to be the Cloud Run INSTANCE_ID to connect; i/e: ./ssh ${INSTANCE_ID}.
After the Cloud Run instance ID you may use any valid OpenSSH client arguments,
except for -p or port as it is reserved for the TLS tunnel ( TCP::2222 ) to deliver all traffic to the Cloud Run instance.
In general, the most useful flag is local port forward or -L which you may use to forward traffic
from a local TCP port into a remote TCP port available in the Cloud Run instance itself or any other remote host reachable/routable from the Cloud Run instance.





