Criação de um novo usuário e senha com Ansible

103

Eu tenho uma tarefa ansible que cria um novo usuário no ubuntu 12.04;

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

ele é concluído conforme o esperado, mas quando eu entro como esse usuário e tento usar o sudo com a senha, eu a defino sempre diz que está incorreta. O que estou fazendo de errado?

raphael_turtle
fonte
1
Você está logando com a mesma senha ou chaves SSH? Você verificou seu arquivo shadow para ter certeza de que seu conteúdo é o esperado? Além disso, o seu passwordnão deve estar em texto simples, mas sim pré-hash.
Mxx
minha senha está em texto simples, não posso usá-la assim? Não entendo como torná-lo criptografado, ou realmente preciso fazer.
raphael_turtle

Respostas:

101

Se você ler o manual do usermódulo do Ansible , ele o direcionará para o repositório github de exemplos do Ansible para obter detalhes sobre como usar o passwordparâmetro .

Lá você verá que sua senha deve ter um 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}}

Se seu playbook ou linha de comando ansible tiver sua senha no estado em que se encontra em texto simples, isso significa que o hash de sua senha registrado no arquivo shadow está errado. Isso significa que quando você tenta autenticar com sua senha, seu hash nunca coincidirá.

Além disso, consulte as Perguntas frequentes do Ansible sobre algumas nuances do parâmetro de senha e como usá-lo corretamente.

Mxx
fonte
25
Obrigado pela dica. Eu só queria salientar que a documentação do módulo do usuário no link acima recomenda o uso de openssl passwd -salt <salt> -1 <plaintext>para gerar o hash de senha, em vez do Python one-liner que você tem acima. Tive alguns problemas para obter a saída correta do Python, provavelmente devido à minha própria incompetência e o comando openssl funcionou melhor.
Brendan Wood
6
Como você sincroniza o salt a ser usado com o SO?
Breedly
5
@Breedly: não há necessidade - o salt é sempre armazenado como parte da senha ($ 1 $ thesalt $ thepasswordhash), o que significa que é portátil entre sistemas operacionais usando a mesma função hash
Benjamin Dale
3
Usar este comando python para gerar um hash não funcionou para mim. Mas eu encontrei o hash /etc/shadowdepois de definir a senha manualmente usando passwd <user>.
dokaspar
172

Posso ser tarde demais para responder, mas recentemente descobri que os filtros jinja2 têm a capacidade de lidar com a geração de senhas criptografadas. No meu main.yml, estou gerando a senha criptografada 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" e "upassword" são passados --extra-varspara o manual e observe que usei o filtro jinja2 aqui para criptografar a senha passada.

Eu adicionei o tutorial abaixo relacionado a isso no meu blog

monstro pensante
fonte
6
Para evitar que o item seja sempre "alterado", você pode adicionar um "sal" secreto como segundo parâmetro para password_hash.
Michael Wyraz
11
De acordo com a sugestão de @MichaelWyraz: adicionar um segundo parâmetro "sal" evita "alterado". Você pode definir isso por meio de uma variável, por exemplo password={{upassword|password_hash('sha512', upassword_salt)}}. Isso permite que você coloque salt em um cofre de variáveis , como você provavelmente faria com upasswordtambém, mantendo ambos fora de tasks.yml.
user85461 de
Eu também aplicaria a verificação de estado de @bbaassssiiee e adicionaria update_password: on_createo módulo de usuário a esta resposta para evitar que as senhas expirem para usuários já criados.
madhead
Obrigado pelo seu ótimo exemplo, isso me trouxe no caminho certo. No entanto, eu tive que fazer mais algumas coisas para fazê-lo funcionar em um mac com ansible versão 2.8.2. Em primeiro lugar, em um mac não é possível usar crypt, portanto, é necessário instalar a biblioteca passlib com pip install passlib. Então, para ser capaz de usar um vault cifrado corda em linha eu tive que reformatar com o seguinte aditamento: password: {{ upassword | string | password_hash('sha512') }}. Isso evita a mensagem de errosecret must be unicode or bytes, not ansible.parsing.yaml.objects.AnsibleVaultEncryptedUnicode
Michael Aicher
apesar de usar este método para definir uma senha: "{{my_password | string | password_hash ('sha512')}}" Eu ainda vou obter - [AVISO]: A senha de entrada parece não ter sido hash. O argumento 'senha' deve ser criptografado para que este módulo funcione corretamente.
openCivilisation
46

Quero propor mais uma solução:

