Securely Connecting to VM Instances

When developing projects on Google Compute Engine, there are a variety of scenarios in which you want to keep the instances from being reached from the public Internet:

  • Web services are still under development and not ready to be exposed to external users because they are feature incomplete or have not yet been configured with HTTPS.
  • Instance might be providing services designed to be only consumed by other instances in the project.
  • Instances should only be reached through dedicated interconnect options from company offices or data centers.

Even when a service is intentionally Internet-facing, it is important that communication with the service be restricted to the target user groups, and occur over secure channels, such as SSH or HTTPS, to protect sensitive information.

This article demonstrates several methods for securing communications with Compute Engine instances with or without external IP addresses.

Protecting services on machines with external IP addresses

Connecting to instances without external IP addresses

Protecting services on machines with external IP addresses

When instances have a public IP address, it is important that only the services and traffic you intend to be exposed are reachable, and for those that are exposed, any sensitive information is secured in transit.

Firewalls

Your first line of defense is to restrict who can reach the instance using firewalls. By creating firewall rules, you can restrict all traffic to a network or target machines on a given set of ports to specific source IP addresses.

Firewalls are not a standalone solution. Restricting traffic to specific source IPs does not protect sensitive information, such as login credentials, commands that create or destroy resources or files, or logs. When running a web service on a publicly-accessible machine, such as a Google Compute Engine instance with an external IP, you need to encrypt all communication between your host and the deployed instance to ensure proper security.

In addition, firewalls aren't always the appropriate solution. For example, firewalls are not ideal for development environments that do not have static IP addresses, such as roaming laptops.

HTTPS and SSL

For production web systems, you should configure HTTPS/SSL. HTTPS/SSL can be set up either by setting up an instance to terminate HTTPS or by configuring HTTPS load balancing. HTTPS/SSL does involve some initial complexity, requiring you to perform the following tasks:

If you have set up SSL-serving domains before, it should be straightforward to do the same with Google Compute Engine. If not, you might find it easier to use a different security method, such as port forwarding or SOCKS proxy.

Port forwarding over SSH

You can use the gcloud command-line tool to start a server on a given local port that forwards all traffic to a remote host over an SSH connection.

First, take note of the instance and port that are providing the service to which you would like to establish a secure connection. Next, run the following command:

gcloud compute ssh example-instance \
    --project my-project \
    --zone us-central1-a \
    --ssh-flag="-L" \
    --ssh-flag="2222:localhost:8888"

In the above command, the parameters are defined as follows:

  • example-instance is the name of the instance to which you'd like to connect.
  • my-project is your Google Cloud Platform project ID.
  • us-central1-a is the zone in which your instance is running.
  • 2222 is the local port you're listening on.
  • 8888 is the remote port you're connecting to.

With these example settings, if you open http://localhost:2222/ in your browser, the HTTP connection will go over the SSH tunnel you have just created over to your remote host and connect to the specified instance via SSH and then connect to port 8888 on the same machine, but over an encrypted, secure SSH connection.

The gcloud command creates and maintains an SSH connection, and this approach only works while the SSH session is active. As soon as you exit the SSH session that gcloud creates, port forwarding via http://localhost:2222/ will stop working.

If you want to create more than one port forwarding rule, you can specify multiple rules on a single command line by repeating the flags:

gcloud compute ssh example-instance \
    --project my-project \
    --zone us-central1-a \
    --ssh-flag="-L" \
    --ssh-flag="2222:localhost:8888" \
    --ssh-flag="-L" \
    --ssh-flag="2299:localhost:8000"

Alternatively, you can run a new gcloud command each time to create a separate tunnel. Note that you cannot add or remove port forwarding from an existing connection without exiting and re-establishing the connection from scratch.

SOCKS proxy over SSH

If you want to connect to a number of different hosts in your cloud deployment, the easiest way to do so is to change your browser to do the lookups directly from your network. This approach allows you to use the short name of the hosts instead of looking up each host's IP address, opening up ports for each service, or creating an SSH tunnel for each host/port pair.

The approach that you use here is as follows:

  1. Set up a single SSH tunnel to one of the hosts on the network, and create a SOCKS proxy on that host.
  2. Change the browser configuration to do all the lookups via that SOCKS proxy host.

Note that because you are tunneling all traffic via that host, you don't want to browse the web in general using that browser or that specific profile, as you will use your cloud service's bandwidth for this. In general, you might want to use a separate browser profile and switch to it when necessary.

Start the SOCKS proxy

To start your SOCKS proxy, run the following command:

gcloud compute ssh example-instance \
    --project my-project \
    --zone us-central1-a \
    --ssh-flag="-D" \
    --ssh-flag="1080" \
    --ssh-flag="-N"

In the above command, the parameters are defined as follows:

  • example-instance is the name of the instance to which you would like to connect.
  • my-project is your Google Cloud Platform project ID.
  • us-central1-a is the zone in which your instance is running.
  • 1080 is the local port you're listening on.

Note that, in this case, you don't need to specify a remote port. Because a SOCKS proxy does not bind to any specific remote port, any connection you make via the SOCKS proxy will be resolved relative to the host you connect to.

By using a SOCKS proxy, you can connect to any instance that shares a Compute Engine network with your proxy instance by using the instance's short name. In addition, you can connect to any port on a given instance.

This approach is much more flexible than the simple port-forwarding method, but will also require you to change the settings in your web browser to utilize the proxy.

