21

The documentation refers us to the github example, but this is a bit sparse and mysterious.

It says this:

# created with:
# crypt.crypt('This is my Password', '$1$SomeSalt')
password: $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.

but crypt.crypt doesn't emit what the example shows. It also uses MD5.

I tried this:

# python
import crypt
crypt.crypt('This is my Password', '$6$somereallyniceandbigrandomsalt$')
>> '$69LxCegsnIwI'

but the password field of user should get something like this:

password: $6$somereallyniceandbigrandomsalt$UqddPX3r4kH3UL5jq5/ZI.

which includes three $ delimiters separating the 6 (which signifies that its a SHA-512 hash), the salt, and the crypted password.

Note that the python crypt docs don't mention anything about the $N format.

Questions:

  1. Is the salt, as supplied to crypt.crypt, supposed to end with a trailing $ or is it in $N$SALT format?

  2. Python docs refer to DES, but how is SHA-512 or MD5 being called and where is the documention for this?

  3. Am I really supposed to take the output of crypt.crypt and cut off the first $6 and make $N$SALT$CRYPTED? Is this what ansible needs?

Chris Sattinger
  • 3,788
  • 3
  • 26
  • 27
  • A1 : trailing $ in salt doesn't make any difference – Chris Sattinger Mar 06 '13 at 11:58
  • Talked with the Ansible IRC people, you should update this and answer it based off the feedback you got from Github https://github.com/ansible/ansible/issues/2305 – David Apr 15 '13 at 19:29
  • that feedback didn't solve the issue. I am still not able to create passwords using ansible. I will update the question though as there is more info to add. thanks for the reminder – Chris Sattinger Apr 25 '13 at 21:31
  • Sorry to http://stackoverflow.com/users/1937270/syb0rg but I just rolled back your edit. You removed the links and the rest of the changes were just capitalization changes, some of which resulted in incorrect grammar. That added nothing to the question. – Chris Sattinger Apr 25 '13 at 21:36

8 Answers8

33

The python example shown in the documentation depends on what version of crypt is running on the OS you are using.

I generated the crypt on OS X and the server I was targetting is ubuntu.

Due to differences in which implementation of crypt is offered by the OS, the result is different and incompatible.

Use this instead:

http://pythonhosted.org/passlib/

Passlib is a password hashing library for Python 2 & 3, which provides cross-platform implementations of over 30 password hashing algorithms, as well as a framework for managing existing password hashes. It’s designed to be useful for a wide range of tasks, from verifying a hash found in /etc/shadow, to providing full-strength password hashing for multi-user application.

>>> # import the hash algorithm
>>> from passlib.hash import sha512_crypt

>>> # generate new salt, and hash a password
>>> hash = sha512_crypt.encrypt("password")
>>> hash

'$6$rounds=656000$BthPsosdEpqOM7Qd$l/ln9nyEfxM67ea8Bvb79JoW50pGjf6iM87taIvfSmpjasE4/wBG1.60pFS6W992T7Q1q2wikMbxYUvMHD1tT1'

Chris Sattinger
  • 3,788
  • 3
  • 26
  • 27
  • What do I do with this output string? Where do I put it and which part of this string? Can you provide also example ansible playbook which uses this string? – Petr Apr 14 '16 at 11:16
  • Do I need to replace ' with " so that I can use this in yml? – Petr Apr 14 '16 at 11:21
  • Thank you! I made this into a script that I am including in my project to save macOS users from having to Google for an answer. I included the script in my answer below. https://stackoverflow.com/a/45105252/117471 – Bruno Bronosky Jul 14 '17 at 14:22
6

This has been updated in the Ansible docs. There are two preferred ways:

How do I generate crypted passwords for the user module?

The mkpasswd utility that is available on most Linux systems is a great option:

mkpasswd --method=SHA-512 If this utility is not installed on your system (e.g. you are using OS X) then you can still easily generate these passwords using Python. First, ensure that the Passlib password hashing library is installed.

pip install passlib

Once the library is ready, SHA512 password values can then be generated as follows:

python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt(getpass.getpass())"

Eli
  • 31,424
  • 32
  • 127
  • 194
4

This worked for me (using python 2.7.4):

python
>>> import crypt
>>> crypt.crypt('thisismypassword', '$6$Som3S@lt$')
'$6$Som3S@lt$XGoe9ONI00NaTkYn46CLDr8TSkvkovahinFqy95vrSe5Hzx2999C9mgF76ODFRnXMJHUCWFHLdkYd3c7AB9WV.'

I have a vars.yml that looks like this:

---
password: $6$Som3S@lt$XGoe9ONI00NaTkYn46CLDr8TSkvkovahinFqy95vrSe5Hzx2999C9mgF76ODFRnXMJHUCWFHLdkYd3c7AB9WV.

and a playbook.yml like this:

---
- hosts: vagrant
  vars_files:
    - vars.yml
  user: vagrant

  tasks:
  - name: create artefactual user
    user: name=artefactual state=present password={{password}} shell=/bin/bash

I run my playbook using vagrant, vagrant up , and then from another console I can ssh to the newly created vm using the artefactual user created by ansible, with the password thisismypassword.

I just copied the output of crypt.crypt into the ansible variable called password and used that. The output of crypt that you show in your question looks too short, I am not sure why you got that, perhaps a different version of python?

Chris Seymour
  • 75,961
  • 24
  • 144
  • 187
2

You can use the jinja2 filters that have the capability to handle the generation of encrypted passwords. Here is a working example to create the linux user with the provided password:

- name: Creating Linux User
  user:
    name: "{{ myuser }}" 
    password: "{{ mypassword | password_hash('sha512') }}"

