* Fixed a couple bugs to get System->change_shell_user_password() working.

* Made logging between journald and a traditional file configurable via 'sys::log_file'. Also made the file handle unbuffered when logging to a file.
* Fixed a bug with loading the anvil.conf config file in a few locations.
* Created System->stty_echo() to handle enabling/disabling shell echo, and added restoring the echo to Tools->catch_sig.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 7 years ago
parent b42d4a6fea
commit eafd4fd3f7
  1. 10
      Anvil/Tools.pm
  2. 39
      Anvil/Tools/Log.pm
  3. 2
      Anvil/Tools/Storage.pm
  4. 47
      Anvil/Tools/System.pm
  5. 6
      ocf/alteeve/server
  6. 2
      share/words.xml
  7. 52
      tools/anvil-change-password
  8. 2
      tools/anvil-configure-network
  9. 3
      tools/anvil-prep-database

@ -697,6 +697,8 @@ sub _set_defaults
user => "admin", user => "admin",
}, },
host_type => "", host_type => "",
log_file => "/var/log/anvil.log",
stty => "",
use_base2 => 1, use_base2 => 1,
}; };
$anvil->data->{defaults} = { $anvil->data->{defaults} = {
@ -798,6 +800,7 @@ sub _set_paths
md5sum => "/usr/bin/md5sum", md5sum => "/usr/bin/md5sum",
'mkdir' => "/usr/bin/mkdir", 'mkdir' => "/usr/bin/mkdir",
nmcli => "/bin/nmcli", nmcli => "/bin/nmcli",
openssl => "/usr/bin/openssl",
passwd => "/usr/bin/passwd", passwd => "/usr/bin/passwd",
ping => "/usr/bin/ping", ping => "/usr/bin/ping",
pgrep => "/usr/bin/pgrep", pgrep => "/usr/bin/pgrep",
@ -919,6 +922,13 @@ sub catch_sig
if ($signal) if ($signal)
{ {
print "Process with PID: [$$] exiting on SIG".$signal.".\n"; print "Process with PID: [$$] exiting on SIG".$signal.".\n";
if ($anvil->data->{sys}{stty})
{
# Restore the terminal.
print "Restoring the terminal\n";
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{stty}});
}
} }
$anvil->nice_exit({code => 255}); $anvil->nice_exit({code => 255});
} }

