Introduction #

If you’re anything like me, you’ll have at least a hand full of servers you’ll log into (at least) every now and then using SSH. These servers don’t all have the same key. And on top of that, I frequently have multiple keys per host. Managing this by hand is a pain in the arse. Thankfully there are a few tools that make it a breeze. I will be going through what I have done to both my local configuration and what I tend to do with my server configuration.

Generating keys #

In order to do anything with SSH in a secure way, you’ll need to make use of public and private keys. These keypairs are generated using ssh-keygen. I typically generate mine using:

ssh-keygen -t ed25519 -f ~/.ssh/<new-key>

Without any parameters, it will generate an rsa3072 key. This form of cryptography isn’t recommended as it’s become a bit flimsy with computers becoming stronger. Instead I recommend at least adding the -t ed25519 flag to generate a ed25519 key.

When prompted for a passphrase, always give it one. The only situation where not using a passphrase is acceptable is when you are planning on using the key for a forced command.

Server Configuration #

This is all done under the assumption that the you use the OpenSSH implementation on your server. If you use something like Dropbear, I can’t help you as haven’t properly dug through it’s configuration file (yet).

The things I see way to often on the internet are…

So lets go through these steps one by one.

Password Authentication #

Password authentication is the most basic thing every server should have disabled. Otherwise, it is possible to brute force a connection into your server.

“But my server is not exposed to the internet.”

I guess you could get away with not disabling password authentication, but it’s still not a good idea in case, say, your network gets compromised. On top of that, it’s less convenient to have to type in a password every time you want to log into your server (more in that in the SSH Agent section).

In order to disable password authentication, open your SSH daemon configuration file: /etc/ssh/sshd_config and look for the following lines…

...
# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
...

…uncomment PasswordAuthentication and replace “yes” for “no”. Make sure you still have a way into your server before restarting the daemon.

If you’re not planning on logging in as the root user, uncomment and set the following setting to “no”.

...
#PermitRootLogin prohibit-password
...

Be aware of the fact that you can still utilise the root account using sudo su - (assuming you’re using sudo on your server, else use whatever other privilege escalation tool you have at hand).

Restarting the daemon on modern systems is usually done using:

systemctl restart sshd

If you’re not using systemd, I’m sure you know what command to use instead.

Adding keys to authorized_keys #

When going through /etc/ssh/sshd_config you’ve probably come across a few lines resembling:

...
# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile     .ssh/authorized_keys .ssh/authorized_keys2
...

This means that the SSH daemon will check in .ssh/authorized_keys in the home directory of the user as whom you’re trying to log in for authorized keys. So the next step is to append your public key to this file in the home directory of the user as whom you want to be able to log in. This can be done in a few ways. The proper way is by using:

ssh-copy-id -i ~/.ssh/<key-file> <user>@<host>

I’m usually too lazy to remember there is a proper way and just open the file in vi paste and it in there by hand during the same initial login when I’m disabling password authentication. Either way works fine.

Changing the port #

The advantage to this is that your logs will be a lot cleaner if your host is exposed to the internet. There are large amounts of bots hammering port 22 on every IP address they can find with common usernames and passwords like “root” and “admin”. A solution next to this is to use fail2ban along side changing the port.

“Won’t this mean I have to add the port to my login command every time I go to this server?”

No, more in this in the client configuration section

In /etc/ssh/sshd_config look for…

...
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
...

…and change the Port to your liking, I tend to change this to something like 6969 or some other meme number.

Another thing I tend to do is not open a port in my firewall, thus preventing any normal outside connections all together. Instead opting to only connect over Yggdrasil and/or Tor.

Client configuration #

If people tend not to think much about their server configuration, their client configuration is probably not even touched at all.

The ~/.ssh/config file #

The very first thing I do after setting up a server, is add an entry to my ~/.ssh/config in order to manage its key, the user, the port and possibly subdomain should the need arise.

A basic configuration section looks like the following:

Host <host> # this is something you can easily identify
	Host <hostname> # this does need to be an IP address or DNS record pointing to an IP address
	IdentityFile ~/.ssh/<key-file>
	User <user-name>
	Port <meme-number>

