Creando un nuevo usuario y contraseña con Ansible

103

Tengo una tarea ansible que crea un nuevo usuario en ubuntu 12.04;

- name: Add deployment user
    action: user name=deployer password=mypassword

se completa como se esperaba, pero cuando inicio sesión como ese usuario e intento sudo con la contraseña que configuré, siempre dice que es incorrecta. ¿Qué estoy haciendo mal?

raphael_turtle
fuente
1
¿Está iniciando sesión con la misma contraseña o claves ssh? ¿Verificó su archivo de sombra para asegurarse de que su contenido sea el esperado? Además, passwordse supone que su no debe estar en texto sin formato, sino más bien preash.
Mxx
mi contraseña es texto sin formato, ¿no puedo usarla de esa manera? No entiendo cómo cifrarlo, o tampoco lo necesito.
raphael_turtle

Respuestas:

101

Si lee el manual de Ansible para el usermódulo , lo dirigirá al repositorio de github de Ansible-examples para obtener detalles sobre cómo usar el passwordparámetro .

Allí verá que su contraseña debe tener hash.

- 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}}

Si su libro de jugadas o línea de comando ansible tiene su contraseña tal como está en texto sin formato, esto significa que el hash de su contraseña está registrado en su sombra archivo de es incorrecto. Eso significa que cuando intentas autenticarte con tu contraseña, su hash nunca coincidirá.

Además, consulte las preguntas frecuentes de Ansible sobre algunos matices del parámetro de contraseña y cómo usarlo correctamente.

Mxx
fuente
25
Gracias por el consejo. Solo quería señalar que la documentación del módulo de usuario vinculada anteriormente recomienda usar openssl passwd -salt <salt> -1 <plaintext>para generar el hash de la contraseña, en lugar del Python one-liner que tiene arriba. Tuve algunos problemas para obtener la salida correcta de Python, probablemente debido a mi propia incompetencia y el comando openssl funcionó mejor.
Brendan Wood
6
¿Cómo se sincroniza la sal para utilizarla con el sistema operativo?
Breedly
5
@Breedly: no es necesario: la sal siempre se almacena como parte de la contraseña ($ 1 $ thesalt $ thepasswordhash), lo que significa que es portátil entre sistemas operativos que usan la misma función hash
Benjamin Dale
3
El uso de este comando de Python para generar un hash no funcionó para mí. Pero encontré el hash /etc/shadowdespués de configurar la contraseña manualmente usando passwd <user>.
dokaspar
172

Puede que sea demasiado tarde para responder esto, pero recientemente descubrí que los filtros jinja2 tienen la capacidad de manejar la generación de contraseñas cifradas. En mi main.ymlestoy generando la contraseña cifrada como:

- 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" y "upassword" se pasan como --extra-varsen el libro de jugadas y observe que he usado el filtro jinja2 aquí para encriptar la contraseña pasada.

He agregado el siguiente tutorial relacionado con esto en mi blog.

monstruo de pensamiento
fuente
6
Para evitar que el elemento siempre se "cambie", puede agregar una "sal" secreta como segundo parámetro a password_hash.
Michael Wyraz
11
Según la sugerencia de @ MichaelWyraz: agregar un segundo parámetro de "sal" evita "cambiar". Puede configurar esto a través de una variable, por ejemplo password={{upassword|password_hash('sha512', upassword_salt)}}. Eso le permite poner sal en una bóveda de variables , como presumiblemente también lo haría upassword, manteniendo a ambos fuera de tasks.yml.
user85461
También aplicaría la verificación de estado de @ bbaassssiiee y agregaría un update_password: on_createmódulo de usuario a esta respuesta para evitar que expiren las contraseñas de los usuarios ya creados.
loco
Gracias por tu gran ejemplo, esto me llevó por buen camino. Sin embargo, tuve que hacer algunas cosas más para que funcionara en un mac con ansible versión 2.8.2. En primer lugar, en un mac no es posible utilizar crypt, por lo que es necesario instalar la biblioteca passlib con pip install passlib. Luego de ser capaz de utilizar una bóveda encriptada cadena de línea que tenía que cambiar el formato con la siguiente adición: password: {{ upassword | string | password_hash('sha512') }}. Esto evita el mensaje de errorsecret must be unicode or bytes, not ansible.parsing.yaml.objects.AnsibleVaultEncryptedUnicode
Michael Aicher
a pesar de usar este método para establecer una contraseña: "{{my_password | string | password_hash ('sha512')}}" Aún obtendré - [ADVERTENCIA]: La contraseña de entrada parece no haber sido hash. El argumento 'contraseña' debe estar cifrado para que este módulo funcione correctamente.
openCivilisation
46

