views:

215

answers:

2

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
A: 

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
A: 

ok 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 }

cheers, Storm

Storm Knight