Skip to main content
Skip table of contents

Configuring SSH

SSH enables secure connections between computers. It can be used from the command line and with desktop tools for lots of things:

  • Remote shell access
  • Accessing remote git repositories
  • Tunneling network traffic that might otherwise be blocked by firewalls

Step-by-step guide

The basic SSH workflow is to authenticate then exchange encryption keys so that all network traffic for that connection is encrypted and secure. You can use standard username/password authentication but this requires entering a username and password every time you start ssh. The easier way to do this is to create a public/private SSH key and configure your SSH targets to use that.

Create public/private key pair

If you don't already have the folder ~/.ssh, start by creating that:

CODE
mkdir ~/.ssh
chmod 700 ~/.ssh

Note the chmod command. ssh will refuse to work if that folder and its contents aren't properly secured. For most files in that folder, this means no read/write access for group or world (i.e. 600 or 700), with the exception of public keys (with an extension of .pub).

Once you have your ssh folder set up, generate an SSH key with the following command:

CODE
# ssh-keygen -t ed25519 -C whoever@wherever.edu
Generating public/private ed25519 key pair.
Enter file in which to save the key (/data/xnat/home/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /data/xnat/home/.ssh/id_ed25519
Your public key has been saved in /data/xnat/home/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:r8FWGsinmZcHEoFabTeBAMDkYVu7wczthXrLrFd5FT4 whoever@wherever.edu
The key's randomart image is:
+--[ED25519 256]--+
|+=.o.+....       |
|o.B = =.o  .     |
| o O +.o .. .    |
|  . =..o   E     |
|   o o+ S o .    |
|    + .X O       |
|     += O o      |
|    .. o +       |
|   ..   .        |
+----[SHA256]-----+

Substitute your own email address (although the address is just informative and doesn't affect the actual key generated).

In response to the prompt "Enter file in which to save the key..." press Enter to save to the default destination shown in parentheses. You can specify another file to use but the default destination is where ssh looks unless you tell it otherwise, so that's usually the easiest.

In response to the passphrase prompts, you can hit Enter to create a key without password protection but you should never do this other than for the most basic development environments for ease of use and not even then if the development environment could possibly be exposed outside of your local network.

Your public/private key pair can be a security risk:

  • Provide a strong password to protect your key
  • Don't store them in a git repository or stash them in a publicly accessible location
  • Store backup copies in a secret manager like LastPass or similar application
  • Only store working copies in properly secured .ssh folders

Finally, don't lose the files! I mention this because I used to "misplace" them a lot and kept having to regenerate them.

Older systems may not support ed25519 signatures. In that case you can omit the -t ed25519 parameter and generate an RSA key pair.

To verify your newly generated key pair, look in your ~/.ssh folder. You should see two files, id_ed25519 and id_ed25519.pub:

CODE
# ls -l ~/.ssh
total 8
-rw------- 1 xnat xnat 444 Feb  9 09:24 id_ed25519
-rw-r--r-- 1 xnat xnat  95 Feb  9 09:24 id_ed25519.pub

You can verify your key pair with the following command:

CODE
# ssh-keygen -y -f ~/.ssh/id_ed25519 | diff -s - <(cat ~/.ssh/id_ed25519.pub)
Enter passphrase:
Files - and /proc/self/fd/16 are identical

If you see anything other than Files ... are identical, your keys are mismatched and should be regenerated.

Setting up remote SSH access

Accessing a remote server through SSH using username and password authentication requires no advance setup. Accessing it using your public/private key pair is much easier in regular usage but requires some configuration on the remote side:

  1. Log into the remote server as the user you want to use for SSH access. For some servers, such as Vagrant VMs, this may mean logging in as one user (e.g. vagrant) then sudo'ing or su'ing to the target user. For example, on a Vagrant VM you might do this: 

    CODE
    # vagrant ssh
    Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-91-generic x86_64)
    
    vagrant@focal:~$ sudo su - xnat
    xnat@focal:~$
  2. If you haven't already created the ~/.ssh folder for this user, create it now as described in the previous section.
  3. In the ~/.ssh folder, create a file named authorized_keys.
  4. Set the permissions on authorized_keys to 600.
  5. Add the contents of your public key file (e.g. id_ed25519.pub) to authorized_keys and save.

That's it. You can now ssh directly into the remote server. From your desktop (or wherever you're ssh'ing from), run the following command:

CODE
ssh -i ~/.ssh/id_ed25519 xnat@focal.xnat.org
ssh -i ~/.ssh/id_ed25519 -l xnat focal.xnat.org

These commands tell ssh to use the specified private key (or identity) to access the remote server. The login name can be set through the -l option or with the @ notation: these are functionally equivalent, but the @ notation is by far the more commonly used form.

Configuring SSH destinations

SSH references the configuration file ~/.ssh/config. There's a lot more you can do with this configuration than can be covered in this simple how-to guide (here's a fairly exhaustive reference on configuration options), so this will just address a couple basic configurations that will make it much easier to integrate SSH into your workflow.

Most entries in your config will look something like this:

CODE
Host focal
	HostName     	focal.xnat.org
	Port			22
	User			xnat
 	IdentityFile 	~/.ssh/id_ed25519

