Como posso escrever um programa (script) para remover as teclas de host obsoletas de ~/.ssh/conhecido_hosts?
Pergunta
Eu uso um cluster de cerca de 30 máquinas que foram reconfiguradas recentemente com novas teclas de host OpenSSH. Quando tento fazer login em um, recebo esta mensagem de erro (muitas linhas removidas para a brevidade):
@ 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
Eu posso remover a linha ofensiva manualmente, nesse caso, recebo uma reclamação diferente sobre os endereços IP, o que requer remover outro Linha manualmente, e não tenho vontade de repetir este exercício 29 vezes. Eu gostaria de escrever um programa para fazer isso. Infelizmente, a linha no arquivo .ssh não contém mais o nome do host e o endereço IP no texto claro, como fez nas versões anteriores.
Então aqui está minha pergunta:
- Dado um nome de host e um endereço IP, como posso escrever um programa para descobrir quais linhas do meu
~/.ssh/known_hosts
Armazene uma chave host SSH para esse host ou endereço IP?
Se eu puder recuperar essas informações, acho que posso fazer o resto.
Nota de rodapé: eu preferiria codificar em bash/ksh/sh ou c ou lua; Meu Perl e Python estão muito enferrujados.
Esclarecimentos:
Não quero remover o arquivo inteiro e repovoar -o; Ele contém mais de cem teclas validadas que eu prefiro não validar novamente.
Se eu mantenho uma única cópia mestre ou várias réplicas, o problema de esfregar um grande grupo de chaves obsoletas do host permanece.
Responda
Aqui está o script da Lua que escrevi usando 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
Solução 2
Se eu quiser descobrir em que linha a entrada de um host vive,
ssh-keygen -F hostname
O mesmo truque funciona com endereços IP.
Outras dicas
ssh-keygen -R hostname
ssh-keygen -R ipaddress
Pessoalmente, esfrego os endereços IP com um loop e Perl e removo os conflitos manualmente.
$!/usr/bin/perl
for (1..30){
`ssh keygen -R 192.168.0.$_`; #note: backticks arent apostrophies
}
Toque e edite "ClearKey.sh" ou o que sempre o faz feliz.
#! /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"
Deve ser capaz de fazer "ClearKey.sh 3" e ele excluirá a linha ofensiva!
Eu costumo fazer o seguinte no script bash checkssh
Para remover automaticamente a linha:
#!/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
Você pode adicionar ssh $1 exit
No final de recriar automaticamente uma entrada no arquivo, se o seu SSH estiver configurado para fazê-lo.
Chame como checkssh <hostname>
.