7

Questions

  1. Is there a more secure/better way for setting a user's password non-interactively via a Python script? My current solution uses chpasswd from a Fabric script. Another option would be to use Pexpect from within the Fabric script.
  2. Is my current method of setting the password a security concern? The potential security concern that I see is that the password is shown as clear-text on my local terminal as follows:
    [xxx.xx.xx.xxx] run: echo "johnsmith:supersecretpassw0rd" | chpasswd.
    Since I only run the Fabric script from my laptop, I don't think this is a security issue, but I'm interested in other people's input.

Background

I've created a Python script using Fabric to configure a freshly built Slicehost Ubuntu slice. In case you're not familiar with Fabric, it uses Paramiko, a Python SSH2 client, to provide remote access "for application deployment or systems administration tasks."

One of the first things I have the Fabric script do is to create a new admin user and set their password. Unlike Pexpect, Fabric cannot handle interactive commands on the remote system, so I need to set the user's password non-interactively. At present, I'm using the chpasswd command, which reads the username and password as clear-text.

Current Code

# Fabric imports and host configuration excluded for brevity
root_password = getpass.getpass("Root's password given by SliceManager: ")
admin_username = prompt("Enter a username for the admin user to create: ")
admin_password = getpass.getpass("Enter a password for the admin user: ")
env.user = 'root'
env.password = root_password
# Create the admin group and add it to the sudoers file
admin_group = 'admin'
run('addgroup {group}'.format(group=admin_group))
run('echo "%{group} ALL=(ALL) ALL" >> /etc/sudoers'.format(
    group=admin_group)
)
# Create the new admin user (default group=username); add to admin group
run('adduser {username} --disabled-password --gecos ""'.format(
    username=admin_username)
)
run('adduser {username} {group}'.format(
    username=admin_username,
    group=admin_group)
)
# Set the password for the new admin user
run('echo "{username}:{password}" | chpasswd'.format(
    username=admin_username,
    password=admin_password)
)

Local System Terminal I/O

$ fab config_rebuilt_slice
Root's password given by SliceManager: 
Enter a username for the admin user to create: johnsmith
Enter a password for the admin user: 
[xxx.xx.xx.xxx] run: addgroup admin
[xxx.xx.xx.xxx] out: Adding group `admin' (GID 1000) ...
[xxx.xx.xx.xxx] out: Done.
[xxx.xx.xx.xxx] run: echo "%admin ALL=(ALL) ALL" >> /etc/sudoers
[xxx.xx.xx.xxx] run: adduser johnsmith --disabled-password --gecos ""
[xxx.xx.xx.xxx] out: Adding user `johnsmith' ...
[xxx.xx.xx.xxx] out: Adding new group `johnsmith' (1001) ...
[xxx.xx.xx.xxx] out: Adding new user `johnsmith' (1000) with group `johnsmith' ...
[xxx.xx.xx.xxx] out: Creating home directory `/home/johnsmith' ...
[xxx.xx.xx.xxx] out: Copying files from `/etc/skel' ...
[xxx.xx.xx.xxx] run: adduser johnsmith admin
[xxx.xx.xx.xxx] out: Adding user `johnsmith' to group `admin' ...
[xxx.xx.xx.xxx] out: Adding user johnsmith to group admin
[xxx.xx.xx.xxx] out: Done.
[xxx.xx.xx.xxx] run: echo "johnsmith:supersecretpassw0rd" | chpasswd
[xxx.xx.xx.xxx] run: passwd --lock root
[xxx.xx.xx.xxx] out: passwd: password expiry information changed.

Done.
Disconnecting from root@xxx.xx.xx.xxx... done.
Matthew Rankin
  • 1,155
  • 5
  • 15
  • 32
  • FYI: chpasswd can take encrypted passwords with the -e option. You can encrypt a password with `grub-crypt`. Someone else mentioned `openssl passwd`. The difference between the two is that `openssl passwd` can only encrypt passwords with traditional Unix `crypt` or MD5, and `grub-crypt` supports sha-256 and sha-512. – Jonathan Jun 01 '15 at 12:42

5 Answers5

3

You pass the password in the command line you are executing. Those may probably be visible not only in your terminal, but also by other users issuing 'ps' command (that may depend on the system configuration). And the password is plain text.

I don't know Fabric, but if it gives you a possibility to communicate with executed commands via their stdin/stdout (like subprocess.Popen()), then it would be probably a better choice to use that instead of 'echo username:password|...'.

Also, you may chose to create the password hash in your Python script (using the crypt module and "$1$XXXXXXXX$" ("X" are random characters) salt) and pass that to chpasswd -e. The plain text password will never be displayed or logged then.

You generate the password hash with the crypt module like so:

import crypt
p='secretpassword'
print(crypt.crypt(p))

And you can pass the output to chpasswd -e like this:

`echo 'someusername:passwordhash' | chpasswd -e`
Mani
  • 131
  • 5
Jacek Konieczny
  • 3,597
  • 2
  • 21
  • 22
2

What I usually do for non-interactive password-setting is I generate a random password and set it to a variable, then pass the variable to my command. I don't have to be creative with passwords, and I don't have to leave a standard password in a plain text file.

Here's an active-state recipe for generating random passwords.

As far as the security goes, I don't feel that it's much of an issue to have a password printed on your own terminal (with the random-password, you'd want that anyway so you can take note of the created pwd).

Is there something different that Fabric does with ssh? -- most stuff traveling through ssh should be encrypted anyway.

mgorven
  • 30,036
  • 7
  • 76
  • 121
snk
  • 372
  • 3
  • 4
  • 10
  • @Steve: "Is there something different that Fabric does with ssh?" Nothing that I'm aware of. Fabric uses Paramiko as its SSH module so I agree that everything should be encrypted over SSH. – Matthew Rankin Mar 16 '10 at 14:25
2

If you are using chpasswd from with in a fabric, then make sure not to use the plain text password from the command line. It is not just the stdout where the password will be displayed, it will also be written to /var/log/secure. In Fabric you can use

with hide('running', 'output', 'error'):
     <your task here>

And make sure you use hash string along with the chpasswd command so that even in the secure logs you will not see the password in plain-text. First decide upon the password to be used, and generate the hash string. you could use openssl for that.

openssl passwd -1

This will provide you a prompt to enter password and generate the MD5 hash. and you can use this in you script as follows.

echo "<user>:<MD5-hash>" | chpasswd -e

The -e flag in the above command tells chpasswd that the password is encrypted.

sabs6488
  • 151
  • 4
1

The upcoming 1.0 release of Fabric (available now from the git branch) will allow for interactive sessions with the remote end.

mgorven
  • 30,036
  • 7
  • 76
  • 121
0

Could you just auto configure your new slice and then just login and change the password right away? Sometimes the simplest approach is the best.

But alternatively, you can locally generate the encrypted password and just go ahead and write the encrypted password to the /etc/shadow file. This way you do not use plain text at all.

Mind you, that you will have to trust your local system when initially give the initial password.

By the way, could you make a copy of your auto config available so I use it instead of writing everything from scratch? Currently I am doing everything via bash.

something like this?

sed 's/^root:.*$/root:$PASSWD:13063::::::/' /etc/shadow > /etc/shadow
mgorven
  • 30,036
  • 7
  • 76
  • 121
Val Neekman
  • 101
  • 1