* Created Storage->update_config that will update a variable in anvil.conf (locally or remotely).

* Finished (for now) tools/anvil-change-password.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 7 years ago
parent 6f3537807a
commit 2163739b93
  1. 171
      Anvil/Tools/Storage.pm
  2. 2
      Anvil/Tools/System.pm
  3. 2
      share/words.xml
  4. 114
      tools/anvil-change-password

@ -25,6 +25,7 @@ my $THIS_FILE = "Storage.pm";
# record_md5sums
# rsync
# search_directories
# update_config
# write_file
# _create_rsync_wrapper
@ -1796,6 +1797,174 @@ sub search_directories
return ($self->{SEARCH_DIRECTORIES});
}
=head2 update_config
This takes a variable name and value and updates the C<< path::configs::anvil.conf >> file. If the given variable is already set to the requested value, nothing further is done.
Returns C<< 0 >> on success, C<< 1 >> on error.
B<< Note >>: If the variable is not found, it is treated like an error and C<< 1 >> is returned.
Parameters;
=head3 port (optional, default 22)
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 password (optional)
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made (though this will usually fail).
=head3 secure (optional)
If set to 'C<< 1 >>', the value is treated as containing secure data for logging purposes.
=head3 target (optional)
If set, the config file will be updated on the target machine. This must be either an IP address or a resolvable host name.
=head3 variable (required)
This is the C<< a::b::c >> format variable name to update.
=head3 value (optional)
This is the value to set the C<< variable >> to. If this is not passed, the variable will be set to an empty string.
The updated config file will be written locally in C<< /tmp/<file_name> >>, C<< $anvil->Storage->rsync() >> will be used to copy the file, and finally the local temprary copy will be removed.
=head3 remote_user (optional, default root)
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=cut
sub update_config
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $secure = defined $parameter->{secure} ? $parameter->{secure} : "";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $variable = defined $parameter->{variable} ? $parameter->{variable} : "";
my $value = defined $parameter->{value} ? $parameter->{value} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $seen = 0;
my $update = 0;
my $new_file = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
password => $anvil->Log->secure ? $password : "--",
port => $port,
secure => $secure,
target => $target,
value => ((not $secure) or ($anvil->Log->secure)) ? $value : "--",
variable => $variable,
remote_user => $remote_user,
}});
if (not $variable)
{
# No source
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->update_config()", parameter => "variable" }});
return(1);
}
# Read in the config file.
my $body = $anvil->Storage->read_file({
debug => $debug,
file => $anvil->data->{path}{configs}{'anvil.conf'},
password => $password,
port => $port,
target => $target,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { body => $body }});
foreach my $line (split/\n/, $body)
{
my $original_line = $line;
$line =~ s/#.*$//;
$line =~ s/^\s+//;
if ($line =~ /^(.*?)=(.*)$/)
{
my $this_variable = $1;
my $this_value = $2;
$this_variable =~ s/\s+$//;
$this_value =~ s/^\s+//;
my $is_secure = $this_variable =~ /passw/i ? 1 : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
this_variable => $this_variable,
this_value => ((not $is_secure) or ($anvil->Log->secure)) ? $this_value : "--",
}});
if ($this_variable eq $variable)
{
$seen = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { seen => $seen }});
if ($this_value ne $value)
{
$update = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { update => $update }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { ">> original_line" => $original_line }});
$original_line =~ s/$this_value/$value/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => { "<< original_line" => $original_line }});
}
}
}
$new_file .= $original_line."\n";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 1, list => { new_file => $new_file }});
# Did we see the variable?
if (not $seen)
{
if ($target)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0175", variables => {
variable => $variable,
file => $anvil->data->{path}{configs}{'anvil.conf'},
target => $target,
}});
return(1);
}
else
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0174", variables => {
variable => $variable,
file => $anvil->data->{path}{configs}{'anvil.conf'},
}});
return(1);
}
}
# Do we need to update the file?
my $error = 0;
if ($update)
{
# Yup!
$error = $anvil->Storage->write_file({
body => $new_file,
debug => $debug,
file => $anvil->data->{path}{configs}{'anvil.conf'},
group => "admin",
mode => "0640",
overwrite => 1,
secure => 1,
user => "admin",
password => $password,
port => $port,
target => $target,
remote_user => $remote_user,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { error => $error }});
}
return($error);
}
=head2 write_file
This writes out a file, either locally or on a remote system. It can optionally set the ownership and mode as well.
@ -1880,7 +2049,7 @@ sub write_file
my $user = defined $parameter->{user} ? $parameter->{user} : "root";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $error = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
body => $body,
file => $file,
group => $group,