This allows you to log into host <host> with on port <meme-number> with key ~/.ssh/<key-file> as user <user-name> without by typing:

ssh <user-name>@<hostname> -p <meme-number> -i ~/.ssh/<key-file>

Instead the following command will work:

ssh <host>

More information on the SSH config file can be found in the ssh_config manpage.

Some other frequently used settings for me are:

AddKeysToAgent #

Automatically adds the key to your SSH agent if you have one running.

This is useful if you frequently log in and out of a certain host and don’t want to take the time to add it’s key to your agent manually.

IdentitiesOnly #

Ignore whatever keys your agent has and only use the contents of IdentityFile.

Useful for when you want to be able to log into the same host using multiple keys while using an SSH agent session.

ProxyCommand #

Always connect to your host using a proxy, using a given command.

Useful for when you can only access a host through a certain proxy.

I use this for my Tor hosts:

Host tor-<host>
        Hostname <lengthy-56-character-string>.onion
	# this is dependent on the netcat implementation of the OpenBSD project, often packaged as "netcat-openbsd"
        ProxyCommand nc -X 5 -x localhost:9050 %h %p # this assumes you are running a tor proxy on your local system and attempts to make a connection through that
        Identityfile ~/.ssh/<key-file>
        User <user>

The SSH Agent #

If you’re using SSH keys with passphrases, it will very quickly get annoying to type in the passphrase every time you use a certain key. To alleviate this tedium, the SSH agent exists.

If you’re using a full desktop environment, chances are that you already have an SSH agent running in the background. You can check this by seeing if $SSH_AGENT_PID is set to anything.

echo $SSH_AGENT_PID

If this isn’t set to anything, you can start an agent session by running:

eval $(ssh-agent)

Now you can add keys to your agent with:

ssh-add </path/to/key-file>

You can also have it automatically drop keys after a specified amount of time with the -t flag. I tend to do this with my root keys as a security precaution.

ssh-add -t 1h ~/.ssh/<root-key>

Starting an SSH agent every time you open a new shell session gets quite annoying quite quickly. There are a few things you can automate this. The simplest is to add eval $(ssh-agent) to your ~/.profile. Another option, the one I prefer, is to use keychain from the Funtoo project. It checks whether there’s an agent running every time you start a new login session. If there is, it sets the SSH agent environment variables to the existing ones from some other session. If there isn’t a running SSH session, it will start one.

I have the following in my ~/.profile:

...
eval $(keychain --agents 'gpg,ssh' --eval)
...

As you can see, it can also keep track of your GPG agent.

Forced Commands #

Another amazing feature of SSH is forced commands. The name is relatively self explanatory. You give a public key the privilege to execute a single command and nothing else. This is really useful if there is something that you frequently do on a server which is simple enough that it can be automated but still requires interaction from device.

I make use of a forced command to open NCMPCPP on my MPD server. Logging in every time and typing ncmpcpp every time I wanted to add or remove some songs from the current playlist got annoying really fast.

On my client machines I have an entry in my ~/.ssh/config with roughly the following:

Host <host>-radio
	Hostname <hostname>
	User <mpd-user>
	Port <meme-number>
	Identityfile ~/.ssh/<host>-radio
	IdentitiesOnly yes # here's where forcing it to ignore the ssh agent comes in useful

On my MPD server, I have the following in ~/.ssh/authorized_keys:

command="ncmpcpp" ssh-ed25519 <contents of <host>-radio.pub>

When I ssh to <host>-radio, all I get is an NCMPCPP session.

On one of my Raspberry Pis, I make use of a forced command to toggle my desk lamp using a little wrapper I wrote around the Pi’s GPIO interface. It has the following line in ~/.ssh/authorized_keys:

no-pty,command="/path/to/script/lamp toggle" ssh-ed25519 <contents of lamp-key.pub>

The no-pty option prevents any sessions opened with this key from starting an interactive shell session.

Documentation of the authorized_keys file can be found in the sshd manpage under the AUTHORIZED_KEYS FILE FORMAT section.