Quiero proponer otra solución más:

- 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

¿Por qué es mejor? Como ya se ha señalado aquí, las obras de Ansible deben ser idempotentes. Debe pensar en ellos no como una secuencia de acciones en estilo imperativo, sino como un estado deseado, estilo declarativo. Como resultado, debería poder ejecutarlo varias veces y obtener el mismo resultado, el mismo estado del servidor.

Todo esto suena muy bien, pero hay algunos matices. Uno de ellos es la gestión de usuarios. "Estado deseado" significa que cada vez que ejecutas una jugada que crea un usuario, se actualizará para que coincida exactamente con ese estado. Por "actualizado" me refiero a que su contraseña también se cambiará. Pero lo más probable es que no sea lo que necesitas. Por lo general, necesita crear un usuario, establecer y caducar su contraseña solo una vez, más ejecuciones de juego no deberían actualizar su contraseña.

Afortunadamente, Ansible tiene un update_passwordatributo en el usermódulo que resuelve este problema. Mezclando esto con variables registradas , también puede caducar su contraseña solo cuando el usuario esté realmente actualizado.

Tenga en cuenta que si cambia el shell del usuario manualmente (suponga que no le gusta el shell que el administrador malvado forzó en su juego), el usuario se actualizará, por lo que su contraseña expirará.

También tenga en cuenta cómo puede usar fácilmente las contraseñas iniciales de texto sin formato en las obras de teatro. No es necesario codificarlos en otro lugar y pegar hashes, puede usar el filtro Jinja2 para eso. Sin embargo, esto puede ser una falla de seguridad si alguien inicia sesión antes que usted inicialmente.

loco
fuente
3
No pude conseguir que la otra solución me funcionara. Sin embargo, su respuesta es simple y trabajada. Me gustaría darte 5 votos a favor, si pudiera.
leesei
No quiero que mi contraseña esté codificada de esa manera :( ¿Es posible sacarla de ansible-vault e inyectarla aquí? Anidar como "{{ '{{vaulted_password}}' | password_hash('sha512') }}"no parece funcionar ...
dokaspar
¿Ha intentado {{ vaulted_password | password_hash('sha512') }}, dónde vaulted_passwordestá la clave del valor en la bóveda?
madhead
update_password: on_createno parece funcionar (hay un error abierto al respecto desde 2017), por lo que las contraseñas cambiarán cada vez que haya un cambio de estado en un usuario.
Diti
11

El módulo 'usuario' de Ansible gestiona usuarios, de forma idempotente . En el libro de jugadas a continuación, la primera tarea declara estado = presente para el usuario. Tenga en cuenta que ' register: newuser ' en la primera acción ayuda a la segunda acción a determinar si el usuario es nuevo (newuser.changed == True) o existente ( newuser.changed==False), para generar la contraseña solo una vez.

El libro de jugadas de Ansible tiene:

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
bbaassssiiee
fuente
Solo una solución funciona para mí con contraseña de bóveda. Muchas gracias.
Denis Savenko
al menos las distribuciones recientes basadas en Debian no parecen admitir la opción larga de GNU "--stdin" para el binario passwd.
XXL
10

prueba así

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
fuente
2
Me gusta esta solución dado que permite escribir una contraseña cuando se ejecuta el script. Buena solución para un script de arranque que no debe ejecutarse siempre. Sin embargo, para Ubuntu utilicé "sha512_crypt".
Gunnar
6

El propósito del rol en esta respuesta es generar una contraseña aleatoria para new_user_name y caducar la contraseña inmediatamente. Se requiere new_user_name para cambiar la contraseña en su primer inicio de sesión.

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

Cuando desee crear un nuevo usuario:

ansible-playbook -i hosts.ini create_user.yml --extra-vars "new_user_name=kelvin"
McKelvin
fuente
3

Esta es la forma fácil:

---
- 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
Iluminado
fuente
shell: siempre hará que se informe de un cambio.
bbaassssiiee
@datasmid, podría agregar la opción no_log: True docs.ansible.com/ansible/…
Phill Pafford
3

Así es como me funcionó

- 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
hecbuma
fuente
3

Solo para completar, publicaré el comando ad-hoc usando ansible, ya que también hay un problema.

Primero intente generar una contraseña encriptada usando la utilidad mkpasswd que está disponible en la mayoría de los sistemas Linux:

mkpasswd --method=SHA-512

Luego prueba el comando ansible ad-hock:

ansible all -m user -a 'name=testuser shell=/bin/bash \
     comment="Test User" password=$6$XXXX' -k -u admin --sudo

Pero asegúrate:

  1. El comando está entre comillas simples y NO el doble, de lo contrario su contraseña nunca funcionará
  2. Lo ejecutas con --sudoo terminas con un error como ( useradd: cannot lock /etc/passwd; try again later)
pgaref
fuente
gracias amigo, estaba buscando específicamente la versión ad hoc, y tuve el mismo problema de citas que mencionaste
alexakarpov
2

La definición de tareas para el módulo de usuario debe ser diferente en la última versión de Ansible.

tasks:
  - user: name=test password={{ password }} state=present
Christian Berendt
fuente
2

Combinando algunas soluciones anteriores, creé un libro de jugadas que genera automáticamente hashes de contraseña correctos basados ​​en contraseñas de texto sin formato almacenadas en un archivo de bóveda ansible local cifrado:

---
- 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}}"

