Remote to a VM over an IAP tunnel with VSCode

Albert Brand
4 min readFeb 14, 2020

--

SSH-ing to a Google Cloud VM without an external IP address is possible using a tunnel. But what if you want to edit code remotely using VSCode over that tunnel? This solution is a little gem that I could not find on the interwebs.

The setup

With VSCode’s Remote-SSH extension you can develop and run code on any remote machine over SSH. This is a great option when you need to develop on larger, faster, or more specialised hardware than your local machine — for instance, building a deep learning network with GPU or TPU acceleration.

Google Cloud offers to create virtual machines to meet such needs. By default Compute instances are configured to get an external IP address assigned. But if you want to have a more secure VM, you can choose to assign none:

Create a VM without an external IP assigned

After creating your instance (mine is boringly named instance-1), you can SSH to it using Identity-Aware Proxy TCP forwarding. IAP handles the authentication part and tunnels data traffic from your local machine to the remote machine in an HTTPS stream.

By following the docs you create the tunnel and SSH to your new remote machine in one go with the following command:

gcloud compute ssh instance-1 --tunnel-through-iap

(You can even leave out the --tunnel-through-iap as the gcloud CLI automatically detects there is no external IP assigned to your instance.)

So now we want to connect VSCode to this VM via the IAP tunnel, as editing code remotely using the terminal feels a bit… outdated. But how?

The solution

Under the hood, the gcloud compute ssh command starts a SSH command with the correct arguments to connect over IAP. The Remote-SSH extension can import such a command (parsing the command-line arguments) and save it in local SSH config storage.

To get the SSH command that gcloud compute ssh uses, you can run it in ‘dry run’ mode:

$ gcloud compute ssh instance-1 --tunnel-through-iap --dry-run/usr/bin/ssh -t -i /Users/albert/.ssh/google_compute_engine -o CheckHostIP=no -o HostKeyAlias=compute.5635388000963342282 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/Users/albert/.ssh/google_compute_known_hosts -o ProxyCommand /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python -S /Users/albert/Development/google-cloud-sdk/google-cloud-sdk/lib/gcloud.py compute start-iap-tunnel instance-1 %p --listen-on-stdin --project=albert-brand-speeltuin --zone=europe-west1-d --verbosity=warning -o ProxyUseFdpass=no albert@compute.5635388000963342282

(This is my output on a Mac, your result will probably differ).

Let’s try to parse it in Remote-SSH. In VSCode, press Shift-Command-P and find & run Remote-SSH: Add a new host:

Add a new SSH host

But alas, I get the following error:

Argument missing for option ProxyCommand
ProxyCommand error

It seems that the ProxyCommand argument is not picked up correctly. To check if the syntax is OK, I run the dry-run output in a shell and get a similar error:

command-line line 0: Missing argument.

Looking at the dry-run command, the ProxyCommand is supposed to contain the command that starts the IAP tunnel. However the argument is in the wrong format. By changing the ProxyCommand argument to the double quoted syntax ProxyCommand="..." it starts to work!

$ /usr/bin/ssh -t -i /Users/albert/.ssh/google_compute_engine -o CheckHostIP=no -o HostKeyAlias=compute.5635388000963342282 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/Users/albert/.ssh/google_compute_known_hosts -o ProxyCommand="/System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python -S /Users/albert/Development/google-cloud-sdk/google-cloud-sdk/lib/gcloud.py compute start-iap-tunnel instance-1 %p --listen-on-stdin --project=albert-brand-speeltuin --zone=europe-west1-d --verbosity=warning" -o ProxyUseFdpass=no albert@compute.5635388000963342282Linux instance-1 4.9.0-12-amd64 #1 SMP Debian 4.9.210-1 (2020-01-20) x86_64
...
Last login: Fri Feb 14 13:03:10 2020 from ...
albert@instance-1:~$

However, this command is still not properly parsed by the Remote-SSH extension. You need to strip the /usr/bin/ part from the command as well, and finally it will import correctly:

Add a new SSH host correctly

If you open the SSH config file after you saved the configuration it will show up as:

SSH config

So, you can skip this import next time and directly add the config in this format. Well 🤷‍♂️.

Run Shift-Command-P and find & run Remote-SSH: Connect to server (or click on the green icon on the bottom left). Wait a while, choose a root folder and you are finally connected using IAP tunnelling:

Remotely connected VSCode

Happy coding!

--

--