可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an ansible task which creates a new user on ubuntu 12.04;
- name: Add deployment user
action: user name=deployer password=mypassword
it completes as expected but when I login as that user and try to sudo with the password I set it always says it's incorrect. What am I doing wrong?
回答1:
If you read Ansible's manual for user
module, it'll direct you to the Ansible-examples github repo for details how to use password
parameter.
There you'll see that your password must be hashed.
- hosts: all
user: root
vars:
# created with:
# python -c 'import crypt; print crypt.crypt("This is my Password", "$1$SomeSalt$")'
password: $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.
tasks:
- user: name=tset password={{password}}
If your playbook or ansible command line has your password as-is in plain text, this means your password hash recorded in your shadow file is wrong. That means when you try to authenticate with your password its hash will never match.
Additionally, see Ansible FAQ regarding some nuances of password parameter and how to correctly use it.
回答2:
I may be too late to reply this but recently I figured out that jinja2 filters have the capability to handle the generation of encrypted passwords. In my main.yml
I'm generating the encrypted password as:
- name: Creating user "{{ uusername }}" with admin access
user:
name: {{ uusername }}
password: {{ upassword | password_hash('sha512') }}
groups: admin append=yes
when: assigned_role == "yes"
- name: Creating users "{{ uusername }}" without admin access
user:
name: {{ uusername }}
password: {{ upassword | password_hash('sha512') }}
when: assigned_role == "no"
- name: Expiring password for user "{{ uusername }}"
shell: chage -d 0 "{{ uusername }}"
"uusername " and "upassword " are passed as --extra-vars
to the playbook and notice I have used jinja2 filter here to encrypt the passed password.
I have added below tutorial related to this to my blog
- https://thinkingmonster.wordpress.com/it-automation/386-2/ansible-roles/
回答3:
I want to propose yet another solution:
- name: Create madhead user
user:
name: madhead
password: "{{ 'password' | password_hash('sha512') }}"
shell: /bin/zsh
update_password: on_create
register: madhead
- name: Force madhead to change password
shell: chage -d 0 madhead
when: madhead.changed
Why it is better? Like already has been noted here, Ansible plays should be idempotent. You should think of them not as a sequence of actions in imperative style, but like a desired state, declarative style. As a result you should be able to run it multiple times and get the same result, the same server state.
This all sounds great, but there are some nuances. One of them is managing users. "Desired state" means that every time you run a play that creates a user he will be updated to match exactly that state. By "updated" I mean that his password will be changed too. But most probably it is not what you need. Usually, you need to create user, set and expire his password only once, further play runs shouldn't update his password.
Fortunately, Ansible has update_password
attribute in user
module that solves this issue. Mixing this with registered variables you can also expire his password only when the user is actually updated.
Note that if you change user's shell manually (suppose, you don't like the shell that evil admin forced in his play) the user will be updated, thus his password will be expired.
Also note how you can easily use plain text initial passwords in plays. No need to encode them somewhere else and paste hashes, you can use Jinja2 filter for that. However, this can be a security flaw if someone happens to login before you initially do.
回答4:
The Ansible 'user' module manages users, in the idempotent way. In the playbook below the first task declares state=present for the user. Note that 'register: newuser' in the first action helps the second action to determine if the user is new (newuser.changed==True) or existing (newuser.changed==False
), to only generate the password once.
The Ansible playbook has:
tasks:
- name: create deployment user
user:
name: deployer
createhome: yes
state: present
register: newuser
- name: generate random password for user only on creation
shell: /usr/bin/openssl rand -base64 32 | passwd --stdin deployer
when: newuser.changed
回答5:
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"
回答6:
The purpose of the role in this answer is to generate random password for new_user_name and expire the password immediately. The new_user_name
is required to change the password on his/her first logon.
create_user.yml:
---
# create_user playbook
- hosts: your_host_group
become: True
user: ansible
roles:
- create_user
roles/create_user/tasks/main.yml:
---
# Generate random password for new_user_name and the new_user_name
# is required to change his/her password on first logon.
- name: Generate password for new user
shell: makepasswd --chars=20
register: user_password
- name: Generate encrypted password
shell: mkpasswd --method=SHA-512 {{ user_password.stdout }}
register: encrypted_user_password
- name: Create user account
user: name={{ new_user_name }}
password={{ encrypted_user_password.stdout }}
state=present
append=yes
shell="/bin/bash"
update_password=always
when: new_user_name is defined and new_user_name in uids
register: user_created
- name: Force user to change password
shell: chage -d 0 {{ new_user_name }}
when: user_created.changed
- name: User created
debug: msg="Password for {{ new_user_name }} is {{ user_password.stdout }}"
when: user_created.changed
When you want to create a new user:
ansible-playbook -i hosts.ini create_user.yml --extra-vars "new_user_name=kelvin"
回答7:
This is the easy way:
---
- name: Create user
user: name=user shell=/bin/bash home=/srv/user groups=admin,sudo generate_ssh_key=yes ssh_key_bits=2048
- name: Set password to user
shell: echo user:plain_text_password | sudo chpasswd
no_log: True
回答8:
This is how it worked for me
- hosts: main
vars:
# created with:
# python -c "from passlib.hash import sha512_crypt; print sha512_crypt.encrypt('<password>')"
# above command requires the PassLib library: sudo pip install passlib
- password: '$6$rounds=100000$H/83rErWaObIruDw$DEX.DgAuZuuF.wOyCjGHnVqIetVt3qRDnTUvLJHBFKdYr29uVYbfXJeHg.IacaEQ08WaHo9xCsJQgfgZjqGZI0'
tasks:
- user: name=spree password={{password}} groups=sudo,www-data shell=/bin/bash append=yes
sudo: yes
回答9:
The task definition for the user module should be different in the latest Ansible version.
tasks:
- user: name=test password={{ password }} state=present
回答10:
Combining a few solutions from above, I created a playbook that automatically generates correct password hashes based on plaintext passwords stored in an encrypted, local ansible vault file:
---
- hosts: [your hosts]
tasks:
- include_vars: [path to your encrypted vault file]
- local_action: "command openssl passwd -salt '{{password_salt}}' -1 '{{password}}'"
register: password_hash
- user: >
name=[your username]
state=present
password="{{password_hash.stdout}}"
Run this command using "--ask-vault-pass" option to decrypt your vault file (see ansible-vault for info on how to manage an encrypted vault).
回答11:
You can use ansible-vault for using secret keys in playbooks. Define your password in yml.
ex. pass: secret
or
user:
pass: secret
name: fake
encrypt your secrets file with :
ansible-vault encrypt /path/to/credential.yml
ansible will ask a password for encrypt it. (i will explain how to use that pass)
And then you can use your variables where you want. No one can read them without vault-key.
Vault key usage:
via passing argument when running playbook.
--ask-vault-pass: secret
or you can save into file like password.txt and hide somewhere. (useful for CI users)
--vault-password-file=/path/to/file.txt
In your case :
include vars yml and use your variables.
- include_vars: /path/credential.yml
- name: Add deployment user
action: user name={{user.name}} password={{user.pass}}
回答12:
Just for completeness I will post the ad-hoc command using ansible since there is a catch there as well.
First try generating an encrypted password using the mkpasswd utility that is available on most Linux systems:
mkpasswd --method=SHA-512
Then try the ansible ad-hock command:
ansible all -m user -a 'name=testuser shell=/bin/bash \
comment="Test User" password=$6$XXXX' -k -u admin --sudo
But make sure:
- The command is in single quotes and NOT double otherwise your password will never work
- You run it with
--sudo
or you end up with an error like (useradd: cannot lock /etc/passwd; try again later
)
回答13:
How to create encrypted password for passing to password
var to Ansible user
task (from @Brendan Wood's comment):
openssl passwd -salt 'some_plain_salt' -1 'some_plain_pass'
The result will look like:
$1$some_pla$lmVKJwdV3Baf.o.F0OOy71
Example of user
task:
- name: Create user
user: name="my_user" password="$1$some_pla$lmVKJwdV3Baf.o.F0OOy71"
UPD: crypt using SHA-512 see here and here:
Python
$ python -c "import crypt, getpass, pwd; print crypt.crypt('password', '\$6\$saltsalt\$')"
$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Perl
$ perl -e 'print crypt("password","\$6\$saltsalt\$") . "\n"'
$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Ruby
$ ruby -e 'puts "password".crypt("$6$saltsalt$")'
$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
回答14:
Mxx's answer is correct but you the python crypt.crypt()
method is not safe when different operating systems are involved (related to glibc hash algorithm used on your system.)
For example, It won't work if your generate your hash from MacOS and run a playbook on linux. In such case , You can use passlib (pip install passlib
to install locally).
from passlib.hash import md5_crypt
python -c 'import crypt; print md5_crypt.encrypt("This is my Password,salt="SomeSalt")'
'$1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.'
回答15:
Neither of the solutions worked directly on my Mac controlling Ubuntu. So for others' sake, combining Mxx and JoelB answers, here is the current Python 3 solution:
pip3 install passlib
python3 -c 'from passlib.hash import md5_crypt; \
print(md5_crypt.encrypt("This is my Password", salt="SomeSalt"))'
The result will be $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.
, as in Mxx' answer.
Better still, use SHA512 instead of MD5:
python3 -c 'from passlib.hash import sha512_crypt; \
print(sha512_crypt.encrypt("This is my Password", salt="SomeSalt"))'
Result:
$6$rounds=656000$SomeSalt$oYpmnpZahIsvn5FK8g4bDFEAmGpEN114Fe6Ko4HvinzFaz5Rq2UXQxoJZ9ZQyQoi9zaBo3gBH/FEAov3FHv48
回答16:
If you'd like to accomplish this as a Ansible ad-hoc command you can do the following:
$ password='SomethingSecret!'
$ ansible 192.168.1.10 -i some_inventory -b -m user -a "name=joe_user \
update_password=always password=\"{{ \"$password\" | password_hash('sha512') }}\""
Output from above command:
192.168.1.10 | SUCCESS => {
"append": false,
"changed": true,
"comment": "Joe User",
"group": 999,
"home": "/home/joe_user",
"move_home": false,
"name": "joe_user",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"uid": 999
}
回答17:
I know that I'm late to the party, but there is another solution that I'm using. It might be handy for distros that don't have --stdin
in passwd binary.
- hosts: localhost
become: True
tasks:
- name: Change user password
shell: "yes '{{ item.pass }}' | passwd {{ item.user }}"
loop:
- { pass: 123123, user: foo }
- { pass: asdf, user: bar }
loop_control:
label: "{{ item.user }}"
Label in loop_control
is responsible for printing only username. The whole playbook or just user variables (you can use vars_files:
) should be encrypted with ansible-vault.
回答18:
Generating random password for user
first need to define users variable then follow below
tasks:
- name: Generate Passwords
become: no
local_action: command pwgen -N 1 8
with_items: '{{ users }}'
register: user_passwords
- name: Update User Passwords
user:
name: '{{ item.item }}'
password: "{{ item.stdout | password_hash('sha512')}}"
update_password: on_create
with_items: '{{ user_passwords.results }}'
- name: Save Passwords Locally
become: no
local_action: copy content={{ item.stdout }} dest=./{{ item.item }}.txt
with_items: '{{ user_passwords.results }}'
回答19:
Well I'am totally late to party :) I had the need for ansible play that creates multiple local users with randoms passwords. This what I came up with, used some of examples from top and put them together with some changes.
create-user-with-password.yml
---
# create_user playbook
- hosts: all
become: True
user: root
vars:
#Create following user
users:
- test24
- test25
#with group
group: wheel
roles:
- create-user-with-password
/roles/create-user-with-password/tasks/main.yml
- name: Generate password for new user
local_action: shell pwgen -s -N 1 20
register: user_password
with_items: "{{ users }}"
run_once: true
- name: Generate encrypted password
local_action: shell python -c 'import crypt; print(crypt.crypt( "{{ item.stdout }}", crypt.mksalt(crypt.METHOD_SHA512)))'
register: encrypted_user_password
with_items: "{{ user_password.results }}"
run_once: true
- name: Create new user with group
user:
name: "{{ item }}"
groups: "{{ group }}"
shell: /bin/bash
append: yes
createhome: yes
comment: 'Created with ansible'
with_items:
- "{{ users }}"
register: user_created
- name: Update user Passwords
user:
name: '{{ item.0 }}'
password: '{{ item.1.stdout }}'
with_together:
- "{{ users }}"
- "{{ encrypted_user_password.results }}"
when: user_created.changed
- name: Force user to change the password at first login
shell: chage -d 0 "{{ item }}"
with_items:
- "{{ users }}"
when: user_created.changed
- name: Save Passwords Locally
become: no
local_action: copy content={{ item.stdout }} dest=./{{ item.item }}.txt
with_items: "{{ user_password.results }}"
when: user_created.changed