What is the secure/correct way of adding www.github.com to the known_hosts file?

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 what ssh-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