Python SSH Private Key Authentication

Learn how to use SSH private key authentication with Python for our SDN project

Share This Post

With SSH, you can securely manage a remote device, so you can use it to automate some tasks. We already presented SSH extensively, but it all boils down to sending commands and getting the text as a response. Before we can do any of that, however, we need to authenticate to the remote device. We need to provide a user and a password, to show we actually have the right to perform commands. This is great but has some limitations and some providers may not even support it. In fact, some big players like Amazon Web Services prefer the SSH Private Key authentication. If you have no clue about it, you are in the right place. Today we will see what is private key authentication, and how to use it in python, as well as why.

What is a Private Key?

Together with public keys, private keys are the foundation of asymmetric encryption. If you want to know more about it, check out this reading about encryption. However, today we don’t need all that knowledge. To us, asymmetric encryption is simply a way of creating a secure communication channel between two devices that have never seen each other before.

A private key, like a public key, is a tiny piece of text, generally wrapped in a .pem file. SSH can leverage that, thus we can implement SSH Private Key authentication.

How does it work?

There is a software utility on pretty much any system that can create private keys and public keys. You can only generate them in pairs, meaning that you will generate together a private and public key. They relate to each other, remember that because it is fundamental. Once you have both keys, you should put the public key on the SSH server (accepting the connection).

PuTTYGen allows you to test your SSH Private Keys for your network automation script in Python
PuTTYGen is a software to manage PuTTY private key and convert .pem files into .ppk files (PuTTY-specific format)

Then, when you attempt to connect to it, the client must provide the private key. The server will check that it is the one paired with the public key it has, and if it is it will grant access. Actually, the client never sends the key to the server. Instead, it uses it to encrypt the message: if the server can decrypt it with the public key, then it knows it was encrypted with the right private key. This is SSH private key authentication.

Tip: you can generate both keys on the client and then upload the public on the server. However, many cloud providers like AWS allows you to generate the keys on the server, and then download the private key. This can be easier for beginners.

Implementing SSH Private Key Authentication

Starting with the right foot

If you follow ICTShore, you’ll know about sdncore. It is a powerful utility we are developing to ease automated network management. Among its features, it provides an easy way to implement SSH Private Key Authentication. Of course, to use sdncore we need to install it with pip.

pip install sdncore

Now we are good to go!

Authenticating with SSH Private Key

Connecting to a device using SSH Private Key is as simple as using a single command, thanks to sdncore.

from sdncore.vty.session import Session


session = Session(
    'server.local',
    22,
    'my username',
    'my password',
    Session.SSH,
    driver_parameters={
        'private_key_file': '/path/to/key.pem',
        'private_key_password': 'key password'
    })

session.open()
print(session.command('ls'))

In the first line, we import sdncore.vty.session.Session, the module we need to make the connection. Then, we create an object to represent a session: a connection to a remote device. Here we specify the details of our connection:

  • IP or hostname of the remote server
  • Port the remote server is listening on
  • Username to authenticate with the server, in case the server uses username and SSH private key authentication (otherwise empty string)
  • Password paired with the username, if none use an empty string
  • The session protocol, in this case, SSH (Session.SSH)
  • Some additional SSH-specific parameters for the SSH (driver_parameters):
    • private_key_file is the path to the file containing the private key, we need to specify it to trigger SSH private key authentication.
    • private_key_password is a password we used to encrypt the private key file if it was encrypted. If not, just omit this parameter.

Now we created the session for later use. When we want to connect, we call the open() method. After that, we can launch commands and get the output, as we did in the last line.

Behind the scenes

If all you need is connecting with an SSH private key, you already know how to do it. However, if you want to see how SDNcore works, read this section.

As you see, sdncore is modular and allows you to use both SSH and Telnet seamlessly behind the Session proxy class. Session has the main scope of relaying to a driver class, which is Telnet or SSH specific. Of course, the driver class changes according to the chosen protocol. SSH has even two additional sub-driver classes: shell mode and channel mode. If you are curious about that, check out when we implemented it for the first time.

Session forwards the driver_parameters dictionary to the driver class in the form of named arguments. In fact, the SSH driver has several optional parameters we can specify in this way. Here’s how the constructor of SSH driver works.

def __init__(self, target, username='', password='', port=22, auto_add_keys=True, shell_mode=True,
              look_for_keys=False, chunk_size=1024, private_key_file=None, private_key_password=None):
     self.target = target
     self.username = username
     self.password = password
     self.port = port
     self.shell_mode = shell_mode
     self.look_for_keys = look_for_keys
     self._client = client.SSHClient()
     if auto_add_keys:
         self._client.set_missing_host_key_policy(client.AutoAddPolicy())
     if private_key_file is not None:
         params = [private_key_file]
         if private_key_password is not None:
             params.append(private_key_password)
         self.private_key = RSAKey.from_private_key_file(*params)
     else:
         self.private_key = None
     if shell_mode:
         sub_driver = self.ShellSubDriver
     else:
         sub_driver = self.ChannelSubDriver
     self._driver = sub_driver(self._client, chunk_size)

If you specify an SSH private key file, it creates a list containing only one item: the string indicating the path of the file that you provided. Then, it checks if you also provided the password to decrypt the file. If so, it appends that to the list. Then, it generates a Paramiko SSH private key by converting that list into arguments. We finally store the key (not the path, the key itself) in self.private_key.

Finally, we provide it to the sub-driver when we open the connection, as below.

def open(self):
  try:
    self._driver.open(self.target,
              port=self.port,
              username=self.username,
              password=self.password,
              look_for_keys=self.look_for_keys,
              private_key=self.private_key)
  except AuthenticationException as ex:
    raise DriverError("Authentication failed") from ex
  except client.SSHException as ex:
    raise DriverError("Keys error when attempting connection to " + self.target) from ex

Wrapping it up & What to do next

Using SSH Private Key when authenticating is easy, even inside a python script. To do that, simply install sdncore and provide private_key_file inside driver_parameters when instantiating a new Session.

Besides using SSH private keys like no tomorrow from now on, you now have the tools to better understand SSH automation. If you want to go deeper on that, I suggest you to visit the sdncore project on GitHub. You can also check Paramiko documentation, our actual executor of SSH connections.

So, what do you think of SSH private key authentication? Where are you going to use it? Let me know your opinions in the comments.

Picture of Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.
Picture of Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.

Alessandro Maggio

2018-11-15T16:30:50+00:00

Unspecified

Networking

Unspecified