@ -301,19 +301,8 @@ sub entry
$string .= $message; $string .= $message;
} }
# Log with Log::Journald # If the user set a log file, log to that. Otherwise, log via Log::Journald.
if (0) if ($anvil->data->{sys}{log_file})
{
Log::Journald::send(
PRIORITY => $priority,
MESSAGE => $string,
CODE_FILE => $source,
CODE_LINE => $line,
SYSLOG_FACILITY => $secure ? "authpriv" : $facility,
SYSLOG_IDENTIFIER => $tag,
);
}
else
{ {
# TODO: Switch back to journald later, using a file for testing for now # TODO: Switch back to journald later, using a file for testing for now
if ($string !~ /\n$/) if ($string !~ /\n$/)
@ -324,10 +313,18 @@ sub entry
# Open the file? # Open the file?
if (not $anvil->{HANDLE}{log_file}) if (not $anvil->{HANDLE}{log_file})
{ {
my $shell_call = "/var/log/anvil.log"; # If the file doesn't start with a '/', we'll put it under /var/log.
my $log_file = $anvil->data->{sys}{log_file} =~ /^\// ? $anvil->data->{sys}{log_file} : "/var/log/".$anvil->data->{sys}{log_file};
my ($directory, $file) = ($log_file =~ /^(\/.*)\/(.*)$/);
# Make sure the log directory exists.
$anvil->Storage->make_directory({directory => $directory, group => 755});
# Now open the log
my $shell_call = $log_file;
# NOTE: Don't call '$anvil->Log->entry()' here, it will cause a loop! # NOTE: Don't call '$anvil->Log->entry()' here, it will cause a loop!
open (my $file_handle, ">>", $shell_call) or die "Failed to open: [$shell_call] for writing. The error was: $!\n"; open (my $file_handle, ">>", $shell_call) or die "Failed to open: [$shell_call] for writing. The error was: $!\n";
$file_handle->autoflush(1);
$anvil->{HANDLE}{log_file} = $file_handle; $anvil->{HANDLE}{log_file} = $file_handle;
} }
@ -340,6 +337,17 @@ sub entry
# The handle has to be wrapped in a block to make 'print' happy as it doesn't like non-scalars for file handles # The handle has to be wrapped in a block to make 'print' happy as it doesn't like non-scalars for file handles
print { $anvil->{HANDLE}{log_file} } $string; print { $anvil->{HANDLE}{log_file} } $string;
} }
else
{
Log::Journald::send(
PRIORITY => $priority,
MESSAGE => $string,
CODE_FILE => $source,
CODE_LINE => $line,
SYSLOG_FACILITY => $secure ? "authpriv" : $facility,
SYSLOG_IDENTIFIER => $tag,
);
}
return(0); return(0);
} }
@ -560,6 +568,7 @@ sub variables
{ {
die $THIS_FILE." ".__LINE__."; Log->variables() called without Log->level: [".$anvil->Log->level."] defined from: [$source : $line]\n"; die $THIS_FILE." ".__LINE__."; Log->variables() called without Log->level: [".$anvil->Log->level."] defined from: [$source : $line]\n";
} }
#print "level: [$level], logging: [".$anvil->Log->level."], secure: [$secure], logging secure: [".$anvil->Log->secure."]\n";
if ($level > $anvil->Log->level) if ($level > $anvil->Log->level)
{ {
return(1); return(1);

@ -745,7 +745,7 @@ sub read_config
if (not $file) if (not $file)
{ {
# No file to read # No file to read
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "log_0032"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "log_0164"});
$return_code = 1; $return_code = 1;
} }

@ -29,6 +29,7 @@ my $THIS_FILE = "System.pm";
# reload_daemon # reload_daemon
# start_daemon # start_daemon
# stop_daemon # stop_daemon
# stty_echo
# _load_firewalld_zones # _load_firewalld_zones
# _load_specific_firewalld_zone # _load_specific_firewalld_zone
# _match_port_to_service # _match_port_to_service
@ -182,7 +183,6 @@ sub call
} }
} }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { output => $output }});
return($output); return($output);
} }
@ -264,8 +264,8 @@ sub change_shell_user_password
} }
# Generate a salt and then use it to create a hash. # Generate a salt and then use it to create a hash.
my $salt = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{openssl}." rand 1000 | ".$anvil->data->{path}{exe}{strings}." | ".$anvil->data->{path}{exe}{'grep'}." -io [0-9A-Za-z\.\/] | ".$anvil->data->{path}{exe}{head}." -n 16 | ".$anvil->data->{path}{exe}{'tr'}." -d '\n'" }); my $salt = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{openssl}." rand 1000 | ".$anvil->data->{path}{exe}{strings}." | ".$anvil->data->{path}{exe}{'grep'}." -io [0-9A-Za-z\.\/] | ".$anvil->data->{path}{exe}{head}." -n 16 | ".$anvil->data->{path}{exe}{'tr'}." -d '\n'" });
my $new_hash = $user.":".crypt($new_password,"\$6\$".$salt."\$"); my $new_hash = crypt($new_password,"\$6\$".$salt."\$");
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
salt => $salt, salt => $salt,
new_hash => $new_hash, new_hash => $new_hash,
@ -273,11 +273,12 @@ sub change_shell_user_password
# Update the password using 'usermod'. NOTE: The single-quotes are crtical! # Update the password using 'usermod'. NOTE: The single-quotes are crtical!
my $output = ""; my $output = "";
my $shell_call = $anvil->data->{path}{exe}{usermod}." --password '".$new_hash."'; ".$anvil->data->{path}{exe}{'echo'}." return_code:\$?"; my $shell_call = $anvil->data->{path}{exe}{usermod}." --password '".$new_hash."' ".$user."; ".$anvil->data->{path}{exe}{'echo'}." return_code:\$?";
if ($target) if ($target)
{ {
# Remote call. # Remote call.
$output = $anvil->Remote->call({ $output = $anvil->Remote->call({
debug => $debug,
shell_call => $shell_call, shell_call => $shell_call,
target => $target, target => $target,
port => $port, port => $port,
@ -288,7 +289,7 @@ sub change_shell_user_password
else else
{ {
# Local call # Local call
$output = $anvil->System->call({shell_call => $shell_call}); $output = $anvil->System->call({debug => $debug, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
} }
foreach my $line (split/\n/, $output) foreach my $line (split/\n/, $output)
@ -1481,6 +1482,42 @@ sub stop_daemon
return($return); return($return);
} }
=head2 stty_echo
This turns echo off (for password prompts, for example) and back on again. It does so in a way that a SIGINT/SIGKILL can restore the echo before the program dies.
B<< Note >>: Calling C<< on >> before C<< off >> will result in no change. The C<< off >> stores the current TTY in C<< sys::stty >> and uses the value in there to reset the terminal. If you want to change the terminal, you can set that variable manually then call C<< on >>, though this is not recommended.
Parameters;
=head3 set (required, default 'on')
This is set to C<< on >> or C<< off >>, which enables or disables echo'ing respectively.
=cut
sub stty_echo
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $set = defined $parameter->{set} ? $parameter->{set} : "";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0018", variables => { set => $set }});
if ($set eq "off")
{
$anvil->data->{sys}{stty} = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." --save"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 'sys::stty' => $anvil->data->{sys}{stty} }});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." -echo"});
}
elsif (($set eq "on") && ($anvil->data->{sys}{stty}))
{
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{stty}});
}
return(0);
}
# =head3 # =head3
# #