- 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 que é melhor? Como já foi observado aqui, as jogadas de Ansible devem ser idempotentes. Você deve pensar neles não como uma sequência de ações em estilo imperativo, mas como um estado desejado, estilo declarativo. Como resultado, você deve ser capaz de executá-lo várias vezes e obter o mesmo resultado, o mesmo estado do servidor.

Tudo isso parece ótimo, mas existem algumas nuances. Um deles é o gerenciamento de usuários. "Estado desejado" significa que toda vez que você executar uma jogada que crie um usuário, ele será atualizado para corresponder exatamente a esse estado. Por "atualizado" quero dizer que sua senha também será alterada. Mas provavelmente não é o que você precisa. Normalmente, você precisa criar um usuário, definir e expirar sua senha apenas uma vez, outras execuções de jogo não devem atualizar sua senha.

Felizmente, o Ansible possui um update_passwordatributo no usermódulo que resolve esse problema. Misturando isso com variáveis ​​registradas, você também pode expirar sua senha somente quando o usuário estiver realmente atualizado.

Observe que se você alterar o shell do usuário manualmente (suponha que você não goste do shell que o administrador malvado forçou em seu jogo), o usuário será atualizado, portanto, sua senha expirará.

Observe também como você pode usar facilmente senhas iniciais em texto simples em jogos. Não há necessidade de codificá-los em outro lugar e colar hashes, você pode usar o filtro Jinja2 para isso. No entanto, isso pode ser uma falha de segurança se alguém fizer o login antes de você.