Hope this will help you and others.

Arbab Nazar
  • 18,514
  • 8
  • 66
  • 74
2

I took @felix's answer and made it into a script that I could include in a docker project that I am working on. I know that a lot of developers use macOS/OSX and that there is no mkpasswd on that platform, so I'm saving them the Googling.

I have added the following options:

  • PROCESS_TIME (boolean)
    • Enables 2nd line of output with number of rounds and CPU time
  • ROUNDS (integer)
    • Overrides the default_rounds value which is tuned to take ~300ms on an "average" system. You want a minimum of 100ms, but should be as great as you can afford.
#!/usr/bin/env python3

# Because OSX doesn't have mkpasswd...

# Based on https://stackoverflow.com/a/17992126/117471
# python3 -c "from passlib.hash import sha512_crypt; print(sha512_crypt.encrypt(input()))" <<< bruno  # NOQA

# Usage:
#
# $ ./mkpasswd.py
# Password:
# $6$rounds=656000$pfFmQISGcjWHOCxW$rBptiSK.tqSPnUiq6KiSHzz6LvvW/x1SjkkWFwxWB9Dt75NLNBs0N3OyGV4K5ejjBs/u.o3jtigvUKbmmwVQP.
#
# $ PROCESS_TIME=1 ./mkpasswd.py
# Password:
# $6$rounds=656000$e0OGrad82DBrUo9T$ldqtOdN54gmXI6nb0D.Y5mm5ih.LIQm/Ep/bkNL76.3hE65FqXA9wyZ.M5YOrv6dSvwhPAktXGJ6LJT0Fgd4x.
# 656000 rounds in 1.008705 seconds of cpu time
#
# $ ROUNDS=1280000 PROCESS_TIME=1 ./mkpasswd.py <<< bruno
# $6$rounds=1280000$QO5FSyw5rQpiY6PI$0zRMJ4RzCbH61XxIdpsUm/79.VZ13Mm9TBN9GvJwt1LI1U5FVzakrLya5VJsXlTou3p5ZeWmo29bIUjubRuc31
# 1280000 rounds in 1.9206560000000001 seconds of cpu time

import os
import sys
import time
from getpass import getpass
from passlib.hash import sha512_crypt

rounds = os.environ.get('ROUNDS')
if not rounds:
    rounds = sha512_crypt.default_rounds

passwd = input() if not sys.stdin.isatty() else getpass()

proc = sha512_crypt.using(rounds=rounds)
start = time.process_time()
out = proc.encrypt(passwd)
end = time.process_time()

print(out)

if os.environ.get('PROCESS_TIME'):
    print('{} rounds in {} seconds of cpu time'.format(rounds, end-start))
Bruno Bronosky
  • 54,357
  • 9
  • 132
  • 120
  • Just using a hash function is not sufficient and just adding a salt does little to improve the security. Instead iterate over an HMAC with a random salt for about a 100ms duration and save the salt with the hash. Use a function such as `PBKDF2`, `Rfc2898DeriveBytes`, `Bcrypt` or similar functions. The point is to make the attacker spend a lot of time finding passwords by brute force. – zaph Jul 14 '17 at 15:01
  • I'm not sure what you mean, but I suspect it may be about the `$6$rounds=656000$` part of the output. I'd like to get rid of that. It worked better with `crypt.crypt` in python, but that uses the OS's crypt and the one that comes with macOS is not adequate. Do you have any suggestions on how to do what you are saying with https://passlib.readthedocs.io/en/stable/ – Bruno Bronosky Jul 14 '17 at 15:14
  • By "worked better" I mean generated output consistent with `mkpasswd -m sha-512` (which includes a random salt). I chose `passlib.hash.sha512_crypt.encrypt` based on https://passlib.readthedocs.io/en/stable/narr/quickstart.html#making-a-decision and it should meet your criteria. It is slow in that it takes just over 1 second of real time and 0.026s for system time. – Bruno Bronosky Jul 14 '17 at 15:33
  • 1
    Python [`passlib.hash.sha512_crypt`](http://passlib.readthedocs.io/en/stable/lib/passlib.hash.sha512_crypt.html) is a good solution but the `rounds` needs to be set to a value that takes ~100ms of CPU time. – zaph Jul 14 '17 at 16:00
  • @zaph I have updated the code to prove that the `default_rounds` rounds was indeed adequate. I have also included options that I think you will appreciate. I would appreciate your upvote. This is the most effort I've ever invested in an SO answer to try to reverse a downvote. – Bruno Bronosky Jul 14 '17 at 19:42
1

I have been using the following shell command to set the password.

- name: "Set user password: someuser"
  command: 'echo "somepassword"| passwd --stdin "someuser"'
  sudo: yes
0

try like this

vars_prompt:
 - name: "user_password"    
   prompt: "Enter a password for the user"    
   private: yes    
   encrypt: "md5_crypt" #need to have python-passlib installed in local machine before we can use it    
   confirm: yes    
   salt_size: 7

 - name: "add new user" user: name="{{user_name}}" comment="{{description_user}}" password="{{user_password}}" home="{{home_dir}}" shell="/bin/bash"
Artem Feofanov
  • 111
  • 1
  • 2
0

This needs pwgen installed on target host:

- name: generate linux user password
  local_action: shell /usr/bin/pwgen 16 1
  register: generated_linux_user_password

Use hosts: localhost, set_fact and hostvars, if you need the 'variable' be globally available (facts are readonly after creation):

{{hostvars['localhost']["new_fact"]}}
sjas
  • 15,508
  • 11
  • 75
  • 80