Loading...
Loading...
Marco Franssen

I see many people struggle to use Secure Shell (SSH) comfortably. On Windows, for example, they often install applications such as PuTTY when the terminal would do—just like I did four years ago. Over the years, I've spent a lot of time working on servers without a GUI and learned plenty along the way. I'd like to share my tips and tricks so you can get comfortable using your OS's terminal, or Git Bash on Windows.
The SSH protocol (Secure Shell) lets you log in securely from one computer to another. It supports several strong authentication methods and uses encryption to protect the confidentiality and integrity of communication. It's a secure alternative to unprotected login protocols such as telnet and rlogin, and insecure file transfer methods such as FTP.
To make accessing servers or using Git easier, I always recommend creating an SSH key. This reduces how often you need to enter credentials and adds a layer of security by requiring the key file. To generate one, run the following command in your terminal, or in Git Bash on Windows. Protect the key with a passphrase in case you lose or leak it. You can generally use the default file location.
$ 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.comHere's what the options mean:
-t rsa => create an RSA key.-b 4096 => use an encryption length of 4096 bytes. At the time of writing, I considered shorter lengths insecure.-C marco.franssen@my-email.com => add a comment to identify your key.Next, let's configure an SSH agent, which helps in two ways. First, you won't be prompted for your key's passphrase continually. The agent caches your key for its lifetime, or until you remove it. Second, it lets us use SSH agent forwarding. More on that later. Let's first look at enabling an agent on your machine.
On macOS, my preferred way to set up an SSH agent is to install keychain (not to be confused with the Keychain shipped with your Mac). The easiest way to install it is through Homebrew.
brew install keychainOnce keychain is installed, we'll add the following to our ~/.bash_profile.
eval keychain --eval --agents ssh --inherit any id_rsa
source $HOME/.keychain/$HOSTNAME-shThis launches our SSH agent and caches our id_rsa key when we open a terminal. You'll only be prompted for the key's passphrase the first time.
On Windows, I'm assuming you're using Git Bash. You can install Git via Chocolatey (I don't like downloading installers and clicking through them manually).
choco install -y gitNext, we'll add a small configuration file to tell Git Bash to launch an SSH agent. The following Bash script runs automatically when you open Git Bash. Make sure you use the filename and location shown below, inside your user's home directory.
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
fiWith this in place, each new terminal has an agent with your SSH key loaded. Only the first terminal you open asks for the key's passphrase. You can then use git clone, git push, and git pull without repeatedly entering passwords. You'll first need to upload your public key to your Git server, as described in Authorize your key. Bear with me and read on before trying that step.
Next, we add a ~/.ssh/config file. I use this file to define hosts so I can connect using memorable names. Host defines the alias for your server; choose something easy to remember or descriptive. hostname is the IP address or DNS name used to reach it. The port is usually 22, unless your server administrator has changed it. Finally, user defines the account used to log in.
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_rsaThe Host * entry applies its settings to all hosts, so we don't have to repeat them everywhere. Agent forwarding lets us use our local key during a session on the server without storing the private key there. A potential attacker on that server could use the forwarded agent while the session is active. With forwarding enabled, you can hop to other servers that authorize your key, or run commands such as git clone on the server using the SSH key on your client.
You can use # to add comments and document your configuration. In the example above, I've commented out a configuration that would let us make an automatic SSH hop through my nas host to the Raspberry Pi. This is useful when only one server—a bastion server—is publicly exposed and the others are behind a firewall on a private network. We can still access those servers by connecting to an alias that configures the SSH hop.
With ~/.ssh/config in place, connecting to servers is much easier. All that's left is to authorize our SSH key on each server. You can do this manually by adding your public key to the server's ~/.ssh/authorized_keys file, or use ssh-copy-id to make it easier.
$ 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 addedFor Git, this usually means uploading your public key to your Git provider's profile settings. For example:
To copy your public key on macOS, run:
cat ~/.ssh/id_rsa.pub | pbcopyThis reads the contents of your public key and copies them to the clipboard.
On Windows, run the following in Git Bash:
cat ~/.ssh/id_rsa.pub | clipNever share your private key (~/.ssh/id_rsa).
On your profile page, paste the public key (ctrl+v or command+v) to authorize it and add a description, such as Key on my Macbook Pro. To verify access, run the following command in your terminal or Git Bash.
$ ssh -T git@github.com
Hi marcofranssen! You've successfully authenticated, but GitHub does not provide shell access.With everything in place, we can connect using ssh <your alias>. Try the following to check that your forwarded SSH agent is available on the server too.
$ 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)On my client, you can see that my key is loaded in the SSH agent. After connecting to the Raspberry Pi, I run the same command again. The key is available there through agent forwarding, as shown by its matching fingerprint and the key file location on my client.
Thanks for reading my blog post! If you've made it this far, here's a little autocompletion bonus as a thank-you for sticking with me.
# 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-idThe function above finds the lines starting with Host in your SSH config. When you type ssh, scp, or ssh-copy-id, it offers autocompletion using your aliases. This is handy when you have lots of hosts and don't want to remember every name. Press the tab key twice to see the available completions.
For example:
$ ssh r<tab><tab>
raspberryPlease leave a comment and share this post with your friends on social media.
Marco Franssen
Sign Docker image tags with Docker Content Trust, manage signing keys and repository signers, inspect signatures, and enable trust verification.
Marco Franssen
Explore Krypton's phone-based SSH and PGP setup, pairing a laptop with a mobile device to approve server logins and sign Git commits.
Marco Franssen
Manage Go development tools with Go modules using a tools.go file, build constraints, and a Makefile to keep local and CI tool versions aligned.
Marco Franssen
Set up Git, Vim, and Bash on Raspbian, mount NFS shares from a Synology NAS, and configure Kodi to start automatically on your Raspberry Pi.