@ -50,6 +50,7 @@ use JSON;
use HTML::Strip;
use HTML::Strip;
use HTML::FromText;
use HTML::FromText;
use Data::Dumper;
use Data::Dumper;
use Text::Diff;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
@ -73,6 +74,9 @@ $anvil->Log->secure({set => 1});
$anvil->Database->connect({debug => 3, check_if_configured => 1});
$anvil->Database->connect({debug => 3, check_if_configured => 1});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"});
check_ssh_keys($anvil);
die;
# If I have no databases, sleep for a second and then exit (systemd will restart us).
# If I have no databases, sleep for a second and then exit (systemd will restart us).
if (not $anvil->data->{sys}{database}{connections})
if (not $anvil->data->{sys}{database}{connections})
{
{
@ -174,7 +178,7 @@ while(1)
# Exit if 'run-once' selected.
# Exit if 'run-once' selected.
if ($anvil->data->{switches}{'run-once'})
if ($anvil->data->{switches}{'run-once'})
{
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "warn ", key => "message_0055"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert ", key => "message_0055"});
$anvil->nice_exit({code => 0});
$anvil->nice_exit({code => 0});
}
}
@ -219,7 +223,7 @@ sub handle_periodic_tasks
if ((not $anvil->data->{timing}{jobs_running}) && ($anvil->Storage->check_md5sums))
if ((not $anvil->data->{timing}{jobs_running}) && ($anvil->Storage->check_md5sums))
{
{
# NOTE: We exit with '0' to prevent systemctl from showing a scary red message.
# NOTE: We exit with '0' to prevent systemctl from showing a scary red message.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "warn ", key => "message_0014"});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert ", key => "message_0014"});
$anvil->nice_exit({code => 0});
$anvil->nice_exit({code => 0});
}
}
@ -253,7 +257,7 @@ sub handle_periodic_tasks
if ($failed)
if ($failed)
{
{
# Something went wrong.
# Something went wrong.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "warn ", key => "log_0254", variables => {
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert ", key => "log_0254", variables => {
directory => $directory,
directory => $directory,
}});
}});
}
}
@ -311,12 +315,14 @@ sub check_ssh_keys
# Get a list of all hosts and IPs we know about. We'll use this to update
# Get a list of all hosts and IPs we know about. We'll use this to update
# Get a list of machine host keys and user public keys from other machines.
get_other_keys($anvil);
# Users to check:
# Users to check:
# root, admin, hacluster
# root, admin, hacluster
foreach my $user ("root", "admin")
foreach my $user ("root", "admin")
{
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3 , list => { user => $user }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2 , list => { user => $user }});
my $user_home = $anvil->Get->users_home({user => $user});
my $user_home = $anvil->Get->users_home({user => $user});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { user_home => $user_home }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { user_home => $user_home }});
@ -331,7 +337,7 @@ sub check_ssh_keys
if (not -e $ssh_directory)
if (not -e $ssh_directory)
{
{
# Create it.
# Create it.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2 , key => "log_0272", variables => { user => $user, directory => $ssh_directory }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1 , key => "log_0272", variables => { user => $user, directory => $ssh_directory }});
$anvil->Storage->make_directory({
$anvil->Storage->make_directory({
directory => $ssh_directory,
directory => $ssh_directory,
user => $user,
user => $user,
@ -347,20 +353,20 @@ sub check_ssh_keys
my $ssh_private_key_file = $user_home."/.ssh/id_rsa";
my $ssh_private_key_file = $user_home."/.ssh/id_rsa";
my $ssh_public_key_file = $user_home."/.ssh/id_rsa.pub";
my $ssh_public_key_file = $user_home."/.ssh/id_rsa.pub";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2 , list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3 , list => {
ssh_public_key_file => $ssh_public_key_file,
ssh_public_key_file => $ssh_public_key_file,
ssh_private_key_file => $ssh_private_key_file,
ssh_private_key_file => $ssh_private_key_file,
}});
}});
if (not -e $ssh_public_key_file)
if (not -e $ssh_public_key_file)
{
{
# Generate the SSH keys.
# Generate the SSH keys.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2 , key => "log_0270", variables => { user => $user }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1 , key => "log_0270", variables => { user => $user }});
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'ssh-keygen'}." -t rsa -N \"\" -b 8191 -f ".$ssh_private_key_file});
my $output = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'ssh-keygen'}." -t rsa -N \"\" -b 8191 -f ".$ssh_private_key_file});
if (-e $ssh_public_key_file)
if (-e $ssh_public_key_file)
{
{
# Success!
# Success!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2 , key => "log_0271", variables => { user => $user, output => $output }});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1 , key => "log_0271", variables => { user => $user, output => $output }});
}
}
else
else
{
{
@ -369,11 +375,305 @@ sub check_ssh_keys
next;
next;
}
}
}
}
# Now read in the key.
# Now read in the key.
my $users_public_key = $anvil->Storage->read_file({file => $ssh_public_key_file});
my $users_public_key = $anvil->Storage->read_file({file => $ssh_public_key_file});
$users_public_key =~ s/\n$//;
$users_public_key =~ s/\n$//;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { users_public_key => $users_public_key }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { users_public_key => $users_public_key }});
# Now store the key in the 'host_key' table, if needed.
my $host_key_uuid = $anvil->Database->insert_or_update_host_keys({
debug => 3,
host_key_host_uuid => $anvil->Get->host_uuid,
host_key_public_key => $users_public_key,
host_key_user_name => $user,
});
# Read in the existing 'known_hosts' file, if it exists. The 'old' and 'new' variables will
# be used when looking for needed changes.
my $known_hosts_file_body = "";
my $known_hosts_old_body = "";
my $known_hosts_new_body = "";
my $known_hosts_file = $ssh_directory."/known_hosts";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { known_hosts_file => $known_hosts_file }});
if (-e $known_hosts_file)
{
$known_hosts_file_body = $anvil->Storage->read_file({file => $known_hosts_file});
$known_hosts_old_body = $known_hosts_file_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { known_hosts_file_body => $known_hosts_file_body }});
}
# Read in the existing 'authorized_keys' file, if it exists.
my $authorized_keys_file_body = "";
my $authorized_keys_old_body = "";
my $authorized_keys_new_body = "";
my $authorized_keys_file = $ssh_directory."/authorized_keys";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { authorized_keys_file => $authorized_keys_file }});
if (-e $authorized_keys_file)
{
$authorized_keys_file_body = $anvil->Storage->read_file({file => $authorized_keys_file});
$authorized_keys_old_body = $authorized_keys_file_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { authorized_keys_file_body => $authorized_keys_file_body }});
}
### Walk through each host we now know of. As we we do, loop through the old file body to see
### if it exists. If it does, and the key has changed, update the line with the new key. If
### it isn't found, add it. Once we check the old body for this entry, change the "old" body
### to the new one, then repeat the process.
# Look at all the hosts I know about (other than myself) and see if any of the machine or
# user keys either don't exist or have changed.
my $update_known_hosts = 0;
my $update_authorized_keys = 0;
foreach my $host_uuid (keys %{$anvil->data->{peers}{ssh_keys}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{peers}{ssh_keys}{$host_uuid}{host}})
{
my $key = $anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
's1:host_name' => $host_name,
's2:key' => $key,
}});
# Is this in the file and, if so, has it changed?
my $found = 0;
my $test_line = $host_name." ".$key;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { test_line => $test_line }});
foreach my $line (split/\n/, $known_hosts_old_body)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line eq $test_line)
{
# No change needed, key is the same.
$found = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
last;
}
elsif ($line =~ /^$host_name /)
{
# Key has changed, update.
$found = 1;
$line = $test_line;
$update_known_hosts = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
found => $found,
line => $line,
update_known_hosts => $update_known_hosts,
}});
}
$known_hosts_new_body .= $line."\n";
}
# If we didn't find the key, add it.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { found => $found }});
if (not $found)
{
$update_known_hosts = 1;
$known_hosts_new_body .= $test_line."\n";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:update_known_hosts' => $update_known_hosts,
's2:known_hosts_new_body' => $known_hosts_new_body,
}});
}
# Move the new body over to the old body (even though it may not have
# changed) and then clear the new body to prepare for the next pass.
$known_hosts_old_body = $known_hosts_new_body;
$known_hosts_new_body = "";
}
}
# Lastly, copy the last version of the old body to the new body,
$known_hosts_new_body = $known_hosts_old_body;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:update_known_hosts' => $update_known_hosts,
's2:known_hosts_file_body' => $known_hosts_file_body,
's3:known_hosts_new_body' => $known_hosts_new_body,
's4:difference' => diff \$known_hosts_file_body, \$known_hosts_new_body, { STYLE => 'Unified' },
}});
die;
# Update files, if needed.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_known_hosts => $update_known_hosts }});
if ($update_known_hosts)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0273", variables => { user => $user, file => $known_hosts_file }});
if (-e $known_hosts_file)
{
my $backup_file = $anvil->Storage->backup({
debug => 2,
fatal => 1,
file => $known_hosts_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { backup_file => $backup_file }});
if (-e $backup_file)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0154", variables => { source_file => $known_hosts_file, target_file => $backup_file }});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "error_0058", variables => { file => $known_hosts_file }});
}
}
my $failed = $anvil->Storage->write_file({
debug => 3,
overwrite => 1,
file => $known_hosts_file,
body => $known_hosts_new_body,
user => $user,
group => $user,
mode => "0644",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update_authorized_keys => $update_authorized_keys }});
if ($update_authorized_keys)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0273", variables => { user => $user, file => $authorized_keys_file }});
if (-e $authorized_keys_file)
{
my $backup_file = $anvil->Storage->backup({
debug => 2,
fatal => 1,
file => $authorized_keys_file,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { backup_file => $backup_file }});
if (-e $backup_file)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0154", variables => { source_file => $authorized_keys_file, target_file => $backup_file }});
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "error_0058", variables => { file => $authorized_keys_file }});
}
}
my $failed = $anvil->Storage->write_file({
debug => 3,
overwrite => 1,
file => $authorized_keys_file,
body => $authorized_keys_new_body,
user => $user,
group => $user,
mode => "0644",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }});
}
}
return(0);
}
# Get a list of machine host keys and user public keys from other machines.
sub get_other_keys
{
my ($anvil) = @_;
delete $anvil->data->{peers}{ssh_keys};
# Get the machine keys for other hosts.
my $query = "
SELECT
host_uuid,
host_name,
host_key
FROM
hosts
WHERE
host_uuid != ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->Get->host_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $host_uuid = $row->[0];
my $host_name = $row->[1];
my $host_key = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
host_uuid => $host_uuid,
host_name => $host_name,
host_key => $host_key,
}});
$anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$host_name} = $host_key;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"peers::ssh_keys::${host_uuid}::host::${host_name}" => $anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$host_name},
}});
# If the host name is the long host name, create another entry with the short name.
if ($host_name =~ /^(.*?)\./)
{
my $short_hostname = $1;
$anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$short_hostname} = $host_key;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"peers::ssh_keys::${host_uuid}::host::${short_hostname}" => $anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$short_hostname},
}});
}
# Find any IP addresses for this host.
my $query = "SELECT ip_address_address FROM ip_addresses WHERE ip_address_host_uuid = ".$anvil->data->{sys}{database}{use_handle}->quote($host_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $ip_address_address = $row->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
ip_address_address => $ip_address_address,
}});
$anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$ip_address_address} = $host_key;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"peers::ssh_keys::${host_uuid}::host::${ip_address_address}" => $anvil->data->{peers}{ssh_keys}{$host_uuid}{host}{$ip_address_address},
}});
}
}
# Now read in the public key for other users on other machines.
$query = "
SELECT
host_key_host_uuid,
host_key_user_name,
host_key_public_key
FROM
host_keys
WHERE
host_key_host_uuid != ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->Get->host_uuid)."
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
$results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
$count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $host_key_host_uuid = $row->[0];
my $host_key_user_name = $row->[1];
my $host_key_public_key = $row->[2];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
host_key_host_uuid => $host_key_host_uuid,
host_key_user_name => $host_key_user_name,
host_key_public_key => $host_key_public_key,
}});
$anvil->data->{peers}{ssh_keys}{$host_key_host_uuid}{user}{$host_key_user_name} = $host_key_public_key;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"peers::ssh_keys::${host_key_host_uuid}::user::${host_key_user_name}" => $anvil->data->{peers}{ssh_keys}{$host_key_host_uuid}{user}{$host_key_user_name},
}});
}
}
return(0);
return(0);
@ -489,7 +789,7 @@ FROM
WHERE
WHERE
variable_source_table = 'hosts'
variable_source_table = 'hosts'
AND
AND
variable_source_uuid = ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->data->{sys}{host_uuid} )."
variable_source_uuid = ".$anvil->data->{sys}{database}{use_handle}->quote($anvil->Get->host_uuid )."
AND
AND
variable_name = 'reboot::needed'
variable_name = 'reboot::needed'
;";
;";