Photo by Magnus Engø on Unsplash
How to quickly remove multiple entries from the SSH known_hosts file?
I use the SSH protocol to log into multiple hosts several times daily. This creates a lot of entries in my ~/.ssh/known_hosts
file.
It often gets cluttered with conflicting entries, for example, when I have moved the host's FQDN to another host (delete an old server, add a new one).
When that happens, I get an error like so:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
This is an expected issue for my use case, which I will explain briefly. But mainly, I will show you how I created a utility you can use in the z-Shell fzf
to parse, select and delete entries quickly in this post.
First, a quick intro.
What is the SSH known_hosts
File?
The SSH (Secure Shell) known_hosts
file is essential to secure communication when connecting to remote servers or devices using SSH protocol. It serves as a repository of trusted host keys, allowing SSH clients to verify the authenticity of the remote server before establishing a connection. This file helps protect against man-in-the-middle attacks, where an attacker intercepts the communication between the client and server and poses as the legitimate server.
When an SSH client connects to a remote server for the first time, the server's host key fingerprint is presented to the client. The client can then choose to accept or reject the key. If accepted, the host key is stored in the known_hosts file, associating the key with the server's hostname
or IP address. On subsequent connections, the client checks the known_hosts
file to ensure the presented host key matches the one previously stored, thus providing a level of trust and security.
The known_hosts
file typically resides in the user's home directory under the .ssh
subdirectory. Each entry in the file consists of the server's hostname or IP address, followed by its corresponding host key. The host key serves as a unique identifier for the server, and any discrepancies or changes to the key can raise warnings or errors during SSH connections, indicating a potential security risk.
It is essential to manage the known_hosts
file carefully. If a server's host key changes unexpectedly, it may indicate a security breach or configuration change on the remote server. In such cases, it is advisable to investigate the reason behind the change and manually update the known_hosts file to reflect the new key.
Should you delete the entry?
Let me elaborate on my use case to help you understand why I might want to delete an entry from the `known_hosts` file.
When I see an error like so:
Depending on what I am doing, I may have deleted a server and added a new one in its place with the same FQDN example.com
to connect to it over SSH.
However, someone might be attacking me if I have not replaced a server. That's where the strict host key-checking policy comes to play.
When deleting the offending entry from the file, you should ask this: Did I change something that would require me to delete the host key?
How to delete one key by hand?
To delete only any key, you can use the ssh-keygen
a utility like so:
ssh-keygen -R someserver.example.com
OR
if it is an IP address like 123.123.123.123
you can use this:
ssh-keygen -R 123.123.123.123
You would usually pick the `Host` value from the error I mentioned previously. The -R
flag will remove the host key and display a success message like so:
❯ ssh-keygen -R 123.123.123.123
# Host 123.123.123.123 found: line 381
# Host 123.123.123.123 found: line 382
# Host 123.123.123.123 found: line 383
/Users/mrugesh/.ssh/known_hosts updated.
Original contents retained as /Users/mrugesh/.ssh/known_hosts.old
If you look closely, it will back up the known_hosts
file to known_hosts.old
file in case you want to undo the changes.
Use a ZSH widget?
Yes — of course.
Here is a ZSH utility function that can create a fancy widget to "search", "select" and "delete" multiple entries from the known_hosts
file using the same ssh-keygen
tool.
You can include the code below in your ~/.zshrc
file or somewhere convenient. I will break down the code in a moment.
function remove_known_host() {
# Extract the hostnames from the known_hosts file
local hostnames=($(awk '{print $1}' ~/.ssh/known_hosts | sed 's/,/\n/g' | sort | uniq))
# Use 'fzf' to create an interactive menu to select hostnames
local selected_hostnames=($(printf '%s\n' "${hostnames[@]}" | fzf --multi --prompt="Remove Host > " --query "$LBUFFER"))
# If any selections were made, remove them
if [[ ${#selected_hostnames[@]} -ne 0 ]]; then
for sel in "${selected_hostnames[@]}"; do
# Remove host entry using ssh-keygen
ssh-keygen -R "$sel"
done
echo "Removed selected host(s)."
else
echo "No host selected."
fi
# rm -f ~/.ssh/known_hosts.old
zle reset-prompt
}
# Create a widget
zle -N remove_known_host
# Optionaly bind a shortcut like Ctrl + H key to invoke the widget
bindkey '^H' remove_known_host
Here is what the code does:
It defines a function called
remove_known_host
.The function parses the
known_hosts
file to generate a list of hosts, sorted and parsed for uniqueness. This is because you may have multiple Host key entries for the same host (if you have multiple SSH keys, for example).Next, it uses
fzf
to create a fancy fuzzy finder widget where you can start typing part of the host you are looking for.The widget is created in the multi-select mode, so you can navigate through the list using your arrow keys, hit the
Tab
key to make selections, and repeat as you need.Once done, you can hit the
Enter
key to pass the list of entries back to the function.The code then loops over the entries you selected and uses the
ssh-keygen
tool to remove the offending host keys.Uncomment the
rm -f ~/.ssh/known_hosts.old
line if you do not want to keep the backup file.The
bindkey
directive is also optional. You can pressCtrl + H
to bring up the widget in the terminal quickly. Comment it out if you don't want the behavior. In that case, you must type in the function nameremove_known_host
to trigger the widget.
That's it!
You can now source the file or restart your terminal to get it going.
I hope you liked this quick guide. Until the next one!