Hi all

Thanks to everybody who replied.  I agree that the best way would be to 
use an internal cfengine call to generate a random password, but as 
there doesn't seem to be an internal way of generating strings with 
alphanumeric and or punctuation symbols, I persued the external call.

On Wed, Jul 27, 2011 at 08:06:18PM +0200, phnakarin said:

> I spotted errors on your code. For example, repair_failed and 
> promise_repaired are not allowed in commands stanza but classes body.
> http://cfengine.com/manuals/cf3-reference.html#classes-in-_002a

Mmm, I'm not sure that's the case - they seem to work fine in commands 
stanzas.  Everything is a promise, including commands, so it seems 
reasonable (to me) to be able to set classes based on whether a promise 
was kept/repaired, even if it's a command promise.
 
> Try this code if you don't mind. It should do what you want. I just 
> played around with classes

Thank you!  That gave me the crucial bit of logic.

set $newpass if class "need_newpass" exists
create class "need_newpass" if variable $newpass is not defined
if the initial password check fails, then cancel class "need_newpass"

It's still not close to perfect.  It calls makepasswd every time 
cf-agent runs, which isn't efficient when under normal circumstances 
(after initial installation) the generated password will never get used.  
I'd prefer to delay the call to makepassword until after determining 
that a new password is needed, but I can't see a way to set a variable 
based on a class that doesn't exist at the start of the run - something 
like:

vars
  set_new_mysql_password::
    "newpass" string => execresult("/usr/bin/makepasswd --chars 12","noshell");

commands
    "/usr/bin/mysqladmin status"
  repair_failed => { "set_new_mysql_password" };

 set_new_mysql_password::
    "/do/the/mysql/fix/with $newpass"

would be perfect.  If I do that, though, $(newpass) is never 
instantiated.
 
For posterity, here's the code I've finished up with, in case anybody 
wonders how it got solved.  I've added some extra code to force the root 
password if the password in .my.cnf is neither correct nor null.  This 
takes (at least) two cf-agent runs to repair - the first pass resets the 
password back to null, then next run will set a new password.  It 
restarts the mysql daemon, so may well not be something to 
risk in production (for eg if somebody deletes .my.cnf on the 
mysql server, then as part of the password recovery mysql will restart 
at the next agent run ).

bundle agent app_db_mysql_mycnf
{
vars:

    "mycnf" string =>  "root/.my.cnf";

    "newpass" string => execresult("/usr/bin/makepasswd --chars 12","noshell"),
     policy => "constant",
     ifvarclass => "need_newpass"; # only set $(newpass) if class
                                   # need_newpass exists
classes:

    "need_newpass" not => isvariable("newpass");  # create class need_newpass
                                                  # if $(newpass) is not set
commands:

    "/usr/bin/mysqladmin status"
         handle => "check_mysql_root_pwd",
        comment => "Check mysql root password",
  repair_failed => { "set_mysql_root_from_null", "need_newpass" };

 set_mysql_root_from_null::
    "/usr/bin/mysqladmin --password= password $(newpass)"
           handle => "set_mysql_root_from_null",
          comment => "If null, set Mysql root password to $(newpass)",
 promise_repaired => { "update_mycnf" },
    repair_failed => { "force_mysql_root_pwd" },
  cancel_repaired => { "need_newpass" }, # once the password has been set,
  cancel_notkept  => { "need_newpass" }; # cancel the class so that subsequent
                                         # passes don't generate a new pwd
 force_mysql_root_pwd::
    "/root/force_mysql_root_pwd.sh"
         handle => "force_mysql_root_pwd",
        comment => "Force the mysql root password to null.";
       # probably should put some error trapping in here

files:

   "/$(mycnf)"
     handle    => "my_cnf",
     comment   => "Seed the my_cnf file",
     perms     => mog("0600", "root", "root"),
     compare   => "exists",
     copy_from => remote_cp("$(g.clientfiles)/common/$(mycnf)");

update_mycnf::
   "/$(mycnf)"
     handle    => "update_mycnf",
     comment   => "Add the new password to my_cnf",
     perms     => mog("0600", "root", "root"),
     edit_line => section_config("client","password","$(newpass)");

 force_mysql_root_pwd::
   "/root/force_mysql_root_pwd.sh"
     handle    => "my_cnf",
     comment   => "Copy the mysql root pwd force script",
     perms     => mog("0700", "root", "root"),
     copy_from => 
remote_cp("$(g.clientfiles)/common/root/force_mysql_root_pwd.sh");
}

bundle edit_line section_config(section,variable,setting) {
  delete_lines:
    "$(variable).*"
       select_region => INI_section("$(section)");

  insert_lines:
    "$(variable)=$(setting)"
       select_region => INI_section("$(section)");
}

force_mysql_root_pwd.sh is a simple shell script:
--
#!/bin/sh

TEMPFILE=`mktemp`

echo "UPDATE mysql.user SET Password=PASSWORD('') WHERE User='root';" \
   > $TEMPFILE
echo "FLUSH PRIVILEGES;" >> $TEMPFILE

chmod 644 $TEMPFILE

/etc/init.d/mysql stop
mysqld_safe --init-file=$TEMPFILE &

sleep 5

/etc/init.d/mysql stop
/etc/init.d/mysql start

rm $TEMPFILE

Cheers
Simon
_______________________________________________
Help-cfengine mailing list
[email protected]
https://cfengine.org/mailman/listinfo/help-cfengine

Reply via email to