Doing SSH right (on Windows)

Windows now ships with OpenSSH, making life easier for all of us.

Generating an SSH Keypair

I generate my SSH keys with the following considerations in mind:

  • To simplify identifying keys for me and other sysadmins: I add a comment containing my name, the remote system (TEST KEY in this example) and the algorithm used.
  • Security
    • Use ed25519 whenever possible,
    • use RSA with -b 4096 as a fallback,
    • never use DSA or ECDSA,
    • use 100 rounds of key derivation instead of the default 16 to slow down attackers trying to brute force the password of a stolen key,
    • always use a password as this gives you a second authentication factor
  • Up to date: use the new OpenSSH key format with -o
  • Convention: Put the keys in the default location (~/.ssh/) and provide a sensible filename
> ssh-keygen -C "Michael Oberauer - TEST KEY - ed25519" -t ed25519 -o -a 100 -f $env:USERPROFILE/.ssh/TEST_KEY
Generating public/private ed25519 key pair.
Created directory 'C:\Users\oberauer_m/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\oberauer_m/.ssh/TEST_KEY.
Your public key has been saved in C:\Users\oberauer_m/.ssh/TEST_KEY.pub.
The key fingerprint is:
SHA256:4rOGnioriLbyd5iI43p2ssCs1+7Cxz0BP1hB22HH6FY Michael Oberauer - TEST KEY - ed25519
The key's randomart image is:
...

You now have a private key file and a public key file (.pub). The private key file never leaves your computer.

> Get-ChildItem $env:USERPROFILE/.ssh

    Directory: C:\Users\oberauer_m\.ssh

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          10/07/2024    08:58            484 TEST_KEY
-a---          10/07/2024    08:58            120 TEST_KEY.pub

The permissions for the public key are okay, but the private key unfortunately has Full control set by default for SYSTEM and Administrators. As this is MY key, I’ll lock them out using these two commands:

> icacls $env:USERPROFILE\.ssh\TEST_KEY /remove:g SYSTEM
> icacls $env:USERPROFILE\.ssh\TEST_KEY /remove:g Administrators

Installing your key on the remote system

Copy the content of your .pub file into ~/.ssh/authorized_keys.

The config file ~/.ssh/config

Precedence of entries

The first entry (from top to bottom) the ssh client finds will be used. That means your more specific settings go higher up, your more general settings to the bottom. I use a catch all with some sensible default options:

Host *
#don't blindly try all keys stored in the agent, use the one specified in the config file
	IdentitiesOnly yes 
#only use "new" and strong ciphers
	Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
#don't use agent forwarding, it has security problems
	ForwardAgent no
#Don't use protocol 1
	Protocol 2
#Use compression to speed stuff up on slow links
	Compression yes
#Disable challenge response authentication
	ChallengeResponseAuthentication no
#OpenSSH Server does not support roaming anyway, disable to narrow attack surface
	UseRoaming no
#use public key auth, fallback to password if necessary
	PreferredAuthentications publickey,keyboard-interactive,password

Jump-Hosts

Often times not all servers are reachable via the internet directly via SSH, which makes perfect sense from a security standpoint. To get to one of the “internal” servers you would generally ssh into a machine reachable from the internet and then ssh once more to your actual target. With the ProxyJump entry you can have the ssh client do this for you.

Host serverwithoutinternet
    HostName some-server.local
    ProxyJump serverontheinternet
Host serverontheinternet
    HostName publicssh.somewhere.abc

You can then connect to some-server.local with ssh serverwithoutinternet.

Note that using this approach, the ssh session is handed to your local machine by the jump host meaning the authentication key for some-server.local does not have to be stored on the jump host. This is a safe alternative to agent forwarding.

Specify which user and key to use to login to a server

Host abc
    HostName abc.somewhere.on-the.net
    User someuseronabc
    IdentityFile ~/.ssh/mykeyforabc

SSH Agent

Make the service start automatically and start it now.

PS (Admin)> Set-Service ssh-agent -StartupType Automatic
PS (Admin)> Start-Service ssh-agent

Add the key and list all keys known to the ssh-agent

> ssh-add "$env:USERPROFILE\.ssh\TEST_KEY"
Enter passphrase for C:\Users\oberauer_m\.ssh\TEST_KEY:
Identity added: C:\Users\oberauer_m\.ssh\TEST_KEY (Michael Oberauer - TEST KEY - ed25519)
> ssh-add -l
256 SHA256:4rOGnioriLbyd5iI43p2ssCs1+7Cxz0BP1hB22HH6FY Michael Oberauer - TEST KEY - ed25519 (ED25519)

All operations involving the private key are now handed off by clients (like ssh) to the agent. The private key, once decrypted using the password, stays in RAM and never leaves the ssh agent.

Photo by Folco Masi on Unsplash