如何编写程序(脚本)来从 ~/.ssh/known_hosts 中删除过时的主机密钥?

发布于 2024-08-14 23:05:56 字数 1862 浏览 4 评论 0原文

我使用大约 30 台机器组成的集群,这些机器最近都使用新的 OpenSSH 主机密钥进行了重新配置。当我尝试登录其中一个时,我收到此错误消息(为简洁起见,删除了许多行):

@    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

我可以手动删除有问题的行,在这种情况下,我会收到有关 IP 地址的不同投诉,这需要删除另一个 手动进行,我不想重复这个练习 29 次。我想写一个程序来做到这一点。不幸的是,.ssh 文件中的行不再像早期版本那样包含明文形式的主机名和 IP 地址。

所以这是我的问题:

  • 给定主机名和 IP 地址,我如何编写一个程序来找出我的 ~/.ssh/known_hosts 的哪些行存储了 SSH 主机密钥主机或 IP 地址?

如果我可以恢复此信息,我想我可以自己完成剩下的工作。


脚注:我更喜欢用 bash/ksh/sh 或 C 或 Lua 编写代码;我的 Perl 和 Python 很生疏。


澄清:

  • 我不想删除整个文件并重新填充它;它包含一百多个经过验证的密钥,我不想重新验证这些密钥。

  • 无论我维护单个主副本还是多个副本,清除大量过时主机密钥的问题仍然存在。

    无论我维护单个主副本还是多个副本

是我使用 ssh-keygen -F 编写的 Lua 脚本:

#!/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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

亢潮 2024-08-21 23:05:56
ssh-keygen -R hostname
ssh-keygen -R ipaddress

我个人使用循环和 Perl 清理 IP 地址,并手动消除冲突。

$!/usr/bin/perl
for (1..30){
     `ssh keygen -R 192.168.0.$_`; #note: backticks arent apostrophies
}
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
}
望喜 2024-08-21 23:05:56

如果我想找出主机的条目位于哪一行,

ssh-keygen -F hostname

同样的技巧也适用于 IP 地址。

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.

软糯酥胸 2024-08-21 23:05:56

触摸并编辑“clearkey.sh”或任何让您满意的名称。

#! /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"

应该能够执行“clearkey.sh 3”,它将删除有问题的行!

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!

孤独岁月 2024-08-21 23:05:56

我通常在 bash 脚本 checkssh 中执行以下操作来自动删除该行:

#!/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

您可以在末尾添加 ssh $1 exit 来自动在文件中重新创建一个条目,如果您的 ssh 配置为这样做。

checkssh 这样称呼它。

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>.

养猫人 2024-08-21 23:05:56

在编写脚本时,您可能想尝试以下操作:

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;


非常感谢@Storm Knight (@289844)

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文