cabeça louca
fonte
3
Não consegui fazer a outra solução funcionar para mim. No entanto, sua resposta é simples e funcionou. Eu gostaria de lhe dar 5 votos positivos, se pudesse.
leesei
Não quero que minha senha seja codificada assim :( É possível retirá-la do ansible-vault e injetá-la aqui? Aninhar como "{{ '{{vaulted_password}}' | password_hash('sha512') }}"não parece funcionar ...
dokaspar
Você já tentou {{ vaulted_password | password_hash('sha512') }}, onde vaulted_passwordestá a chave para o valor no cofre?
madhead
update_password: on_createnão parece trabalho (há um bug aberto sobre isso a partir de 2017), então as senhas vai mudar qualquer vez que há uma mudança de estado de um usuário.
Diti
11

O módulo 'usuário' do Ansible gerencia os usuários de forma idempotente . No manual abaixo, a primeira tarefa declara state = present para o usuário. Observe que ' registrar: novo usuário' na primeira ação ajuda a segunda ação a determinar se o usuário é novo (newuser.changed == True) ou existente ( newuser.changed==False), para gerar a senha apenas uma vez.

O manual do Ansible contém:

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
fonte
Apenas uma solução funcionando para mim com a senha do vault. Muito obrigado.
Denis Savenko
pelo menos distribuições recentes baseadas em Debian não parecem suportar a opção GNU longa "--stdin" para o binário passwd.
XXL de
10

tente assim

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
fonte
2
Gosto desta solução visto que permite digitar uma senha quando o script é executado. Ótima solução para um script de bootstrap que não deve ser executado sempre. No entanto, para o Ubuntu, usei "sha512_crypt".
Gunnar
6

O objetivo da função nesta resposta é gerar uma senha aleatória para new_user_name e expirar a senha imediatamente. O new_user_name é necessário para alterar a senha em seu primeiro 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

Quando você deseja criar um novo usuário:

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

Esta é a maneira mais 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
fonte
shell: sempre fará com que uma alteração seja relatada.
bbaassssiiee
@datasmid você pode adicionar o no_log: True option docs.ansible.com/ansible/…
Phill Pafford
3

É assim que funcionou para mim

- 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
fonte
3

Apenas para completar, postarei o comando ad-hoc usando ansible, pois também há um problema.

Primeiro, tente gerar uma senha criptografada usando o utilitário mkpasswd que está disponível na maioria dos sistemas Linux:

mkpasswd --method=SHA-512

Em seguida, tente o comando ansible ad-hock:

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

Mas certifique-se:

  1. O comando está entre aspas simples e NÃO o dobro, caso contrário sua senha nunca funcionará
  2. Você o executa --sudoou obtém um erro como ( useradd: cannot lock /etc/passwd; try again later)
pgaref
fonte
obrigado companheiro, eu estava procurando especificamente pela versão adhoc e tive esse mesmo problema de citação que você mencionou
alexakarpov
2

A definição da tarefa para o módulo de usuário deve ser diferente na versão mais recente do Ansible.

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

Combinando algumas soluções acima, criei um manual que gera hashes de senha corretos automaticamente com base em senhas de texto simples armazenadas em um arquivo de cofre ansible local criptografado:

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

Execute este comando usando a opção "--ask-vault-pass" para descriptografar o arquivo do vault (consulte ansible-vault para obter informações sobre como gerenciar um vault criptografado).

Steve Midgley
fonte
2

Como criar uma senha criptografada para passar para passworda usertarefa var para Ansible (do comentário de @Brendan Wood):

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

O resultado será semelhante a:

$1$some_pla$lmVKJwdV3Baf.o.F0OOy71

Exemplo de usertarefa:

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

UPD: crypt using SHA-512 veja aqui e aqui :

Pitão

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

Rubi

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

$6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
Dmitriy
fonte
1
Isso não está gerando uma senha com hash MD5? Isso não é inseguro?
Marque
2

Você pode usar o ansible-vault para usar chaves secretas em manuais. Defina sua senha em yml.

ex. passar: segredo ou

user:
  pass: secret
  name: fake

criptografe seu arquivo de segredos com:

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

ansible pedirá uma senha para criptografá-lo. (vou explicar como usar esse passe)

E então você pode usar suas variáveis ​​onde quiser. Ninguém pode lê-los sem a chave do cofre.

Uso da chave do Vault:

passando o argumento ao executar o manual.

--ask-vault-pass: secret

ou você pode salvar em um arquivo como password.txt e se esconder em algum lugar. (útil para usuários de CI)

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

No seu caso: inclua vars yml e use suas variáveis.

- include_vars: /path/credential.yml

  - name: Add deployment user
    action: user name={{user.name}} password={{user.pass}}
pmoksuz
fonte
Eu tentei exatamente isso e não funcionou. Acredito que seja porque password={{user.pass}}irá expandir para incluir a senha real, enquanto o ansible espera o hash lá.
texnic
2

Gerando senha aleatória para o usuário

primeiro precisa definir a variável de usuários e seguir abaixo

tarefas:

- 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
fonte
1

A resposta de Mxx está correta, mas você o crypt.crypt()método python não é seguro quando diferentes sistemas operacionais estão envolvidos (relacionado ao algoritmo glibc hash usado em seu sistema).

Por exemplo, não funcionará se você gerar seu hash a partir do MacOS e executar um manual no Linux. Nesse caso, você pode 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
fonte
1

Nenhuma das soluções funcionou diretamente no meu Mac controlando o Ubuntu. Portanto, para o bem dos outros, combinando as respostas Mxx e JoelB, aqui está a solução Python 3 atual:

pip3 install passlib

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

O resultado será $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI., como na resposta de Mxx '.

Melhor ainda , use SHA512 em vez de MD5:

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

Resultado:

$ 6 $ rounds = 656000 $ SomeSalt $ oYpmnpZahIsvn5FK8g4bDFEAmGpEN114Fe6Ko4HvinzFaz5Rq2UXQxoJZ9ZQyQoi9zaBo3gBH / FEAov3FHv48

texnico
fonte
1

Eu criei um ansible-playbook que permite criar uma conta Linux que permite autenticação por senha.

Consulte CreateLinuxAccountWithAnsible .

A senha com hash é gerada usando o mkpasswdcomando. Eu forneci maneiras de instalar mkpasswdem diferentes sistemas operacionais.

Aqui estão as etapas necessárias para usar meu script:

  1. Substitua <your_user_name>e <your_password>dentro run.shcom seu nome de usuário e senha desejados.

  2. Altere as informações de conexão inventorypara que o ansible possa se conectar à máquina para criar um usuário.

  3. Execute ./run.shpara executar o script.

Brian
fonte
1
O arquivo não está mais no GitHub.
Janus
0

Se desejar fazer isso como um comando ad-hoc Ansible, você pode fazer o seguinte:

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

Resultado do comando acima:

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
fonte
0

Sei que estou atrasado para a festa, mas há outra solução que estou usando. Pode ser útil para distros que não possuem o --stdinbinário 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_controlé responsável por imprimir apenas o nome de usuário. Todo o manual ou apenas as variáveis ​​do usuário (você pode usar vars_files:) devem ser criptografados com ansible-vault.

Alex Baranowski
fonte
0

Minha solução é usar lookup e gerar senha automaticamente.

---
- 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
fonte
0

Tentei muitos utilitários, incluindo mkpasswd, Python etc. Mas parece que há algum problema de compatibilidade com o Ansible na leitura de valores HASH gerados por outras ferramentas. Finalmente funcionou pelo próprio valor do ansible #.

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

Livro de cantadas -

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

Bem, estou totalmente atrasado para a festa :) Eu precisava do ansible play que cria vários usuários locais com senhas aleatórias. Foi isso que eu inventei, usei alguns dos exemplos do início e os juntei com algumas mudanças.

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
Juhani
fonte