I want to access my github repositories via ssh. When I access the repository for the first time, I am asked If I want to add the github ssh server to my known_hosts
file, which works fine. That request also shows me the RSA key fingerprint of that server and I can manually verify that it is the same that is provided by github here.
These are the SHA256 hashes shown in OpenSSH 6.8 and newer (in base64 format):
SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 (RSA)
SHA256:br9IjFspm1vxR3iA35FWE+4VTyz1hYVLIE2t1/CeyWQ (DSA)
The problem is that I want to prevent that request by adding a public key
to my known_hosts
file before my first access to my git repository. This can be done by using the ssh-keyscan -t rsa www.github.com
command which will give me a public key in the format required by the known_hosts
file. But people mention repeatedly, that this is not safe and is vulnerable to man-in-the-middle attacks. What they do not mention is how to do it right.
So how can I use the RSA fingerprint provided on the github page to safely get the public host key of the ssh server? I am more or less looking for an option to the ssh-keyscan
command that lets me add the expected rsa fingerprint and causes the command to fail if the hosts fingerprint does not match the given one.
Thank you for your time!
The known_hosts file is a list of hostnames (or often, hashes of hostnames), the type of the fingerprint, and the fingerprint itself (cryptographic information) in base64 encoding. The format details can be found in the OpenSSH man page, under the SSH_KNOWN_HOSTS FILE FORMAT section.
The known_hosts file is for verifying the identity of other systems. ssh(1) can automatically add keys to the user's file, but they can be added manually as well. The file contains a list of public keys for all the hosts which the user has connected to.
These hosts keys are stored at locations '/etc/ssh/known_hosts' and '. ssh/known_hosts' in each user's home directory.
authorized_keys is used to identify which public keys are used to match to a users private key to authenticate to the targeted ssh system. The known_hosts is used from the source system to identify the target system's authenticity.
I would not use ssh-keyscan in that case.
Rather, I would use it and double-check the result by comparing its fingerprint with the one provided by GitHub.
And then proceed with an SSH GitHub test, to check I do get:
Hi username! You've successfully authenticated, but GitHub does not
provide shell access.
So, as recommended here, for the manual process:
ssh-keyscan github.com >> githubKey
Generate the fingerprint:
ssh-keygen -lf githubKey
Compare it with the ones provided by GitHub
Finally, copy githubKey
content to your ~/.ssh/known_hosts
file.
You can automate that process (still including the fingerprint step check) with wercker/step-add-to-known_hosts
: it is a wercker step, but can be extrapolated as its own independent script.
- add-to-known_hosts:
hostname: github.com
fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
type: rsa
But that would lack the check against help.github.com/articles/github-s-ssh-key-fingerprints
: see below.
Using nmap does not help much, as explained here:
using
nmap
to get the SSH host key fingerprint and then comparing it to whatssh-keyscan
says the fingerprint: In both cases, the fingerprint comes from the same place.
It's just as vulnerable to MITM as any other of these automated solutions.The only secure and valid way to verify an SSH public key is over some trusted out-of-band channel. (Or set up some kind of key-signing infrastructure.)
Here, help.github.com/articles/github-s-ssh-key-fingerprints
remains the "trusted out-of-band channel".
Based on VonC's answer, the script below can verify and add the key automatically. Use it like this:
$ ./add-key.sh github.com nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8
It tells you whether it successfully verified and saved the fingerprint.
For usage info, use ./add-key.sh --help
The script:
#!/usr/bin/env bash
# Settings
knownhosts="$HOME/.ssh/known_hosts"
if [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ] || [ ${#1} == 0 ]; then
echo "Usage: $0 <host> <fingerprint> [<port>]"
echo "Example: $0 github.com nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8"
echo "The default port is 22."
echo "The script will download the ssh keys from <host>, check if any match"
echo "the <fingerprint>, and add that one to $knownhosts."
exit 1
fi
# Argument handling
host=$1
fingerprint=$2
port=$(if [ -n "$3" ]; then echo "$3"; else echo 22; fi)
# Download the actual key (you cannot convert a fingerprint to the original key)
keys="$(ssh-keyscan -p $port $host |& grep -v ^\#)";
echo "$keys" | grep -v "^$host" # Show any errors
keys="$(echo "$keys" | grep "^$host")"; # Remove errors from the variable
if [ ${#keys} -lt 20 ]; then echo Error downloading keys; exit 2; fi
# Find which line contains the key matching this fingerprint
line=$(ssh-keygen -lf <(echo "$keys") | grep -n "$fingerprint" | cut -b 1-1)
if [ ${#line} -gt 0 ]; then # If there was a matching fingerprint (todo: shouldn't this be -ge or so?)
# Take that line
key=$(head -$line <(echo "$keys") | tail -1)
# Check if the key part (column 3) of that line is already in $knownhosts
if [ -n "$(grep "$(echo "$key" | awk '{print $3}')" $knownhosts)" ]; then
echo "Key already in $knownhosts."
exit 3
else
# Add it to known hosts
echo "$key" >> $knownhosts
# And tell the user what kind of key they just added
keytype=$(echo "$key" | awk '{print $2}')
echo Fingerprint verified and $keytype key added to $knownhosts
fi
else # If there was no matching fingerprint
echo MITM? These are the received fingerprints:
ssh-keygen -lf <(echo "$keys")
echo Generated from these received keys:
echo "$keys"
exit 1
fi
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With