@ -1793,7 +1793,7 @@ sub stty_echo
if ($set eq "off")
{
$anvil->data->{sys}{terminal}{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::terminal::stty' => $anvil->data->{sys}{terminal}{stty} }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { 'sys::terminal::stty' => $anvil->data->{sys}{terminal}{stty} }});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." -echo"});
}
elsif (($set eq "on") && ($anvil->data->{sys}{terminal}{stty}))

@ -253,6 +253,8 @@ The database connection error was:
<key name="log_0171"><![CDATA[[ Error ] - The method: Account->encrypt_password() tried to use the algorithm: [#!variable!algorithm!#], which is not recognized. Only 'sha256', 'sha384' and 'sha512' are currently supported. The desired algorithm can be set via 'sys::password::algorithm'.]]></key>
<key name="log_0172"><![CDATA[[ Error ] - Asked to validate a password for the user: [#!variable!user!#], but that user wasn't found.]]></key>
<key name="log_0173"><![CDATA[[ Error ] - Asked to valudate a password encoded with the algorithm: [#!variable!user_algorithm!#], which is not recognized. Only 'sha256', 'sha384' and 'sha512' are currently supported.]]></key>
<key name="log_0174"><![CDATA[[ Error ] - Asked to update the variable: [#!variable!variable!#] in the configuration file: [#!variable!file!#], but that variable was not found.]]></key>
<key name="log_0175"><![CDATA[[ Error ] - Asked to update the variable: [#!variable!variable!#] in the configuration file: [#!variable!file!#] on the host: [#!variable!target!#], but that variable was not found.]]></key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>

@ -58,29 +58,6 @@ if (not $connections)
$anvil->nice_exit({exit_code => 2});
}
my $user = "admin";
my $password = "Initial2";
# my $user_uuid = $anvil->Database->insert_or_update_users({
# debug => 2,
# user_name => $user,
# user_password_hash => $password,
# user_salt => "",
# user_algorithm => "",
# user_hash_count => "",
# user_is_admin => 1,
# user_is_experienced => 1,
# user_is_trusted => 1,
# });
# print "User name: [".$user."], UUID: [".$user_uuid."]\n";
# die;
my $valid = $anvil->Account->validate_password({
debug => 2,
user => $user,
password => $password,
});
print "Password validated? [".$valid."].\n";
exit;
# The order that we pick up the new password is;
# 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.
@ -159,6 +136,40 @@ else
{
print $anvil->Words->string({key => "message_0023"})."\n";
update_local_passwords($anvil);
# Update the user password.
my $user = "admin";
my $user_uuid = $anvil->Database->insert_or_update_users({
debug => 2,
user_name => $user,
user_password_hash => $anvil->data->{switches}{'new-password'},
user_is_admin => 1,
user_is_experienced => 1,
user_is_trusted => 1,
});
# Validate
my $valid = $anvil->Account->validate_password({
debug => 2,
user => $user,
password => $anvil->data->{switches}{'new-password'},
});
# Update the database password.
# foreach my $user ("postgres", $database_user)
# {
# my $update_output = $anvil->System->call({secure => 1, shell_call => $anvil->data->{path}{exe}{su}." - postgres -c \"".$anvil->data->{path}{exe}{psql}." template1 -c \\\"ALTER ROLE $user WITH PASSWORD '".$anvil->data->{database}{$local_uuid}{password}."';\\\"\"", source => $THIS_FILE, line => __LINE__});
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, secure => 1, list => { update_output => $update_output }});
# foreach my $line (split/\n/, $user_list)
# {
# if ($line =~ /ALTER ROLE/)
# {
# # Password set
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0100", variables => { user => $user }});
# }
# }
# }
}
else
{
@ -190,21 +201,58 @@ sub update_local_passwords
{
my ($anvil) = @_;
my $host_uuid = $anvil->data->{sys}{host_uuid};
my $old_password = $anvil->data->{database}{$host_uuid}{password};
my $dbh = DBI->connect("DBI:Pg:dbname=template1;host=localhost;port=5432", "postgres", $old_password, {
RaiseError => 1,
AutoCommit => 1,
pg_enable_utf8 => 1
});
my $query = "SELECT a.datname, b.usename FROM pg_catalog.pg_database a, pg_catalog.pg_user b WHERE a.datdba = b.usesysid AND a.datistemplate IS NOT TRUE AND a.datname != 'postgres'";
my $DBreq = $dbh->prepare($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0075", variables => {
query => $query,
server => "localhost",
db_error => $DBI::errstr,
}});
# Execute on the query
$DBreq->execute() or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0076", variables => {
query => $query,
server => "localhost",
db_error => $DBI::errstr,
}});
# Return the array
my $results = $DBreq->fetchall_arrayref();
my $database_name = $results->[0]->[0];
my $owner_name = $results->[0]->[1];
foreach my $user ("postgres", $owner_name)
{
my $query = "ALTER ROLE ".$user." WITH PASSWORD ".$dbh->quote($anvil->data->{switches}{'new-password'});
$dbh->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => {
query => $anvil->Log->secure ? $query : "--",
server => "localhost",
db_error => $DBI::errstr,
}});
}
# Update our database password in anvil.conf
$anvil->Storage->update_config({
debug => 2,
secure => 1,
variable => "database::${host_uuid}::password",
value => $anvil->data->{switches}{'new-password'},
});
### TODO: Loop through any other dashboards and nodes we know about and call the above with 'target'
### (and password, port and remote_user) set.
# Update the local users.
foreach my $user ("admin", "root")
{
print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n";
# $anvil->System->change_shell_user_password({debug => 2, 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'}});
}
### TODO: Put the database into maintenance mode, then check for any known nodes and update their
### password for us.
# Update the database password.
#my $apache_user = $anvil->data->{sys}{apache}{user} ? $anvil->data->{sys}{apache}{user} : "admin";
#$anvil->System->change_apache_password({debug => 2, new_password => $anvil->data->{switches}{'new-password'}});
return(0);
}

Loading…
Cancel
Save