How to Identify and Clean Up Obsolete SSH Known Hosts Entries in Linux


3 views

Every SSH connection you make gets recorded in your ~/.ssh/known_hosts file. Over time, especially when working with temporary VMs or cloud instances, this file accumulates obsolete entries that:

  • Clutter your SSH configuration
  • Potentially cause false positive warnings
  • May expose information about your infrastructure

Each entry in known_hosts follows this pattern:

[hostname_or_ip] ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNo...==

The three components are:

  1. Host identifier (hostname, IP, or hashed value)
  2. Key type (ssh-rsa, ecdsa-sha2-nistp256, etc.)
  3. The public key itself

Here's a practical way to audit your known_hosts:

#!/bin/bash
# Check which hosts are still active
CURRENT_HOSTS=$(grep -v '^#' ~/.ssh/known_hosts | awk '{print $1}')

for host in $CURRENT_HOSTS; do
    if [[ "$host" == "|1|"* ]]; then
        echo "Hashed entry detected: $host"
    else
        nc -z ${host%:*} ${host##*:} 2>/dev/null && \
        echo "Active: $host" || \
        echo "Inactive: $host"
    fi
done

Use ssh-keygen to remove entries safely:

# Remove by hostname
ssh-keygen -R example.com

# Remove by IP
ssh-keygen -R 192.168.1.100

# Remove hashed entries (when you only know the original hostname)
ssh-keygen -H -f ~/.ssh/known_hosts -R "old-vm-01"

For more complex management, this Python script helps analyze entries:

import sys
from collections import defaultdict

def parse_known_hosts(file_path):
    entries = defaultdict(list)
    with open(file_path, 'r') as f:
        for line in f:
            if line.strip() and not line.startswith('#'):
                parts = line.strip().split()
                entries[parts[0]].append({
                    'key_type': parts[1],
                    'fingerprint': parts[2]
                })
    return entries

if __name__ == "__main__":
    entries = parse_known_hosts(sys.argv[1])
    for host, keys in entries.items():
        print(f"Host: {host}")
        for key in keys:
            print(f"  Type: {key['key_type']}")
            print(f"  Fingerprint: {key['fingerprint'][:30]}...")
  • Regularly prune entries for decommissioned servers
  • Consider using ssh-keyscan for bulk additions to new systems
  • For development environments, use StrictHostKeyChecking no temporarily
  • Back up known_hosts before major cleanups

When hosts are hashed (starting with |1|), you can:

# List all hashed entries
grep '^|1|' ~/.ssh/known_hosts

# Find a specific host's hash
ssh-keygen -H -F example.com

The ~/.ssh/known_hosts file contains SSH host keys in the following format:

[hostname_or_ip] ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ...
[hostname_or_ip] ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfAm4V...

Use this command to search for a specific host:

ssh-keygen -F example.com -f ~/.ssh/known_hosts

Output will show all matching entries:

Host example.com found: line 15
|1|dGhpc2lzYXNhbHR8ClVzZXJ8U2VydmVy|ecdsa-sha2-nistp256 AAAAE2...

If your file uses hashed hostnames (common in newer SSH versions), first view unhashed:

ssh-keygen -H -f ~/.ssh/known_hosts

To delete a specific host key:

ssh-keygen -R example.com -f ~/.ssh/known_hosts

For IP addresses:

ssh-keygen -R 192.168.1.100

Create a backup first:

cp ~/.ssh/known_hosts ~/.ssh/known_hosts.backup-$(date +%Y%m%d)

Then use this script to find inactive hosts:

#!/bin/bash
TMPFILE=$(mktemp)
CURRENT_HOSTS=$(ssh -G example.com | grep "^hostname" | awk '{print $2}')

while read -r line; do
  host=$(echo "$line" | awk '{print $1}')
  if [[ "$host" =~ ^\|1\| ]]; then
    continue # Skip hashed entries
  fi
  if ! grep -q "$host" <<< "$CURRENT_HOSTS"; then
    echo "$line" >> "$TMPFILE"
  fi
done < ~/.ssh/known_hosts

mv "$TMPFILE" ~/.ssh/known_hosts
  • Regularly audit your known_hosts file (quarterly)
  • Use SSH config files to manage connections
  • Consider using StrictHostKeyChecking=accept-new for test environments
  • For development VMs, use consistent hostnames/IPs