Ejecute este comando usando la opción "--ask-vault-pass" para descifrar su archivo de bóveda (consulte ansible-vault para obtener información sobre cómo administrar una bóveda cifrada).

Steve Midgley
fuente
2

Cómo crear una contraseña encriptada para pasar a passwordvar a la usertarea de Ansible (del comentario de @Brendan Wood):

openssl passwd -salt 'some_plain_salt' -1 'some_plain_pass'

El resultado se verá así:

$1$some_pla$lmVKJwdV3Baf.o.F0OOy71

Ejemplo de usertarea:

- name: Create user
  user: name="my_user" password="$1$some_pla$lmVKJwdV3Baf.o.F0OOy71"

UPD: cripta con SHA-512, consulte aquí y aquí :

Pitón

$ 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/

Rubí

$ ruby -e 'puts "password".crypt("$6$saltsalt$")'

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Dmitriy
fuente
1
¿No está generando una contraseña hash md5? ¿No es eso inseguro?
Mark
2

Puede usar ansible-vault para usar claves secretas en los libros de jugadas. Defina su contraseña en yml.

ex. pasar: secreto o

user:
  pass: secret
  name: fake

cifre su archivo de secretos con:

ansible-vault encrypt /path/to/credential.yml

ansible le pedirá una contraseña para cifrarlo. (explicaré cómo usar ese pase)

Y luego puedes usar tus variables donde quieras. Nadie puede leerlos sin clave de bóveda.

Uso de la clave de la bóveda:

a través de un argumento de paso al ejecutar el libro de jugadas.

--ask-vault-pass: secret

o puede guardar en un archivo como password.txt y esconderse en algún lugar. (útil para usuarios de CI)

--vault-password-file=/path/to/file.txt

En su caso: incluya vars yml y use sus variables.

- include_vars: /path/credential.yml

  - name: Add deployment user
    action: user name={{user.name}} password={{user.pass}}
pmoksuz
fuente
He intentado exactamente esto y no funciona. Creo que es porque password={{user.pass}}se expandirá para incluir la contraseña real, mientras que ansible espera el hash allí.
texnic
2

Generando contraseña aleatoria para el usuario

primero es necesario definir la variable de usuarios y luego seguir a continuación

Tareas:

- 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 }}'
Apuri Srikanth
fuente
1

La respuesta de Mxx es correcta pero tú, la pitón. crypt.crypt() método no es seguro cuando están involucrados diferentes sistemas operativos (relacionado con el algoritmo hash glibc utilizado en su sistema).

