1

I use a cluster of about 30 machines that have all recently been reconfigured with new OpenSSH host keys. When I try to log into one, I get this error message (many lines removed for brevity):

@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
The fingerprint for the RSA key sent by the remote host is
52:bb:71:83:7e:d0:e2:66:92:0e:10:78:cf:a6:41:49.
Add correct host key in /home/nr/.ssh/known_hosts to get rid of this message.
Offending key in /home/nr/.ssh/known_hosts:50

I can go remove the offending line manually, in which case I get a different complaint about the IP addresss, which requires removing another line manually, and I have no desire to repeat this exercise 29 times. I would like to write a program to do it. Unfortunately, the line in the .ssh file no longer contains the host name and IP address in clear text, as it did in earlier versions.

So here's my question:

  • Given a host name and an IP address, how can I write a program to find out which lines of my ~/.ssh/known_hosts store an SSH host key for that host or IP address?

If I can recover this info, I think I can do the rest myself.


Footnote: I would prefer to code in bash/ksh/sh or C or Lua; my Perl and Python are very rusty.


Clarifications:

  • I don't want to remove the whole file and repopulate it; it contains over a hundred validated keys that I prefer not to re-validate.

  • Whether I maintain a single master copy or multiple replicas, the problem of scrubbing away a large group of obsolete host keys remains.

Answer

Here's the Lua script I wrote using ssh-keygen -F:

#!/usr/bin/env lua

require 'osutil'
require 'ioutil'

local known = os.getenv 'HOME' .. '/.ssh/known_hosts'

local function lines(name)
  local lines = { }
  for l in io.lines(name) do
    table.insert(lines, l)
  end
  return lines
end

local function remove_line(host)
  local f = io.popen('ssh-keygen -F ' .. os.quote(host))
  for l in f:lines() do
    local line = l:match '^# Host %S+ found: line (%d+) type %u+$'
    if line then
      local thelines = lines(known)
      table.remove(thelines, assert(tonumber(line)))
      table.insert(thelines, '')
      io.set_contents(known, table.concat(thelines, '\n'))
      return
    end
  end
  io.stderr:write('Host ', host, ' not found in ', known, '\n')
end

for _, host in ipairs(arg) do
  local ip = os.capture('ipaddress ' .. host)
  remove_line(host)
  remove_line(ip)
end
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533

5 Answers5

5
ssh-keygen -R hostname
ssh-keygen -R ipaddress

personally I scrub the IP addresses with a loop and perl, and remove the conflicts by hand.

$!/usr/bin/perl
for (1..30){
     `ssh keygen -R 192.168.0.$_`; #note: backticks arent apostrophies
}
Matthieu
  • 2,736
  • 4
  • 57
  • 87
Storm Knight
  • 51
  • 1
  • 2
1

If I want to find out on what line the entry for a host lives,

ssh-keygen -F hostname

The same trick works with IP addresses.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
1

touch and edit "clearkey.sh" or what ever name makes you happy.

#! /bin/bash
# $1 is the first argument supplied after calling the script

sed -i "$1d" ~/.ssh/known_hosts
echo "Deleted line $1 from known_hosts file"

Should be able to do "clearkey.sh 3" and it will delete the offending line!

0

I usually do the following in bash script checkssh to automatically remove the line:

#!/bin/bash

# Path to "known_hosts" file
KH=~/.ssh/known_hosts
# Find the host in the file, showing line number
L=`grep -i -n $1 $KH`
# If line is not found, exit
[[ $? -ne 0 ]] && exit
# Isolate line number
L=`echo $L | cut -f 1 -d :`
sed -i "${L}d" $KH

You can add ssh $1 exit at the end to automatically re-create an entry in the file, if your ssh is configured to do so.

Call it like checkssh <hostname>.

Matthieu
  • 2,736
  • 4
  • 57
  • 87
0

You might like to try the following when script writing:

declare CHANGED_HOST_NAME="host.yourpublic.work";
declare CHANGED_HOST_IP=$(dig +short ${CHANGED_HOST_NAME});

# Remove old IP address if found
[ -z ${CHANGED_HOST_IP} ] || ssh-keygen -R ${CHANGED_HOST_IP};

# Remove old host key
ssh-keygen -R ${CHANGED_HOST_NAME};

# Add new host key
ssh-keyscan ${CHANGED_HOST_NAME} >> $HOME/.ssh/known_hosts;


Big thanks to @Storm Knight (@289844)

Martin Bramwell
  • 2,003
  • 2
  • 19
  • 35