Is there some cli tool I can use to validate the contents of known_hosts? Maybe try to ping all the hosts in there and see if I can connect to each?
Probably using either ssh-keygen
or ssh-keyscan
?
Is there some cli tool I can use to validate the contents of known_hosts? Maybe try to ping all the hosts in there and see if I can connect to each?
Probably using either ssh-keygen
or ssh-keyscan
?
If you have list of all hosts available you can do it like this:
ssh-keyscan -t rsa,dsa -f hosts_list > ~/.ssh/known_hosts_revised
This will generate a new known_hosts_revised
which you can make a diff
with your current know_hosts
to see the differences.
If you don't need to compare it you can simply do ... > ~/.ssh/known_hosts
to overwrite it (WARNING: the original known_hosts
will be lost!)
The source of information are the OpenBSD man pages for ssh-keyscan(1).
Edit
The hosts_list
expected in for:
1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4
At least for my setup, using ssh-keyscan
is impossible due to my extensive ~/.ssh/config
file. I use lots of proxy commands, jump hosts, and alternate Hostname
declarations.
For example:
# Connect to Tor nodes
Host *.onion
ProxyCommand socat - SOCKS4A:localhost:%h:%p,socksport=9050
# Work jump box
Host bastion
Hostname bastion.work.com
# Office system, e.g. bob.office -> bastion -> bob.work.com
Host *.office
ProxyCommand ssh bastion nc -w600s $(echo "%h" |sed 's/\.office$/work.com/') %p
# Home system, e.g. adam -> home.com -> adam-laptop.local
Host adam
Hostname adam-laptop.local
ProxyJump home.com
None of the above will work.
Here's a script that should work for the rest though:
#!/usr/bin/awk -f
!/^#/ && NF > 2 {
split($1, hosts, ",")
key_type = $2
gsub(/^ssh-/, "", key_type)
gsub(/-.*/, "", key_type)
for (h in hosts) {
p = index(hosts[h], "]:") # [host]:port (supports raw IPv6 hosts)
if (!p && hosts[h] ~ /^[^:]+:[0-9]+$/) p = index(hosts[h], ":") # host:port
if (p > 0) {
port = substr(hosts[h], p + 2)
gsub("\[|\]?:" port, "", hosts[h])
} else {
port = 22
}
if (seen[key_type,port,hosts[h]]++) next # prevent duplicate lookups
if (port_list[key_type,port]) { comma = "," } else { comma = "" }
port_list[key_type,port] = port_list[key_type,port] comma hosts[h]
}
}
END {
for (tp in port_list) {
split(tp, a, SUBSEP)
system("echo ssh-keyscan -t " a[1] " -p " a[2] " " port_list[tp])
}
}
Remove the echo
parts to run once you're convinced this will do what you desire.
This parses non-commented lines and with 3+ fields (since the format is host_list key_type key_hash
). It splits the host list since it can be comma-delimited, and further parsing is needed because it can contain ports but ssh-keyscan
cannot accept hosts in the format used by known_hosts
.
There are two ways a port can be specified:
host:port
[host]:port
p
is set to the position of ]:
if present (the new style). If that string isn't present, we check for the old style and reset p
.
If p
is positive, we have a port specification. Extract the port and remove (it and the square brackets) from the host name. Otherwise, the port is 22.
Just in case there are duplicate entries, we check for them and continue if we've already seen the type,port,host combination (x++
is false (0) only when first run). Finally, we push the host to a comma-delimited list string in the port_list
array as keyed by a tuple of type and port.
After reading in the entire known_hosts file, we iterate on the type,port
tuple pairs that key the port_list
array, split them into an array named a
, and run ssh-keyscan
on them.
Run this like awk -f 'this_script.awk' ~/.ssh/known_hosts
and if you like the ssh-keyscan
commands that it spits out, remove the echo
from the system command and re-run.
Do not pipe this output into ~/.ssh/known_hosts
! You will want to manually review the results (and probably filter out the comments). Also, you can't redirect output onto one of the files used in the input.
© 2022 - 2024 — McMap. All rights reserved.