Skip to content

Enable an SSH connection to a running deployment

When debugging, it might come in handy to be able to SSH directly into a running deployment to investigate. By opening up a port on the deployment and adjusting your code a bit, it's possible to do this. In this how-to we'll walk you through the following steps:

  1. Adjust the deployment package to allow SSH connections via OpenSSH
  2. Adjust the port forwarding settings for your deployment
  3. Connect via your terminal

For this example we will use the multiply example deployment from our starter tutorial.

Create a deployment that sets up the SSH requirements

To create a deployment that allows you to establish an SSH connection with it, we need to do a few things:

  • Adjust the deployment environment to allow SSH connections
  • Adjust the deployment.py to accept SSH connections via OpenSSH
  • Adjust the deployment configuration to work with port forwarding

Let's start with adjusting the environment.

Adjust the deployment environment specifications

We will be using OpenSSH to allow SSH connections. To be able to use it, we need to add a ubiops.yaml file to our deployment package:

apt:
    packages:
        - openssh-server

We also need an sshd_config file that ensures that the host keys are properly read from the deployment. Make sure you create an sshd_config file with the following contents and save it in your deployment package folder (or your environment folder if you're using custom environments).

Port 2222

HostKey /home/deployment/.ssh/ssh_host_rsa_key
HostKey /home/deployment/.ssh/ssh_host_ecdsa_key
HostKey /home/deployment/.ssh/ssh_host_ed25519_key

PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
PrintMotd no
AcceptEnv LANG LC_*
ClientAliveInterval 120
UseDNS no

Adjust the deployment.py to allow SSH connections

As mentioned before, we are using the multiply deployment from our starter tutorial as our base. The deployment code looks like this:

class Deployment:

    def __init__(self, base_directory, context):

        print("Initialising deployment")

    def request(self, data):

        print("Processing request")
        # Retrieve the user input from the data dictionary
        user_input = data['number']

        # Process the input
        number_multiplied = user_input * 2

        # Return the output
        return {'number_multiplied': number_multiplied}

To allow SSH connections to this deployment, we need to alter the __init__ function. We want to make sure that access via SSH is limited to a specific public key. In this example we will provide the public key via an environment variable to the deployment. That means we can do the following:

# Write the public key to authorized keys
os.mkdir("/home/deployment/.ssh/")
try:
    with open("/home/deployment/.ssh/authorized_keys", "w") as f:
        f.write(os.environ["SSH_PUBLIC_KEY"])
    print("SSH key has been set")
except KeyError:
    print("Failed to set SSH key")

Now that the public key of our environment variable is saved in our authorized keys file, we still need to generate our SSH host keys and start a subprocess:

# Generate SSH host keys
subprocess.run(["ssh-keygen", "-t", "rsa", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_rsa_key"])
subprocess.run(["ssh-keygen", "-t", "ecdsa", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_ecdsa_key"])
subprocess.run(["ssh-keygen", "-t", "ed25519", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_ed25519_key"])

subprocess.Popen(["/usr/sbin/sshd", "-f", "/home/deployment/sshd_config"])

And lastly we need to check what the IP address is of the instance and print it to the logs. We need the IP address to be able to connect to the deployment from our terminal and the IP address can be different every time a deployment starts.

# Get the IP address and print to the logs
http_request = urllib.request.urlopen("https://whatismyipv4.ubiops.com")
ip_address = http_request.read().decode("utf8")
http_request.close()
print(f"The IP address of this deployment is: {ip_address}")

Putting this all together gives us the following adjusted deployment.py:

import os
import subprocess
import urllib.request


class Deployment:

    def __init__(self):
        # Write the public key to authorized keys
        os.mkdir("/home/deployment/.ssh/")
        try:
            with open("/home/deployment/.ssh/authorized_keys", "w") as f:
                f.write(os.environ["SSH_PUBLIC_KEY"])
            print("SSH key has been set")
        except KeyError:
            print("Failed to set SSH key")

        # Generate SSH host keys
        subprocess.run(["ssh-keygen", "-t", "rsa", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_rsa_key"])
        subprocess.run(["ssh-keygen", "-t", "ecdsa", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_ecdsa_key"])
        subprocess.run(["ssh-keygen", "-t", "ed25519", "-N", "", "-f", "/home/deployment/.ssh/ssh_host_ed25519_key"])

        subprocess.Popen(["/usr/sbin/sshd", "-f", "/home/deployment/sshd_config"])

        # Get the IP address and print to the logs
        http_request = urllib.request.urlopen("https://whatismyipv4.ubiops.com")
        ip_address = http_request.read().decode("utf8")
        http_request.close()

        print()
        print(f"The IP address of this deployment is: {ip_address}")

    def request(self, data):
        print("Processing request")
        # Retrieve the user input from the data dictionary
        user_input = data['number']

        # Process the input
        number_multiplied = user_input * 2

        # Return the output
        return {'number_multiplied': number_multiplied}

Adjust the deployment configuration to work with port forwarding

If you followed the last two sections, you should now have a deployment package that looks like this:

deployment_package/
├── deployment.py
├── ubiops.yaml
└── sshd_config

Let's adjust the deployment configuration to be able to work with the adjustments. Go to your deployment version in the WebApp and click EDIT. Zip the deployment package folder containing all the changes and upload it in the deployment package section.

Go to you environment settings and ensure that you are using a dedicated instance type, these instances have dedicated in their name. We need this to be able to use port forwarding as it is only supported on these specific instance types.

Newly uploaded package

Now scroll down to the Port forwarding section and use 2222 for both the deployment and the public port. You can also use any other public port as long as it is >= 1024. You can leave the protocol on TCP.

Lastly, create a new environment variable called SSH_PUBLIC_KEY and use your public key as the value. If you don't have one yet, you first need to generate a new SSH key pair.

Port forwarding settings

Click Save and your deployment will start rebuilding using the new changes.

Connecting to your deployment from your terminal

Now that your deployment is adjusted, you can connect to any running instance of it via SSH from your terminal! When a new instance initializes, it will print the IP address it's running on in the logs. You can use this value to set up a connection like this:

ssh deployment@<ip_address> -p 2222

To test this out, you can temporarily change the minimum number of instances of your deployment to 1. This will spin up a new instance. In the logs you will be able to see the IP address. Copy this and use it in the ssh command. If it went succesfully you should see something like this:

terminal with SSH connection

Now that you have your connection established you can start exploring! You can run commands like ls or top to inspect what's going on inside your deployment instance. Note that you do not have sudo permissions during your connection.

If you run into any issues or have any questions, do not hesitate to reach out to our support.

Activating your dependencies

If you want to run Python code via your SSH connection, you may want to activate the virtual environment in which any dependencies from your requirements.txt are installed, and to which environment variables are added. You can do so by running the following shell command:

source /var/deployment_instance/venv/bin/activate

You might get a 'remote identification changed' warning

When a deployment gets the same IP assigned as it had once before, you might get a warning when trying to connect via SSH that "the remote host identification changed". This is because the host keys are generated upon initialization of the deployment, so they will be different for every deployment instance, while it might have the same IP as another instance had before. In case this happens, you can remove the IP address from your known hosts file and you should be able to connect again.