4

I am trying to manage Unix users with puppet. Puppet provides enough tools to create accounts and provide authorized_keys files for instance, but no to set up user password, and it tell to the user.

What I have done is a python script which generate a random password and send it to the user by email. The problem is, it is not possible to launch passwd Unix command with python, I have then written a bash script with the command:

echo -ne "$password\n$password\n" | passwd $user
passwd -e $user

Launched manually, the script works fine and the created user has its password sent by email. But when puppet launches it, only the python script gets executed, as if the os.system('/bin/bash my_bash_script') is ignored. No error is displayed. And the user gets its password, but the passwd commands are not launched.

Is there any limitation with puppet preventing to perform what I described? Or, how can I otherwise change the user account, its expiration, and send password by email?

I can provide more information, but right now, I don't know which are accurate.

Many thanks!

EDIT: Here is a basic code with same symptoms:

python: setuserpassword.py

#!/usr/bin/python
import os
import sys

user = sys.argv[1]
mail = sys.argv[2]
os.system('/bin/bash /root/tools/setuserpassword.sh '+user+' '+mail)

bash: setuserpassword.sh

#!/bin/bash
# Password setup for the account
password=`pwgen -N1 --secure 10`
echo -ne "$password\n$password\n" | passwd $USER > /dev/null 2>&1
[[ $? -ne 0 ]] && exit 2

# Setup the expirancy label to make sure user
# change its password upon fist success login
chage -d0 $USER

# Email sending to inform user of his new password
echo -ne "Hello, $USER;\n
The password is:\n
$password\n" | mail -s "$USER account created" $MAIL

Here is the module (no problem on this side)

define add_user ( $email, $uid, $gid ) { 

$username = $title

user { $username:
  comment => "$email",
  home    => "/home/$username",
  shell   => "/bin/bash",
  uid     => $uid,
  gid     => $gid,
}   

exec { "/root/tools/setuserpassword.py $username $email":
   path           => "/bin:/usr/bin:/usr/sbin/sbin",
   refreshonly    => true,
   subscribe      => user[$username],
   onlyif         => "cat /etc/shadow | grep $username | grep '!'",
   require        => Package['pwgen'],
}   
2
  • can you post the relevant lines in your puppet module and the python script?
    – Andy H
    Sep 9, 2012 at 9:42
  • Thanks for your answer, I have provided the code as an edit
    – philippe
    Sep 9, 2012 at 10:33

2 Answers 2

3

No need to use a bash script, just use the next function I'm using for some other stuff to generate the shadow encrypted password:

 def shadow(self,password):
  SomeZolt="$6$"+''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(9))+"$"
  return crypt.crypt(password, SomeZolt)

After which write additional functionality to your python script which will open the /etc/shadow file and write the encrypted password you get by using this function for this user thus making a line like:

some_user:!!:15579:0:99999:7:::

make look like:

some_user:$1$JShdquwdjsd38hSJhdqwdkwd:15579:0:99999:7:::

Thus some_user will have the assigned password.
Additionaly you can set the user directly in the puppet statement by changing it to:

user { $username:
  comment => "$email",
  home    => "/home/$username",
  shell   => "/bin/bash",
  uid     => $uid,
  gid     => $gid,
  password => "SomeAlreadyEncryptedPassword",
}   

You can use by the way the function specified to get the encrypted password and then insert it to the statement.

1

Thanks for your answer, I have followed your advice and coded a def to generate the password hash and put it in the /etc/shadow file.

Here is the script I wrote if someone may be interested in:

class Unix_Account:

 def __init__(self, user):
    self.user      = user
    self.password  = ''.join(
       random.choice(
          string.ascii_uppercase + string.ascii_lowercase + string.digits
       ) for x in range(password_length)
    )
    self.salt      = '$6$'+''.join(
       random.choice(string.ascii_uppercase + string.digits
    ) for x in range(9))+'$'
    self.line      = self.user+':'+crypt.crypt(self.password, self.salt)+':0:0:99999:7:::'

 def change_password(self):
    self.result = re.sub(self.user+".*", self.line, file('/etc/shadow', 'r').read())
    file('/etc/shadow', 'w').write(self.result)

Then another class sends the password to the user.

The password expiry date is set to 0 so that the user is forced to change it at first connection.

The connection is made by SSH, the appropriate key must be used to set up the password. The password is used for the sudo command.

Thanks again!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .