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 sincessh-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}')