【问题标题】:Creating a new user and password with Ansible使用 Ansible 创建新用户和密码
【发布时间】:2013-10-18 01:03:13
【问题描述】:

我有一个在 ubuntu 12.04 上创建新用户的 ansible 任务;

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

它按预期完成,但是当我以该用户身份登录并尝试使用我设置的密码 sudo 时,它总是说它不正确。我做错了什么?

【问题讨论】:

  • 您是否使用相同的密码或 ssh 密钥登录?您是否检查了影子文件以确保其内容符合预期?此外,您的 password 不应该是纯文本,而是预先散列。
  • 我的密码是明文的,不能这样用吗?我不明白如何加密,或者真的需要。

标签: bash shell ubuntu ansible


【解决方案1】:

我可能来不及回复这个问题,但最近我发现 jinja2 过滤器能够处理加密密码的生成。在我的main.yml 中,我将加密密码生成为:

- 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”和“uppassword”作为--extra-vars传递给剧本,注意我在这里使用了jinja2过滤器来加密传递的密码。

我已将以下与此相关的教程添加到我的博客中

【讨论】:

  • 为避免该项目总是“更改”,您可以添加一个秘密“盐”作为密码哈希的第二个参数。
  • 根据@MichaelWyraz 的建议:添加第二个“salt”参数可以避免“更改”。您可以通过变量设置它,例如password={{upassword|password_hash('sha512', upassword_salt)}}。这让您可以在 variables vault 中添加盐,就像您可能对 upassword 一样,将两者都排除在 tasks.yml 之外。
  • 我还将应用@bbaassssiiee 的状态检查并将update_password: on_create 在用户模块中添加到此答案中,以防止已创建用户的密码过期。
  • 感谢您的出色示例,这使我走上了正轨。尽管如此,我不得不做更多的事情来让它在带有 ansible 版本 2.8.2 的 mac 上工作。首先,在 Mac 上无法使用 crypt,因此需要使用 pip install passlib 安装 passlib 库。然后为了能够使用内联保险库加密字符串,我必须重新格式化并添加以下内容:password: {{ upassword | string | password_hash('sha512') }}。这避免了错误消息secret must be unicode or bytes, not ansible.parsing.yaml.objects.AnsibleVaultEncryptedUnicode
  • 尽管使用这种方法设置密码:"{{ my_password | string | password_hash('sha512') }}" 我仍然会得到 - [警告]:输入密码似乎不是散列。必须对“密码”参数进行加密,此模块才能正常工作。
【解决方案2】:

如果您阅读了 user module 的 Ansible 手册,它会将您定向到 Ansible 示例 github repo,了解如何使用 password parameter 的详细信息。

您会看到您的密码必须经过哈希处理。

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

如果你的 playbook 或 ansible 命令行的密码是纯文本的,这意味着你的 shadow 文件中记录的密码哈希是错误的。这意味着当您尝试使用您的密码进行身份验证时,其哈希值将永远不会匹配。

此外,请参阅 Ansible FAQ,了解密码参数的一些细微差别以及如何正确使用它。

【讨论】:

  • 感谢您的提示。我只是想指出,上面链接的用户模块文档建议使用openssl passwd -salt <salt> -1 <plaintext> 来生成密码哈希,而不是上面的 Python 单行。我在从 Python 获得正确输出时遇到了一些麻烦,可能是由于我自己的无能,而 openssl 命令效果更好。
  • 如何同步使用操作系统的盐?
  • @Breedly:没有必要——盐总是作为密码的一部分存储($1$thesalt$thepasswordhash),这意味着它可以使用相同的散列函数在操作系统之间移植
  • 使用此 python 命令生成哈希对我不起作用。但是在使用passwd <user>手动设置密码后,我在/etc/shadow中找到了哈希。
【解决方案3】:

我想提出另一个解决方案:

- 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

为什么更好?就像这里已经提到的那样,Ansible 游戏应该是幂等的。您不应将它们视为命令式风格的一系列动作,而应将其视为所需的状态、声明式风格。因此,您应该能够多次运行它并获得相同的结果,相同的服务器状态。

这一切听起来都很棒,但也有一些细微差别。其中之一是管理用户。 “所需状态”意味着每次运行创建用户的游戏时,他都会更新以完全匹配该状态。 “更新”是指他的密码也将被更改。但很可能这不是你所需要的。通常,您只需要创建用户、设置密码和过期密码一次,以后的游戏运行不应该更新他的密码。

幸运的是,Ansible 在user module 中有update_password 属性,可以解决这个问题。将此与registered variables 混合使用,您也可以仅在用户实际更新时才使他的密码过期。