This is pretty straightforward for the most part:

  • HostName is the address of the remote server
  • Port is the SSH port (port 22 is the default port for ssh operations so doesn't actually need to be specified, but is provided here for illustration)
  • User is the account you want to use to login (and which presumably has your public key in its ~/.ssh/authorized_keys file)
  • IdentityFile is the private key you want to use when authenticating

The one thing that may seem odd is the distinction between Host and HostName. Basically, Host is the key to this particular configuration, while HostName is just the server address. This is where configuring your remote SSH destinations is really useful. Now instead of the longer ssh command above, you can ssh to the remote server with a much simpler command:

CODE
ssh focal

In that command, ssh looks for the Host entry with the name focal and, if found, uses the configuration options to file in the details for the ssh operation.

In cases where you may access multiple remote servers with similar configurations, you can combine configurations by using the %h escape character to substitute host names:

CODE
Host focal buster bullseye
	HostName     	%s.xnat.org
	Port			22
	User			xnat
 	IdentityFile 	~/.ssh/id_ed25519

The hosts focal, buster, and bullseye will be resolved to the hostnames  focal.xnat.org, buster.xnat.org, and bullseye.xnat.org respectively, while port, user, and identity file will all remain the same.

You also probably want to create a "global" configuration. This should be at the top of your config file and looks something like this:

CODE
Host *
    UseKeychain     yes					# OS X only
    AddKeysToAgent  yes					# OS X only
    IdentityFile    ~/.ssh/id_ed25519
    IdentitiesOnly  yes
    ForwardAgent    yes

Because these apply to the "global host" ssh uses them as default values for these configuration options unless any of those options are explicitly overridden in your specific host entry. For example, if you have a different identity file for your remote server (this happens a lot when working with cloud environments like AWS/EC2), you can just specify that for your host:

CODE
Host *
    UseKeychain     yes					# OS X only
    AddKeysToAgent  yes					# OS X only
    IdentityFile    ~/.ssh/id_ed25519
    IdentitiesOnly  yes
    ForwardAgent    yes

Host ec2-server
	HostName     	xxx.aws.amazon.com
	User			ec2-user
 	IdentityFile 	~/.ssh/ec2-server.pem

Setting up ssh-agent

So you've created a public/private key pair with a nice strong password, added your public key to the login user on the remote server, and configured the remote server in your ssh configuration. Everything's great except... you have to enter that nice strong password every time you ssh somewhere.

Handling this is the realm of ssh-agent. This application runs in the background and stores "live" copies of your private keys. You still have to provide your password when ssh-agent first starts, but all SSH applications that use private keys first check if ssh-agent is running and, if so, tries to get the private key from there.

The primary challenge with ssh-agent is getting it started once then re-using it. On Macs, this is easy, as the system starts ssh-agent automatically. On other systems, you need to know both the PID for the SSH agent process and, more importantly, the socket on which the agent is running (configured with the SSH_AGENT_PID and SSH_AUTH_SOCK environment variables respectively).  ssh-agent helps with this by outputting the necessary variables on start-up:

CODE
# ssh-agent -s
SSH_AUTH_SOCK=/tmp/ssh-teJ5qoSYYNWc/agent.55080; export SSH_AUTH_SOCK;
SSH_AGENT_PID=55081; export SSH_AGENT_PID;
echo Agent pid 55081;

That's helpful but would be even more helpful if integrated into your environment, which can be accomplished through command substitution:

CODE
# eval $(ssh-agent -s)
Agent pid 55128
# echo ${SSH_AGENT_PID}
55128
# echo ${SSH_AUTH_SOCK}
/tmp/ssh-rpalFIoHiREI/agent.55127

Storing this output makes it accessible for later login sessions (ssh-agent continues running across login sessions). The following snippet of scripting can be added to a bash or zsh initialization file (e.g. .zshrc) and used to initialize and restore ssh-agent settings across login sessions.

CODE
[[ -f ${HOME}/.ssh/.agent && $(ps -q $(grep 'echo Agent pid ' ${HOME}/.ssh/.agent | sed -E 's/^echo Agent pid ([0-9]+);$/\1/') -o comm=) != "ssh-agent" ]] && { rm -f ${HOME}/.ssh/.agent; }
[[ -f ${HOME}/.ssh/.agent ]] && {
    source ${HOME}/.ssh/.agent > /dev/null
} || {
    ssh-agent -s > ${HOME}/.ssh/.agent
    source ${HOME}/.ssh/.agent
    ssh-add ${HOME}/.ssh/id_ed25519
}

Using SSH keys with git

One of the handiest uses of SSH keys is working with remote git repository servers like Bitbucket and Github. Once you have your public/private key pair generated, all you have to do is configure each service to recognize your public key. This is described in detail in each provider's documentation:

The primary difference between using http-based addresses with username/password authentication and ssh is in the address for repos. You can access a git repo on, e.g., Bitbucket with something like this:

CODE
git clone https://username:password@bitbucket.org/xnatdev/xnat-web.git

This is convenient because you can easily push commits to that repository without having to enter your password every time, but it also leaves your username and password in plain text in the repo metadata.

With ssh, the remote repo URL changes:

  • https:// is replaced by git@
  • The username and password are gone and replaced implicitly by the username git (from the prefix above) and an identity file (the default identity or one specified in a host-specific configuration)
  • The / after the domain is replaced by :

The command above then becomes:

CODE
git clone git@bitbucket.org:xnatdev/xnat-web.git



JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.