Post date: Oct 18, 2019 8:41:50 PM
I rarely use password login, and in my career I've been surprised at how many people have a hard time wrapping their head around how to use SSH keypairs or have never even heard of them. What are they? Think of the private key as the key to the locks, and the public key as an endless supply of locks you can use wherever you want (sort of). So you can use your ssh private key to log in instead of a password wherever you've set up your public key. Why would you do this? In short, you get better security AND convenience. I know, typically these two are exclusive to each other.
To explain the security, I'll explain the difference between how password login and ssh keypair login works. With password login, you first establish a secure encrypted channel to the remote system, and then send your username and password to that system. The remote system then compares those credentials with whatever mechanisms are set in place (local, ldap, etc), and determines whether you provided the correct password and whether you are authorized to log in. With ssh keypair exchange, you establish a secure encrypted channel, then the remote system sends a random block of data for you to "sign". If the signature verification passes with the public key set up on that remote system, you pass the credential check and it continues to see if you are authorized. The main difference here is that the private key used for logging in is never sent over the wire, whereas with password login the password is sent to the remote system. If the remote system were compromised, and collected passwords from ssh logins, it could not do the same for ssh keypairs. One caveat here is that you MUST guard your private key! If you leave that laying around, it's like writing your password on the wall.
So let's get to how to set it up and guard your private key. First of all, you probably only want one copy of your private key, and maybe a backup offline somewhere. Whatever you do, do not go pushing your private key around to all your systems! Again, like your house key, less copies is better.
You can generate a keypair with the command:
ssh-keygen -t rsa -b 4096
That creates a 4096 bit rsa keypair. You may want more or less bits in your key size, but this is what I use. By default, this creates files in ~/.ssh/id_rsa for the private key and ~/.ssh/id_rsa.pub for the public key. Now we have our keypair, all we need to do is drop the public key on systems and we can start using it.
You can use the utilities in the openssh package to push the public key, this helps a little by creating the authorized_keys file with the correct permissions for us. But, while on the subject of file permissions, it's worth a mention that file and directory permissions is the #1 reason I've seen for ssh keypair login not working. The SSH server checks permissions of the authorized_keys file, and if they're too open, it will not honor it and your keys will not work. It also checks permissions on the .ssh directory, and your home directory. Why does it do this? Because, let's say group has write permission on your home directory. That would allow anyone in your group to rename .ssh, create a new .ssh/authorized_keys, and drop their own key and be able to compromise the account. So if ssh keypair login isn't working or mysteriously stopped working, check ACLs on home directory, .ssh, and authorized_keys to make sure they are correct.
You can push your new ssh public key with ssh-copy-id:
ssh-copy-id -i ~/.ssh/id_rsa me@someremotehost
I like to turn right around and test it with something like:
ssh -i ~/.id_rsa me@someremotehost "uname -a"
If it gives you the output of uname from the remote system without prompting for your password, then you're set, otherwise I'd start with checking permissions on directories and the authorized_keys file. Another place to look is the sshd_config on the remote system to make sure PubkeyAuthentication isn't disabled.
Now for some more advanced stuff... if you encrypted your key during generation, and you get prompted for decryption phrase every time you use it, you may be wondering where the convenience is. Here's where ssh-agent comes in. ssh-agent is ssh's authentication agent, described in it's man page as "ssh-agent is a program to hold private keys used for public key authentication". It's meant to hold private keys decrypted in-memory for ssh to use. The way I use ssh-agent is I start the agent, and load my private keys from disk, giving the decrypt phrase for each one. Then through the day, when a key is needed, ssh talks to the agent and doesn't need to prompt me for decrypt phrases. At the end of the day, the computer is shut down, and the keys are encrypted at rest again.
To start the agent, I'd recommend something like this:
eval $(ssh-agent)
We use "eval" here because ssh-agent outputs variables we want our shell to pick up, like where the agent socket is and what pid the process is running.
Now we can add our private key:
ssh-add ~/.ssh/id_rsa
And check it with:
ssh-add -l
For even more advanced stuff, you can do neat things like forwarding the authentication agent socket through ssh by using the "-A" option. This would allow you to use the agent from a remote host for the duration of the ssh session. It's kind of like having the private key on the remote host without ever putting it there!
Here's where I really dig into the weeds. I look at /var/log/secure all the time and see SHA256 fingerprints of accepted publickeys logged there all the time. How do we map that back to public keys listed in authorized_keys, those are in entirely different format?
examples:
Oct 18 11:37:31 hostname sshd[12650]: Accepted publickey for username from 10.1.1.1 port 1234 ssh2: RSA SHA256:0N/i2VOsIOX81p1CmAx583ai1w279GQqqtWcMSLyFTI
vs:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD8H24zDmbx6LGtc1RKzKIfClhuLEccmMW1VRib8rI2ZGfwuWfRA09RFheTqh8oFbFMaGhoiIUjFIE6Uw7JSv4jxaMvcEbKENfkfTlz1WgL9UG52WxJXPSYmKkYeCBW4l36iWVgB0x2sBrgarPxNdsnuUq0HAVVOfRTtkUtFLpH7Dij7Zomq2iiH2ZhfkQ9gHxnBOg9DJy5A5QQ2LH+VU+W7D7D16/qG8qXOZyLuqFxPnQGuCR2bhheIp2Zb+bpVtuExzO58jRf4JWEzPly/1qVlxctXmvDTCtlCblHKzbiegvU9bSXZAuuW+Kqs+3TUtiSAlfsjrgX5ZcUH4iVRLWkA4qnt8Knn3SYAGeI//O7BaSur2GCinhG3HiJnmyHRF/lIy3Xh6o/lEaNSLamZ1hCSe69duYewusqQ+lHCkQtJwCVowxTfQusk8c+7zjSC4cDmr3M7NFA+N/n25DPZGA+8Yx/jhu1r48GUt6D+OyrTKbeRNOoM0Z9lD62gCp3aey0HM2nGxWgGgAb2TRE0Z87JmnUEkTcT25cUq31fQ119uJD8kWH+G8QVoAIRPFvr2MLzVpGQm2P9YTTP8wifvtVX7hJ+pd1vpTbj7cYTnLO32CmBce747IwNtcyxcfq9xYfOLEB+aMD5nN+ZNXU7LL9Dmd2cDPuZeYePlLdY81+XQ== someuser@somehost
Hmm, let's see if we can get the SHA256 some of that public key to match from the line in /var/log/secure:
echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD8H24zDmbx6LGtc1RKzKIfClhuLEccmMW1VRib8rI2ZGfwuWfRA09RFheTqh8oFbFMaGhoiIUjFIE6Uw7JSv4jxaMvcEbKENfkfTlz1WgL9UG52WxJXPSYmKkYeCBW4l36iWVgB0x2sBrgarPxNdsnuUq0HAVVOfRTtkUtFLpH7Dij7Zomq2iiH2ZhfkQ9gHxnBOg9DJy5A5QQ2LH+VU+W7D7D16/qG8qXOZyLuqFxPnQGuCR2bhheIp2Zb+bpVtuExzO58jRf4JWEzPly/1qVlxctXmvDTCtlCblHKzbiegvU9bSXZAuuW+Kqs+3TUtiSAlfsjrgX5ZcUH4iVRLWkA4qnt8Knn3SYAGeI//O7BaSur2GCinhG3HiJnmyHRF/lIy3Xh6o/lEaNSLamZ1hCSe69duYewusqQ+lHCkQtJwCVowxTfQusk8c+7zjSC4cDmr3M7NFA+N/n25DPZGA+8Yx/jhu1r48GUt6D+OyrTKbeRNOoM0Z9lD62gCp3aey0HM2nGxWgGgAb2TRE0Z87JmnUEkTcT25cUq31fQ119uJD8kWH+G8QVoAIRPFvr2MLzVpGQm2P9YTTP8wifvtVX7hJ+pd1vpTbj7cYTnLO32CmBce747IwNtcyxcfq9xYfOLEB+aMD5nN+ZNXU7LL9Dmd2cDPuZeYePlLdY81+XQ== someuser@somehost' | cut -f2 -d' ' |base64 -d | openssl sha -sha256 -binary | base64
0N/i2VOsIOX81p1CmAx583ai1w279GQqqtWcMSLyFTI=
Well, there it is, it matches the one from the line in /var/log/secure!
Now, can we match, or verify and check to see if a public key and private key is a pair? I remember comparing the key modulus on x509 certificates and keys. Here's what I did for the ssh keypair:
openssl rsa -in id_rsa -modulus -noout | openssl md5 -binary | base64
/bGywdQF0xtTQkdiLCIX/A==
ssh-keygen -e -m PKCS8 -f id_rsa.pub | openssl rsa -pubin -modulus -noout | openssl md5 -binary | base64
/bGywdQF0xtTQkdiLCIX/A==
So, since the modulus matches on these two, they are a pair and the private key can be used to login where the public key is present.