Howto Secure Shell easily from the terminal

I see many struggle when it comes to using Secure Shell in a comfortable way. Many are installing unneeded applications like Putty on Windows for example. Just like I did 4 years ago. Over the years I have been working a lot on servers where there was no GUI available and learned a lot doing that. I would like to share my tips and tricks so you can also be empowered by just sticking to the terminal on your OS or simply using Git Bash on Windows.

What is SSH

The SSH protocol (also referred to as Secure Shell) is a method for secure remote login from one computer to another. It provides several alternative options for strong authentication, and it protects the communications security and integrity with strong encryption. It is a secure alternative to the non-protected login protocols (such as telnet, rlogin) and insecure file transfer methods (such as FTP).

SSH key

Too ease the way you access servers or use Git I always recommend to create an SSH key. This reduces the amount of times you need to enter credentials and it adds a small layer of additional security by having the need for this (physical) ssh key file on available. To generate an SSH key you simply run the following command from your terminal or Git Bash when on Windows. Ensure to protect the key with a password, so no one can abuse it, when you loose or leak it somewhere. In general you can go with the default location.

Terminal
1
2
3
4
5
6
7
8
9
$ ssh-keygen -t rsa -b 4096 -C marco.franssen@my-email.com
Generating public/private rsa key pair.
Enter file in which to save the key (/users/marco/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ~/.ssh/id_rsa.
Your public key has been saved in ~/.ssh/id_rsa.pub.
The key fingerprint is:
30:a5:c1:7a:bb:00:bf:7c:24:b7:4d:f1:d6:3d:e5:7e marco.franssen@my-email.com

To break it down.

  • -t rsa => a key of type rsa (encryption format)
  • -b 4096 => use an encryption length of 4096 bytes long. Lower lengths are as of this writing considered insecure.
  • -C marco.franssen@my-email.com => a comment to document your key.

SSH Agent

Next up you do want to configure an SSH Agent, which will help us in a couple of ways. First of all we won’t continuously be prompted to enter the password for our key. The SSH Agent will cache our key for the lifetime of the SSH-Agent, or until you remove the key from the agent. Secondly it will allow us to do something called SSH Agent Forwarding. More on that later. Lets first have a look on how to enable an SSH agent on your machine.

MacOS

On MacOS the easiest to setup an SSH Agent is by installing keychain (Not to be confused with keychain shipped natively with your Mac). Easiest to install keychain is by using Homebrew.

Terminal
1
brew install keychain

Once keychain is installed we will add the following to our ~/.bash_profile.

~/.bash_profile
1
2
eval keychain --eval --agents ssh --inherit any id_rsa
source $HOME/.keychain/$HOSTNAME-sh

This will launch our ssh agent and cache our id_rsa key whenever we launch a terminal. Only the first time you will be prompted to enter a password for your key.

Windows

On Windows I assume you are using Git Bash. You can install Git via Chocolatey (I don’t like downloading installers manually and clicking through them manually).

Powershell
1
choco install -y git

Then we will add a small config file to instruct Git Bash to launch an SSH Agent. Following script is a Bash script that automatically executes when openening Git Bash. Important is to name and place the file exactly as mentioned below. Inside your users home directory.

C:\Users\Marco\.profile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
declare -x SSH_ENV="$HOME/.ssh/environment"

# start the ssh-agent
function start_agent {
echo "Initializing new SSH agent..."
# spawn ssh-agent
ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
echo succeeded
chmod 600 "${SSH_ENV}"
. "${SSH_ENV}" > /dev/null
ssh-add
}

# test for identities
function test_identities {
# test whether standard identities have been added to the agent already
ssh-add -l | grep "The agent has no identities" > /dev/null
if [ $? -eq 0 ]; then
ssh-add
# $SSH_AUTH_SOCK broken so we start a new proper agent
if [ $? -eq 2 ];then
start_agent
fi
fi
}

# check for running ssh-agent with proper $SSH_AGENT_PID
if [ -n "$SSH_AGENT_PID" ]; then
ps -f -u "${USERNAME}" | grep "$SSH_AGENT_PID" | grep ssh-agent > /dev/null
if [ $? -eq 0 ]; then
test_identities
fi
else
if [ -f "$SSH_ENV" ]; then
. "$SSH_ENV" > /dev/null
fi
ps -f -u "${USERNAME}" | grep "$SSH_AGENT_PID" | grep ssh-agent > /dev/null
if [ $? -eq 0 ]; then
test_identities
else
start_agent
fi
fi

Now having this in place for every new terminal we open we simply will have the Agent with a loaded ssh-key available. Only the first terminal you open will request you to enter the password for your ssh key. Now you can simply git clone, git push and git pull without the need to enter passwords. You will first have to upload your key to your Git Server as described in the section Authorize your Key though. If you can bring up the patience please continue reading before trying to authorize your key.

SSH Config

Next we add a ~/.ssh/config file. In this file I define some Hosts which allow me easy access to the servers using an easy to remember name. Host defines an alias you can use for your server, name it to be easy remembered or describe your server. The hostname is the ip address or dns record on which the server can be accessed. The port in general is port number 22, unless your server admin changed it. Next we can define user which will be used as the user to login to the server.

~/.ssh/config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Host nas
Hostname 192.168.0.20
Port 2200
User marco

Host raspberry
Hostname 192.168.0.33
User pi

# Host proxy-example-via-nas
# HostName raspberry
# ProxyCommand ssh nas -W %h:%p

Host *
Port 22
ForwardAgent=yes
IdentityFile ~/.ssh/id_rsa

The Host * applies the defined settings for all the other hosts, so we don’t have to repeat the setting everywhere. The forward agent option allows us to use our key also on the server during our session. This has as an advantage that you will never have the need to store SSH keys on the server. This means a potential hacker could only have access to your SSH key during the time you have an active session. Using the forward agent option you can do things like hopping to other servers which also have your key authorized or you can do things like git clone on the server using the ssh-key on your client.

Furthermore you can use # to make comments in the file, for the sake of documenting your own things. In above example I commented a configuration that would allow us to use an automated SSH hop via my nas host to the raspberry. E.g. this can be very usefull if there is only a single server publicly exposed (bastion server) and the rest of your servers are behind a firewall in a private network. Using this approach we can still directly access those servers by connecting to the alias that applies the SSH hop strategy.

Authorize your key

Now we have ~/.ssh/config in place we can very easily SSH into servers. The only thing remaining is to authorize our SSH Key on the server(s). This can be done manually by going onto the server and updating the file ~/.ssh/authorized_keys or simply by using the ssh-copy-id command that will allow us to do this in an easier way.

1
2
3
4
5
6
7
8
9
$ ssh-copy-id raspberry
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
pi@192.168.0.33's password:

Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'raspberry'"
and check to make sure that only the key(s) you wanted were added

For Git this usually boils down to upload your public key on your Git provider profile. E.g.:

To copy your key on MacOS you simple do following command:

1
cat ~/.ssh/id_rsa.pub | pbcopy

This will read the contents from your public key and put it on the clipboard.

On Windows in your Git Bash you simply do the following:

1
cat ~/.ssh/id_rsa.pub | clip

Please be sure to never share your private key (~/.ssh/id_rsa) anywhere.

Now on your profile page simply paste your key (ctrl+v or command+v) to authorize it and give it a description. E.g.: Key on my Macbook Pro. To verify if you have access you can run following command on your terminal / Git Bash.

1
2
$ ssh -T git@github.com
Hi marcofranssen! You've successfully authenticated, but GitHub does not provide shell access.

Done

Now with all these things in place we can simply run commands in the form of ssh <your alias>. Please also try the following to ensure you have access to your SSH Agent on the server itself.

1
2
3
4
5
$ ssh-add -l
4096 SHA256:X3LpX9VnhhGsJa2eEReYr50nQP3izSX9I4Nqj3L18kh /Users/marco/.ssh/id_rsa (RSA)
$ ssh raspberry
pi@raspberry:$ ssh-add -l
4096 SHA256:X3LpX9VnhhGsJa2eEReYr50nQP3izSX9I4Nqj3L18kh /Users/marco/.ssh/id_rsa (RSA)

You can see on my client I have my key loaded in the ssh-agent. Then when I ssh into the raspberry I’m running this command again. As you can see on the raspberry my key is also accessible via the Agent Forwarding. You can see this based on the fingerprint as well from the key file location, which is the location of the key on my client.

Bonus

Thank you for reading my blogpost. If you made it till here you captured my nice little autocompletion Bonus, which is my present to you for making it to the end.

~/.bash_profile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# autocomplete ssh hosts configured in ~/.ssh/config

_ssh()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts=$(grep '^Host' ~/.ssh/config | grep -v '[?*]' | cut -d ' ' -f 2-)

COMPREPLY=( $(compgen -W "$opts" -- ${cur}) )
return 0
}
complete -F _ssh ssh
complete -F _ssh scp
complete -F _ssh ssh-copy-id

Above function will simply grep all the lines starting with Host. Now when you type either ssh, scp or ssh-copy-id it will autcomplete based on your aliases. This way when having a huge amount of hosts you can simply autocomplete their names by using the shell autocompletion feature. Shell autocompletion is triggered when you hit the tab key on your keyboard twice.

E.g.:

1
2
$ ssh r<tab><tab>
raspberry

References

Please leave me a comment and share on Social media with your friends.

Share