How to manage known_hosts file using shell script

I am using simple shell script to manage OpenSSL known_hosts file as it requires many small updates due to constantly changing host keys on a dynamic virtual servers.

Shell scipt

Store the following shell script in your PATH.

#!/bin/bash
# Add, replace or remove host keys from known_hosts file
# https://blog.sleeplessbeastie.eu/2017/11/13/how-to-manage-known_hosts-file-using-shell-script/

## parameters
# host
param_host=""

# known hosts file location
param_known_hosts="~/.ssh/known_hosts"

# action
param_action=""

# port
param_port="-P 22"

# hash a known host file
param_hash=""

## functions
known_hosts_check() {
  ssh-keygen -F $host -f $known_hosts &>/dev/null 
}

known_hosts_remove() {
  ssh-keygen -R $host -f $known_hosts &>/dev/null
}

known_hosts_add() {
  ssh-keyscan $param_hash $host 2>/dev/null | tee -a $known_hosts &>/dev/null
}

usage(){
  echo "Usage:"
  echo "  $0 -h hostname -p 22 -H -k ~/.ssh/known_hosts [-a|-r|-p]"
  echo ""
  echo "Parameters:"
  echo "  -h hostname    : set SSH hostname (required)"
  echo "  -k known_hosts : set SSH known hosts file location (default is \"~/.ssh/known_hosts\")"
  echo "  -P port        : set SSH port (default is  \"22\")"
  echo "  -H             : hash SSH known hosts file when adding an entry (default is to not hash)"
  echo
  echo "Actions:"
  echo "  -a             : add or replace host entries for the provided hostname"
  echo "  -r             : remove host entries for the provided hostname"
  echo "  -p             : print host entries related to the provided hostname"
  echo ""
}


# parse parameters
while getopts ":h:k:P:Harp" option; do
  case $option in
    "h") 
      param_host="${OPTARG}"
      ;;
    "k") 
      param_known_hosts="${OPTARG}"
      ;;
    "P") 
      param_port=""
      ;;
    "H") 
      param_hash="-H"
      ;;
    "a")
      param_action="add_or_replace"
      ;;
    "r")
      param_action="remove"
      ;;
    "p")
      param_action="print"
      ;;
    \?|:|*)
      usage
      exit
      ;;
  esac
done

# evaluate path for know hosts file
known_hosts="$(eval echo $param_known_hosts)"
if [ ! -f "$known_hosts" ]; then
  echo "Known hosts file (${known_hosts}) does not exists"
  exit 10
fi

# hostname and action is required 
if [ -n "$param_host" ] && [ -n "$param_action" ]; then
  # get addional IP address for given hostname
  additional_addresses=$(dig +short $param_host)
  possible_entries="$param_host $additional_addresses"

  # action: print
  if [ "$param_action" = "print" ]; then
    echo "Possibe entries to search for:"
    for host in $possible_entries; do
      echo " * $host"
    done
    echo
  
    found_in_known_hosts=0
    echo "Entries located in $known_hosts file"
    for host in $possible_entries; do
      known_hosts_check
      if [ "$?" -eq "0" ]; then
        echo " * $host"; 
        found_in_known_hosts=1
      fi
    done
    if [ "$found_in_known_hosts" -eq "0" ]; then echo " * none"; fi
  fi

  # action: remove
  if [ "$param_action" = "remove" ]; then
    counter=0
    for host in $possible_entries; do
      known_hosts_check
      if [ "$?" -eq "0" ]; then
        known_hosts_remove
        counter=$(expr $counter \+ 1)
      fi
    done
    echo "Entries removed: $counter" 
  fi


  counter=0
  if [ "$param_action" = "add_or_replace" ]; then
    for host in $possible_entries; do
      known_hosts_check
      if [ "$?" -eq "0" ]; then
        known_hosts_remove
        known_hosts_add
      else
        known_hosts_add
      fi
      counter=$(expr $counter \+ 1)
    done
    echo "Entries processed: $counter" 
  fi
else
  usage
fi

Usage

Display usage information.

$ sshc.sh
Usage:
  sshc.sh -h hostname -p 22 -H -k ~/.ssh/known_hosts [-a|-r|-p]

Parameters:
  -h hostname    : set SSH hostname (required)
  -k known_hosts : set SSH known hosts file location (default is "~/.ssh/known_hosts")
  -P port        : set SSH port (default is  "22")
  -H             : hash SSH known hosts file when adding an entry (default is to not hash)

Actions:
  -a             : add or replace host entries for the provided hostname
  -r             : remove host entries for the provided hostname
  -p             : print host entries related to the provided hostname

Display host entries related to the provided hostname.

$ bash sshc.sh -h sleeplessbeastie.eu -p
Possibe entries to search for:
 * sleeplessbeastie.eu
 * 84.16.240.28

Entries located in /Users/mgalazka/.ssh/known_hosts file
 * sleeplessbeastie.eu
 * 84.16.240.28

Remove host entries for the provided hostname.

$ bash sshc.sh -h sleeplessbeastie.eu -r
Entries removed: 2

Add host entries for the provided hostname.

$ bash sshc.sh -h sleeplessbeastie.eu -a
Entries processed: 2

Additional notes

This shell script does not support IPv6 addresses as I am limited to IPv4, but you can use the following command to quickly implement this feature.

$ dig +short sleeplessbeastie.eu A sleeplessbeastie.eu AAAA
84.16.240.28
2a00:c98:2060:a000:1:0:ca7:a
Milosz Galazka's Picture

About Milosz Galazka

Milosz is a Linux Foundation Certified Engineer working for a successful Polish company as a system administrator and a long time supporter of Free Software Foundation and Debian operating system. He is also open for new opportunities and challenges.

Gdansk, Poland https://sleeplessbeastie.eu