请注意,如果您手动更改用户的shell(假设您不喜欢邪恶管理员在他的游戏中强加的shell),用户将被更新,因此他的密码将过期。

还要注意如何在剧本中轻松使用纯文本初始密码。无需在其他地方对它们进行编码并粘贴哈希,您可以使用Jinja2 filter。但是,如果有人在您最初​​登录之前碰巧登录,这可能是一个安全漏洞。

【讨论】:

  • 我无法让其他解决方案为我工作。然而你的回答很简单而且有效。如果可以的话,我想给你 5 票。
  • 我不希望我的密码被这样硬编码 :( 是否可以将其从 ansible-vault 中取出并注入到这里?像"{{ '{{vaulted_password}}' | password_hash('sha512') }}" 这样的嵌套似乎没有上班……
  • 您是否尝试过{{ vaulted_password | password_hash('sha512') }},其中vaulted_password 是保险库中值的关键?
  • update_password: on_create 似乎不起作用(从 2017 年开始有一个 open bug about it),因此密码会在用户状态发生变化时随时更改。
  • 幂等性。这就是我们首先使用 Ansible 的原因。
【解决方案4】:

Ansible 'user' 模块以 幂等 方式管理用户。在下面的剧本中,第一个任务为用户声明 state=present。请注意,第一个操作中的 'register: newuser' 有助于第二个操作确定用户是新用户 (newuser.changed==True) 还是现有用户 (newuser.changed==False),仅生成密码一次。

Ansible 剧本有:

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

【讨论】:

  • 只有一个解决方案适用于我的保险库密码。非常感谢。
  • 至少最近基于 Debian 的发行版似乎不支持 passwd 二进制文件的长 GNU 选项“--stdin”。
【解决方案5】:

这样试试

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"

【讨论】:

  • 我喜欢这个解决方案,因为它允许在脚本运行时输入密码。一个不应该每次都运行的引导脚本的好解决方案。不过,对于 Ubuntu,我使用了“sha512_crypt”。
【解决方案6】:

此答案中角色的目的是为 new_user_name 生成随机密码并立即使密码过期。新用户名 首次登录时需要更改密码。

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

当你想创建一个新用户时:

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

【讨论】:

    【解决方案7】:

    我尝试了许多实用程序,包括mkpasswd、Python 等,但在读取其他工具生成的 HASH 值时,Ansible 似乎存在一些兼容性问题。所以最后它由 Ansible # 值本身起作用。

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

    剧本:

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

    【讨论】:

      【解决方案8】:

      这是最简单的方法:

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

      【讨论】:

      【解决方案9】:

      这对我来说是这样的

      - 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
      

      【讨论】:

        【解决方案10】:

        为了完整起见,我将使用 ansible 发布临时命令,因为那里也有一个问题。

        首先尝试使用大多数 Linux 系统上可用的 mkpasswd 实用程序生成加密密码:

        mkpasswd --method=SHA-512
        

        然后尝试 ansible ad-hock 命令:

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

        但请确保:

        1. 该命令是单引号,不能双引号,否则您的密码将永远无法使用
        2. 您使用 --sudo 运行它,或者您最终会遇到类似 (useradd: cannot lock /etc/passwd; try again later) 之类的错误

        【讨论】:

        • 谢谢伙计,我专门找临时版本,遇到了你提到的同样的引用问题
        【解决方案11】:

        为用户生成随机密码

        首先需要定义用户变量,然后按照下面的操作

        任务:

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

        【讨论】:

          【解决方案12】:

          我的解决方案是使用查找并自动生成密码。

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

          【讨论】:

            【解决方案13】:

            user module 的任务定义在最新的 Ansible 版本中应该有所不同。

            tasks:
              - user: name=test password={{ password }} state=present
            

            【讨论】:

              【解决方案14】:

              结合上面的几个解决方案,我创建了一个剧本,它根据存储在加密的本地 ansible 保险库文件中的明文密码自动生成正确的密码哈希:

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

              使用“--ask-vault-pass”选项运行此命令以解密您的保管库文件(有关如何管理加密保管库的信息,请参阅 ansible-vault)。

              【讨论】:

                【解决方案15】:

                如何创建加密密码以传递给password var 到 Ansible user 任务(来自@Brendan Wood 的评论):

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

                结果将如下所示:

                $1$some_pla$lmVKJwdV3Baf.o.F0OOy71
                

                user 任务示例:

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

                UPD:使用 SHA-512 进行加密,请参阅 herehere

                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 -e 'puts "password".crypt("$6$saltsalt$")'
                
                $6$saltsalt$qFmFH.bQmmtXzyBY0s9v7Oicd2z4XSIecDzlB5KiA2/jctKu9YterLp8wwnSq.qc.eoxqOmSuNp2xS0ktL3nh/
                

                【讨论】:

                • 这不是生成md5散列密码吗?这不是不安全吗?
                【解决方案16】:

                您可以使用 ansible-vault 在 playbook 中使用密钥。在 yml 中定义您的密码。

                例如。通行证:秘密 或

                user:
                  pass: secret
                  name: fake
                

                加密你的秘密文件:

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

                ansible 将询问密码以对其进行加密。 (我将解释如何使用该通行证)

                然后你可以在你想要的地方使用你的变量。没有保管库密钥,任何人都无法阅读它们。

                保管库密钥用法:

                在运行 playbook 时通过传递参数。

                --ask-vault-pass: secret
                

                或者您可以将其保存到 password.txt 之类的文件中并隐藏在某处。 (对 CI 用户有用)

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

                在你的情况下: 包含 vars yml 并使用您的变量。

                - include_vars: /path/credential.yml
                
                  - name: Add deployment user
                    action: user name={{user.name}} password={{user.pass}}
                

                【讨论】:

                • 我已经尝试过了,但它不起作用。我相信,这是因为 password={{user.pass}} 将扩展为包含实际密码,而 ansible 期望那里有哈希。
                【解决方案17】:

                Mxx 的回答是正确的,但是当涉及到不同的操作系统时,您的 python crypt.crypt() 方法并不安全(与您系统上使用的 glibc 哈希算法有关。)

                例如,如果您从 MacOS 生成哈希并在 linux 上运行 playbook,它将无法工作。在这种情况下,您可以使用 passlib(pip install passlib 本地安装)。

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

                【讨论】:

                  【解决方案18】:

                  这两种解决方案都不能直接在我控制 Ubuntu 的 Mac 上运行。因此,为了其他人,结合 Mxx 和 JoelB 的答案,这是当前的 Python 3 解决方案:

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

                  结果将是$1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.,如 Mxx 的回答。

                  Better still,使用 SHA512 而不是 MD5:

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

                  结果:

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

                  【讨论】:

                    【解决方案19】:

                    如果您想以 Ansible ad-hoc 命令的形式完成此操作,您可以执行以下操作:

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

                    上述命令的输出:

                    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
                    }
                    

                    【讨论】:

                      【解决方案20】:

                      我知道我迟到了,但我正在使用另一种解决方案。对于在 passwd 二进制文件中没有 --stdin 的发行版可能会很方便。

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

                      loop_control 中的标签负责打印用户名。整个剧本或只是用户变量(您可以使用vars_files:)应该使用 ansible-vault 加密。

                      【讨论】:

                        【解决方案21】:

                        我创建了一个 ansible-playbook,它允许您创建一个允许密码验证的 linux 帐户。

                        AnsibleLinuxAccountCreator

                        哈希密码是使用mkpasswd 命令生成的。我已经提供了在不同操作系统上安装mkpasswd 的方法。

                        以下是使用我的脚本所需的步骤:

                        1. run.sh 中的&lt;your_user_name&gt;&lt;your_password&gt; 替换为您想要的用户名和密码。

                        2. 更改inventory中的连接信息,以便ansible可以连接机器创建用户。

                        3. 运行./run.sh 执行脚本。

                        【讨论】:

                        • 该文件已不在 GitHub 上。
                        • 我已将 github 链接更改为正确的链接。
                        【解决方案22】:

                        尽管使用 Vaulted 变量,但我无法在这里得到任何答案。因此,对于使用“受保护”密码并收到此错误的任何人:

                        "msg": "Unexpected templating type error occurred on ({{ jinja template stuff }}): expected string or bytes-like object"
                        

                        你需要使用管道'|' Vault 加密变量通过“字符串”使其工作。

                        - name: Creating user "{{ uusername }}" with admin access
                          user: 
                            name: {{ uusername }}
                            password: "{{ upassword | string | password_hash('sha512', (upassword_salt | string)) }}"
                            groups: admin append=yes
                          when:  assigned_role  == "yes"
                        

                        基于 GitHub issue #24425 的 Ansible 解决方案

                        thinkingmonsteranswer 构建代码并由michael-aicher 评论。

                        【讨论】:

                          【解决方案23】:

                          好吧,我迟到了 :) 我需要 ansible play 来创建多个具有随机密码的本地用户。这是我想出的,使用了一些顶部的例子,并将它们放在一起并进行了一些更改。

                          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
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2016-05-08
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2020-05-03
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多