Next, configure your browser to use the proxy.

Chrome setup for SOCKS proxy

Chrome uses system-wide proxy settings by default, so you need to specify a different proxy using command-line flags. Launching Chrome by default creates an instance of an already-running profile, so to enable you to run multiple copies of Chrome simultaneously, one which is using the proxy and others which are not, you need a new profile.

Launch Chrome using a new profile. It will be created automatically if it does not exist.

Linux:

/usr/bin/google-chrome \
    --user-data-dir="$HOME/chrome-proxy-profile" \
    --proxy-server="socks5://localhost:1080"

Mac OS X:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
    --user-data-dir="$HOME/chrome-proxy-profile" \
    --proxy-server="socks5://localhost:1080"

Windows:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" ^
    --user-data-dir="%USERPROFILE%\chrome-proxy-profile" ^
    --proxy-server="socks5://localhost:1080"

Be sure to set the localhost port to the same value that you used in the gcloud command earlier (1080 in our example).

Firefox setup for SOCKS proxy

Before changing these settings, you may want to create a new Firefox profile. Otherwise, it will affect all instances of Firefox to use that host as a proxy, which is very likely not what you want.

After you have Firefox running with a separate profile, you can set up the SOCKS proxy:

  1. Open Preferences.
  2. Click Advanced > Networks > Settings to open the Connection Settings dialog.
  3. Choose the option Manual proxy configuration
    • In the SOCKS Host section, fill in localhost as the host and the port you selected when you ran the gcloud command earlier.
    • Choose SOCKS v5.
    • Check the box Remote DNS.
    • Leave all other entries blank.
  4. Click OK and close the Preferences dialog box.

Connecting to instances without external IP addresses

When instances do not have external IP addresses they can only be reached by other instances on the network, or via managed VPN gateway. You can provision instances in your network to act as trusted relays for inbound connections (bastion hosts) or network egress (NAT Gateways). For more transparent connectivity without setting up such connections, you can use a managed VPN gateway resource.

Bastion hosts

Bastion hosts provide an external facing point of entry into a network containing private network instances. This host can provide a single point of fortification or audit and can be started and stopped to enable or disable inbound SSH communication from the Internet.

Bastion Architecture

By using a bastion host, you can connect to an instance that does not have an external IP address. This approach allows you to connect to a development environment or manage the database instance for your external application, for example, without configuring additional firewall rules.

A complete hardening of a bastion host is outside the scope of this article, but some initial steps taken can include:

  • Limit the CIDR range of source IPs that can communicate with the bastion.
  • Configure firewall rules to allow SSH traffic to private instances from only the bastion host.

By default, SSH on instances is configured to use private keys for authentication. When using a bastion host, you log into the bastion host first, and then into your target private instance. Because of this two-step login, which is why bastion hosts are sometimes called "jump servers," you should use ssh-agent forwarding instead of storing the target machine's private key on the bastion host as a way of reaching the target machine. You need to do this even if using the same key-pair for both bastion and target instances, as the bastion has direct access to only the public half of the key-pair.

How to setup ssh-agent on Mac and Linux

On Mac OS X, ssh-agent is already installed by default, and should be present on most Linux operating systems. First, ensure that ssh-agent is set up and your shell environment is prepared.

eval `ssh-agent -s`

You can then add a private key to ssh-agent with the ssh-add command:

ssh-add PATH-TO-PRIVATE-KEY

If your private key is protected with a passphrase, you can add that to the OS X keychain by using the -K flag:

ssh-add -K PATH-TO-PRIVATE-KEY

You will need to either do this for each login session, or add this to your shell login init file, for example your .bash_profile. You can list the keys that have been added with the -L flag:

ssh-add -L

With the key added, you connect to the bastion host with agent forwarding enabled via the -A flag:

ssh -A user@BASTION-EXTERNAL-IP

Alternatively, you can use the gcloud command line tool, which will look up the IP address of the instance for you.

gcloud compute ssh example-instance \
    --project my-project \
    --zone us-central1-a \
    --ssh-flag="-A"

When connected to the bastion, you can then initiate an SSH session to your target instance with just the instance name which uses the Compute Engine provided private network DNS.

ssh example-instance

VPN

Cloud VPN lets you connect your existing network to your Google Cloud Platform network via an IPsec connection to a VPN gateway device. This allows direct routing of traffic from your premises to the private IP interfaces of Compute Engine instances. Traffic is encrypted as it transits over public links to Google.

For details on setting up, configuring, and using VPN with Compute Engine, see Cloud VPN.

Traffic egress using NAT gateways

When an instance does not have an external IP address assigned it can not make direct connections to external services, including other Cloud Platform services. To allow these instances to reach services on the public Internet, you can set up and configure a NAT gateway machine, which can route traffic on behalf of any instance on the network. Be aware that a single instance should not be considered highly available, and can not support high traffic throughput for multiple instances.

Interactive serial console access

When an instance doesn't have an external IP address, you might still need to interact with the instance for troubleshooting or maintenance purposes. Setting up a Bastion host is one option but might require more setup than worthwhile for your needs. If you want to troubleshoot an instance without an external IP address, consider enabling interactive access on the serial console, which allows you to interact with an instance's serial console using SSH and run commands against the serial console.

To learn more, read Interacting with the Serial Console.

Try out other Google Cloud Platform features for yourself. Have a look at our tutorials.

Send feedback about...

Compute Engine Documentation