Fastest way to reset a host key when rebuilding servers on the same IP or hostname frequently

I build and rebuild servers quite often, and when I want to jump into the server to check a config setting (when I'm not using Ansible, that is...), I need to log in via SSH. It's best practice to let SSH verify the host key every time you connect to make sure you're not getting MITMed or anything else is going on.

However, any time you rebuild a server from a new image/OS install, the host key should be new, and this will result in the following message the next time you try to log in:

 10:21 AM:~ $ ssh [email protected]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:9Xayf4TxnEwUCDrkJm2h9+upZV+hbx4p4Bi7gSB3tZw.
Please contact your system administrator.
Add correct host key in /Users/jeff.geerling/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/jeff.geerling/.ssh/known_hosts:188
ECDSA host key for 10.0.1.18 has changed and you have requested strict checking.
Host key verification failed.

One fix for this, which is especially useful for local development environments or things like Raspberry Pis that you re-image frequently, is to add a configuration like the following for all hosts which you don't care about the SSH host key verification to your .ssh/config file:

Host 10.0.1.60 10.0.1.61 10.0.1.62 10.0.1.63 10.0.1.64
        StrictHostKeyChecking no
        UserKnownHostsFile=/dev/null
        LogLevel=error

(The above configuration is what I use to prevent the warning when working on my Raspberry Pi Dramble infrastructure.)

However, if you have live servers in the wild that you do want to verify on every connection that change often, you can use one of two commands to quickly remove the offending line from your known_hosts file:

# Best for removing one line, by line number (e.g. 188), from the known_hosts file:
 10:21 AM:~ $ sed -i '' '188d' ~/.ssh/known_hosts

# Best for removing host, by hostname/ip, from the known_hosts file:
 10:21 AM:~ $ ssh-keygen -R hostname

(Where 188 is the line to be deleted, or hostname is the hostname or IP address). For non-OS X bash, remove the blank ''.

You can even add a function to your profile (e.g. ~/.bash_profile) so you can just enter something like knownrm 188 to delete that line number from your known_hosts file by line number:

# Delete a given line number in the known_hosts file.
knownrm() {
  re='^[0-9]+$'
  if ! [[ $1 =~ $re ]] ; then
    echo "error: line number missing" >&2;
  else
    sed -i '' "$1d" ~/.ssh/known_hosts
  fi
}

I find it's easier/faster to type knownrm [line-number] than ssh-keygen -R hostname.

Comments

You could also use this: ssh-keygen -R hostname

It will remove all keys belonging to hostname from known_hosts.

TIL! Thanks for posting this; from the docs:

-R  Removes all keys belonging to hostname from a known_hosts file. This option is useful to delete hashed hosts (see the -H option above).

Using a bash function like knownrm in the post above can be a little more efficient if you're like me and you would always forget to explicitly reset the key for the given host, but it's nice to have plenty of options for doing the same thing, especially since ssh-keygen is available all over the place and my bash function might only be available locally.

Instead of just deleting the old, you could also add the new host-key for IP, hostname and FQDN

# delete IP:
ssh-keygen -R $1
# delete FQDN
ssh-keygen -R $(host $1 | awk '{print $NF}')
# delete hostname
ssh-keygen -R $(host $1 | awk '{print $NF}'| sed 's#\..*##')
# add IP
ssh-keyscan -H  $1 >> ~/.ssh/known_hosts
  # add hostname
ssh-keyscan -H  $(host $1 | awk '{print $NF}'| sed 's#\..*##') >> ~/.ssh/known_hosts
# add FDQN
ssh-keyscan -H  $(host $1 | awk '{print $NF}')