Por ejemplo, no funcionará si genera su hash desde MacOS y ejecuta un libro de jugadas en Linux. En tal caso, puede usar passlib ( pip install passlibpara instalar localmente).

from passlib.hash import md5_crypt
python -c 'import crypt; print md5_crypt.encrypt("This is my Password,salt="SomeSalt")'
'$1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.'
Joel B
fuente
1

Ninguna de las soluciones funcionó directamente en mi Mac que controlaba Ubuntu. Entonces, por el bien de los demás, combinando las respuestas de Mxx y JoelB, aquí está la solución actual de Python 3:

pip3 install passlib

python3 -c 'from passlib.hash import md5_crypt; \
      print(md5_crypt.encrypt("This is my Password", salt="SomeSalt"))'

El resultado será $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI., como en la respuesta de Mxx.

Mejor aún , use SHA512 en lugar de MD5:

python3 -c 'from passlib.hash import sha512_crypt; \
      print(sha512_crypt.encrypt("This is my Password", salt="SomeSalt"))' 

Resultado:

$ 6 $ rondas = 656000 $ SomeSal $ oYpmnpZahIsvn5FK8g4bDFEAmGpEN114Fe6Ko4HvinzFaz5Rq2UXQxoJZ9ZQyQoi9zaBo3gBH / FEAov3FHv48

texnic
fuente
1

He creado un libro de jugadas ansible que le permite crear una cuenta de Linux que permite la autenticación de contraseña.

Consulte CreateLinuxAccountWithAnsible .

La contraseña hash se genera usando el mkpasswdcomando. He proporcionado las formas de instalar mkpasswden diferentes sistemas operativos.

Estos son los pasos necesarios para usar mi script:

  1. Reemplace <your_user_name>y <your_password>adentro run.shcon su nombre de usuario y contraseña deseados.

  2. Cambie la información de conexión inventorypara que ansible pueda conectarse a la máquina para crear un usuario.

  3. Ejecutar ./run.shpara ejecutar el script.

Brian
fuente
1
El archivo ya no está en GitHub.
Janus
0

Si desea lograr esto como un comando ad-hoc de Ansible, puede hacer lo siguiente:

$ 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') }}\""

Salida del comando anterior:

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
}
slm
fuente
0

Sé que llego tarde a la fiesta, pero hay otra solución que estoy usando. Puede ser útil para las distribuciones que no tienen el --stdinbinario passwd.

- 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_controles responsable de imprimir solo el nombre de usuario. Todo el libro de jugadas o solo las variables de usuario (que puede usar vars_files:) deben estar encriptadas con ansible-vault.

Alex Baranowski
fuente
0

Mi solución es usar la búsqueda y generar una contraseña automáticamente.

---
- hosts: 'all'
  remote_user: root
  gather_facts: no
  vars:
    deploy_user: deploy
    deploy_password: "{{ lookup('password', '/tmp/password chars=ascii_letters') }}"

  tasks:
    - name: Create deploy user
      user:
        name: "{{ deploy_user }}"
        password: "{{ deploy_password | password_hash('sha512') }}"
alex naumov
fuente
0

Probé muchas utilidades, incluidas mkpasswd, Python, etc. Pero parece que hay algún problema de compatibilidad con Ansible al leer los valores HASH generados por otras herramientas. Así que finalmente funcionó con ansible # value.

ansible all -i localhost, -m debug -a "msg = {{'yourpasswd' | password_hash ('sha512', 'mysecretsalt')}}"

Libro de jugadas -

- name: User creation
  user: 
    name: username  
    uid: UID
    group: grpname
    shell: /bin/bash
    comment: "test user"
    password: "$6$mysecretsalt$1SMjoVXjYf.3sJR3a1WUxlDCmdJwC613.SUD4DOf40ASDFASJHASDFCDDDWERWEYbs8G00NHmOg29E0"
DjangoChained
fuente
-1

Bueno, llegué tarde a la fiesta :) Tenía la necesidad de un juego ansible que creara múltiples usuarios locales con contraseñas aleatorias. Esto es lo que se me ocurrió, utilicé algunos de los ejemplos de arriba y los combiné con algunos cambios.

crear-usuario-con-contraseña.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
Juhani
fuente