@ -97,7 +97,7 @@ my $conf = {
}, },
# If a program isn't at the defined path, $ENV{PATH} will be searched. # If a program isn't at the defined path, $ENV{PATH} will be searched.
path => { path => {
config => { configs => {
definition => "/mnt/anvil/definitions/#!NAME!#.xml", definition => "/mnt/anvil/definitions/#!NAME!#.xml",
}, },
exe => { exe => {
@ -351,7 +351,7 @@ sub start_server
# If we're still alive, we're ready to boot. # If we're still alive, we're ready to boot.
to_log($conf, {message => "Sanity checks passed, ready to start: [$server].", 'line' => __LINE__, level => 2}); to_log($conf, {message => "Sanity checks passed, ready to start: [$server].", 'line' => __LINE__, level => 2});
my $definition_file = $conf->{path}{config}{definition}; my $definition_file = $conf->{path}{configs}{definition};
$definition_file =~ s/#!NAME!#/$server/; $definition_file =~ s/#!NAME!#/$server/;
to_log($conf, {message => "definition_file: [$definition_file].", 'line' => __LINE__, level => 2}); to_log($conf, {message => "definition_file: [$definition_file].", 'line' => __LINE__, level => 2});
@ -1545,7 +1545,7 @@ sub read_server_definition
my ($conf) = @_; my ($conf) = @_;
my $server = $conf->{environment}{OCF_RESKEY_name}; my $server = $conf->{environment}{OCF_RESKEY_name};
my $definition_file = $conf->{path}{config}{definition}; my $definition_file = $conf->{path}{configs}{definition};
$definition_file =~ s/#!NAME!#/$server/; $definition_file =~ s/#!NAME!#/$server/;
my $server_xml = ""; my $server_xml = "";
to_log($conf, {message => "server: [$server], definition_file: [$definition_file]", 'line' => __LINE__, level => 3}); to_log($conf, {message => "server: [$server], definition_file: [$definition_file]", 'line' => __LINE__, level => 3});

@ -44,6 +44,7 @@ Author: Madison Kelly <mkelly@alteeve.ca>
<key name="message_0020">About to update the local passwords (shell users, database and web interface).</key> <key name="message_0020">About to update the local passwords (shell users, database and web interface).</key>
<key name="message_0021">Proceed? [y/N]</key> <key name="message_0021">Proceed? [y/N]</key>
<key name="message_0022">Aborting.</key> <key name="message_0022">Aborting.</key>
<key name="message_0023">Auto-approved by command line switch, proceeding.</key>
<!-- Log entries --> <!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key> <key name="log_0001">Starting: [#!variable!program!#].</key>
@ -242,6 +243,7 @@ The database connection error was:
<key name="log_0161"><![CDATA[[ Error ] - The method Storage->read_file() was asked to read the remote file: [#!variable!file!#] but it appears to be missing the file name. Aborting.]]></key> <key name="log_0161"><![CDATA[[ Error ] - The method Storage->read_file() was asked to read the remote file: [#!variable!file!#] but it appears to be missing the file name. Aborting.]]></key>
<key name="log_0162"><![CDATA[[ Error ] - The method Storage->read_file() tried to rsync the remote file: [#!variable!remote_file!#] to the local temporary file: [#!variable!local_file!#], but it did not arrive. There might be more information above.]]></key> <key name="log_0162"><![CDATA[[ Error ] - The method Storage->read_file() tried to rsync the remote file: [#!variable!remote_file!#] to the local temporary file: [#!variable!local_file!#], but it did not arrive. There might be more information above.]]></key>
<key name="log_0163">The file: [#!variable!file!#] does not exist.</key> <key name="log_0163">The file: [#!variable!file!#] does not exist.</key>
<key name="log_0164"><![CDATA[[ Warning ] - Storage->read_config()' was called without a file name to read.]]></key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>

@ -31,13 +31,13 @@ $( = $);
my $anvil = Anvil::Tools->new(); my $anvil = Anvil::Tools->new();
$anvil->Log->level({set => 2}); $anvil->Log->level({set => 2});
$anvil->Log->secure({set => 0}); $anvil->Log->secure({set => 1});
# Read switches # Read switches
$anvil->Get->switches; $anvil->Get->switches;
# Paths # Paths
$anvil->Storage->read_config({file => $anvil->data->{path}{config}{'anvil.conf'}}); $anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
# Make sure we're running as 'root' # Make sure we're running as 'root'
# $< == real UID, $> == effective UID # $< == real UID, $> == effective UID
@ -62,17 +62,20 @@ if (not $connections)
# 1. If we've been told of a password file, read it # 1. If we've been told of a password file, read it
# 2. If the user passed the password with --new-password <secret>, use that. # 2. If the user passed the password with --new-password <secret>, use that.
# 3. Ask the user for the new password. # 3. Ask the user for the new password.
if ($anvil->data->{switches}{password_file}) if ($anvil->data->{switches}{'password-file'})
{ {
# Read the password in from the file. # Read the password in from the file.
if (-e $anvil->data->{switches}{password_file}) $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { "switches::password-file" => $anvil->data->{switches}{'password-file'} }});
if (-e $anvil->data->{switches}{'password-file'})
{ {
$anvil->data->{switches}{'new-password'} = $anvil->Storage->read_file({file => $anvil->data->{switches}{password_file}}); # Read it in and remove the new-line(s), if it(they) exist.
$anvil->data->{switches}{'new-password'} = $anvil->Storage->read_file({file => $anvil->data->{switches}{'password-file'}});
$anvil->data->{switches}{'new-password'} =~ s/\n//gs;
} }
else else
{ {
# The file doesn't exist. # The file doesn't exist.
print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{password_file} }}); print $anvil->Words->string({key => "error_0008", variables => { file => $anvil->data->{switches}{'password-file'} }});
$anvil->nice_exit({exit_code => 4}); $anvil->nice_exit({exit_code => 4});
} }
} }
@ -80,15 +83,13 @@ elsif (not $anvil->data->{switches}{'new-password'})
{ {
print $anvil->Words->string({key => "message_0018"})."\n"; print $anvil->Words->string({key => "message_0018"})."\n";
# Turn off echo # Turn off echo
my $old_stty = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." --save"}); $anvil->System->stty_echo({set => "off"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { old_stty => $old_stty }});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." -echo"});
my $password1 = <STDIN>; my $password1 = <STDIN>;
chomp($password1); chomp($password1);
$password1 =~ s/^\s+//; $password1 =~ s/^\s+//;
$password1 =~ s/\s+$//; $password1 =~ s/\s+$//;
# Turn echo on # Turn echo on
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$old_stty}); $anvil->System->stty_echo({set => "on"});
if (not $password1) if (not $password1)
{ {
@ -98,18 +99,18 @@ elsif (not $anvil->data->{switches}{'new-password'})
print $anvil->Words->string({key => "message_0019"})."\n"; print $anvil->Words->string({key => "message_0019"})."\n";
# Turn off echo # Turn off echo
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." -echo"}); $anvil->System->stty_echo({set => "off"});
my $password2 = <STDIN>; my $password2 = <STDIN>;
chomp($password2); chomp($password2);
$password2 =~ s/^\s+//; $password2 =~ s/^\s+//;
$password2 =~ s/\s+$//; $password2 =~ s/\s+$//;
# Turn echo on # Turn echo on
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$old_stty}); $anvil->System->stty_echo({set => "on"});
if ($password1 eq $password2) if ($password1 eq $password2)
{ {
$anvil->data->{switches}{'new-password'} = $password1; $anvil->data->{switches}{'new-password'} = $password1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { "switches::new_password" => $anvil->data->{switches}{'new-password'} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { "switches::new-password" => $anvil->data->{switches}{'new-password'} }});
} }
else else
{ {
@ -130,18 +131,27 @@ else
{ {
### TODO: Support '--peers' to also update the peer dashboards. ### TODO: Support '--peers' to also update the peer dashboards.
# Updating just ourself # Updating just ourself
print $anvil->Words->string({key => "message_0020"})."\n"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 1, list => { "switches::new-password" => $anvil->data->{switches}{'new-password'} }});
print $anvil->Words->string({key => "message_0021"})." "; if (($anvil->data->{switches}{y}) or ($anvil->data->{switches}{yes}))
my $answer = <STDIN>;
chomp($answer);
if ($answer =~ /^y/)
{ {
print $anvil->Words->string({key => "message_0023"})."\n";
update_local_passwords($anvil); update_local_passwords($anvil);
} }
else else
{ {
# Abort. print $anvil->Words->string({key => "message_0020"})."\n";
print $anvil->Words->string({key => "message_0022"})."\n"; print $anvil->Words->string({key => "message_0021"})." ";
my $answer = <STDIN>;
chomp($answer);
if ($answer =~ /^y/)
{
update_local_passwords($anvil);
}
else
{
# Abort.
print $anvil->Words->string({key => "message_0022"})."\n";
}
} }
} }
@ -161,7 +171,7 @@ sub update_local_passwords
foreach my $user ("admin", "root") foreach my $user ("admin", "root")
{ {
print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n"; print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n";
$anvil->System->change_shell_user_password({user => $user, new_password => $anvil->data->{switches}{'new-password'}}); $anvil->System->change_shell_user_password({debug => 2, user => $user, new_password => $anvil->data->{switches}{'new-password'}});
} }
# Update the database password. # Update the database password.

@ -33,7 +33,7 @@ $anvil->Log->secure({set => 0});
$anvil->Get->switches; $anvil->Get->switches;
# Paths # Paths
$anvil->Storage->read_config({file => $anvil->data->{path}{config}{'anvil.conf'}}); $anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
# Make sure we're running as 'root' # Make sure we're running as 'root'
# $< == real UID, $> == effective UID # $< == real UID, $> == effective UID

@ -34,8 +34,7 @@ $anvil->Log->secure({set => 1});
$anvil->Get->switches; $anvil->Get->switches;
# Paths # Paths
$anvil->data->{path}{config}{'anvil.conf'} = "/etc/anvil/anvil.conf"; $anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Storage->read_config({file => $anvil->data->{path}{config}{'anvil.conf'}});
my $local_id = $anvil->Database->get_local_id; my $local_id = $anvil->Database->get_local_id;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { local_id => $local_id }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { local_id => $local_id }});

Loading…
Cancel
Save