Skip to content

Instantly share code, notes, and snippets.

@robinkaty
Last active August 6, 2023 19:15
Show Gist options
  • Save robinkaty/b110e0deb81af698dcb7a39daa1244f1 to your computer and use it in GitHub Desktop.
Save robinkaty/b110e0deb81af698dcb7a39daa1244f1 to your computer and use it in GitHub Desktop.
Validate Known Hosts
#!/usr/bin/env perl
# perl script to validate entries in the known_hosts file
# by pinging or nslookup.
# tried several versions of perl modules for the ping and validate but they were
# very unreliable or requred root.
# the process uf using the shell seems to work reliably
# The output is written to ~/.ssh/new_known_hosts.
use strict;
use warnings;
use Time::HiRes qw( sleep );
#use Data::Dumper;
# signal handler for ctrl-c
$SIG{INT} = \&ctrl_c_handler;ig
sub ctrl_c_handler
{
print "\nCtrl-C received. Exiting gracefully...\n";
exit;
}
sub is_host_reachable
{
my ($host) = @_;
my $timeout = 2; # Timeout in seconds
# Run system ping command
my $result = system("ping -c 1 -W $timeout $host > /dev/null 2>&1");
return $result > 0 ? 0 : 1;
}
sub is_host_resolvable
{
my ($host) = @_;
# Run system nslookup command
my $result = system("nslookup $host > /dev/null 2>&1");
return $result > 0 ? 0 : 1;
}
sub main
{
# Signal handler for Ctrl-C
# Signal handler for Ctrl-C
$SIG{INT} = \&ctrl_c_handler;
# Path to the known_hosts file
my $known_hosts_file = "$ENV{HOME}/.ssh/known_hosts";
# Path to the new_known_hosts file
my $new_known_hosts_file = "$ENV{HOME}/.ssh/new_known_hosts";
# Read the known_hosts file into an array
open( my $fh, '<', $known_hosts_file ) or die "Error opening $known_hosts_file: $!";
my @known_hosts = <$fh>;
close($fh);
# Create an array to store valid host lines
my @valid_host_lines;
my @normalized_lines;
# Check and validate each host in the known_hosts file
foreach my $host_line (@known_hosts)
{
#sleep(0.050);
chomp $host_line; # Remove trailing newline
# Split the host entry into separate components
my ( $hosts, $key_type, $key ) = split ' ', $host_line, 3;
next unless $hosts; # Skip empty lines
# Split the hosts on commas
my @hosts = split( /,/, $hosts );
foreach my $host_entry (@hosts)
{
$host_entry =~ s/^\s+|\s+$//g; # Remove leading and trailing whitespaces
# Check if the host entry is empty or undefined
if ($host_entry)
{
push @normalized_lines, "$host_entry $key_type $key\n";
print "$host_entry $key_type $key\n";
}
}
}
my %seen;
my @sorted_normalized_lines = grep { !$seen{$_}++ } sort @normalized_lines;
#print Dumper(@sorted_normalized_lines);
foreach my $host_line (@sorted_normalized_lines)
{
sleep(0.050);
my ( $host_entry, $key_type, $key ) = split ' ', $host_line, 3;
$host_entry =~ s/^\s+|\s+$//g; # Remove leading and trailing whitespaces
# Check if the host entry is empty or undefined
if ($host_entry)
{
# Check host availability with TCP port scanning
#if ( is_host_reachable( $host_entry ) || is_host_resolvable($host_entry) )
if ( is_host_resolvable($host_entry) || is_host_reachable($host_entry) )
{
# The host is reachable, add the host entry to the array
print "Host is alive/valid : $host_entry\n";
push @valid_host_lines, "$host_entry $key_type $key";
}
else
{
print "Host is not reachable: $host_entry\n";
}
}
else
{
print "Empty host entry found, skipping...\n";
}
}
# Write the sorted and deduplicated host lines to new_known_hosts file
open( my $new_fh, '>', $new_known_hosts_file ) or die "Error opening $new_known_hosts_file: $!";
print $new_fh @valid_host_lines;
close($new_fh);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment