ansible-collections / community.general

Ansible Community General Collection
https://galaxy.ansible.com/ui/repo/published/community/general/
GNU General Public License v3.0
833 stars 1.53k forks source link

community.general.ldap_entry fails if bind_pw is a variable from ansible_vault #8871

Closed tsgsh closed 2 months ago

tsgsh commented 2 months ago

Summary

When a string variable created using ansible-vault is used as a bind password (bind_pw:) for ldap_entry it fails with the error message

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ldap.INVALID_CREDENTIALS: {'msgtype': 97, 'msgid': 1, 'result': 49, 'desc': 'Invalid credentials', 'ctrls': []}
fatal: [hostname]: FAILED! => changed=false 
  details: '{''msgtype'': 97, ''msgid'': 1, ''result'': 49, ''desc'': ''Invalid credentials'', ''ctrls'': []}'
  msg: Cannot bind to the server.

even if the variable is correct. There appears to be a difference in how vaulted and non-vaulted string variables are presented that ldap_entry cannot parse correctly in the former case. There is a "clue" in that a debug of the vaulted variable is always presented as a literal block scalar rather than on the same line:

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.046 ****** 
ok: [alice] => 
  openldap_password: openldap_password

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.068 ****** 
ok: [alice] => 
  vault_password: |-
    openldap_password

There doesn't appear to be a workaround through maniuplating the string (I've tried adding a null string, slicing it twice and combining the slides, reversing it twice, converting to a list and taking the zeroeth entry), setting another variable to match it (including setting a fact), or even editing the vault file to place the entry on a single line instead of the literal block scalar generated by ansible-vault. None of these change the result of the above debug or stop the error from ldap-entry.

Issue Type

Bug Report

Component Name

ldap_entry

Ansible Version

$ ansible --version
[steve@trillian shared]$ ansible --version
ansible [core 2.14.14]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/steve/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/steve/.ansible/collections:/usr/local/share/ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.18 (main, Sep  7 2023, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

Community.general Version

$ ansible-galaxy collection list community.general

# /usr/share/ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 7.5.0 

Configuration

$ ansible-config dump --only-changed
CALLBACKS_ENABLED(/etc/ansible/ansible.cfg) = ['profile_tasks']
COLLECTIONS_PATHS(/etc/ansible/ansible.cfg) = ['/home/steve/.ansible/collections', '/usr/local/share/ansible/collecti>
CONFIG_FILE() = /etc/ansible/ansible.cfg
DEFAULT_ROLES_PATH(/etc/ansible/ansible.cfg) = ['/home/steve/.ansible/roles', '/usr/local/share/ansible/roles', '/usr>
DEFAULT_STDOUT_CALLBACK(/etc/ansible/ansible.cfg) = yaml
DEFAULT_VAULT_ID_MATCH(/etc/ansible/ansible.cfg) = True
(END)

OS / Environment

Control host and target are both on AlmaLinux 9.4. OpenLDAP packages are:

openldap.x86_64                                  2.6.6-3.el9
openldap-clients.x86_64                          2.6.6-3.el9
openldap-servers.x86_64                          2.6.6-3.el9

Steps to Reproduce

[steve@trillian shared]$ # Step 1: install OpenLDAP and create the vault
[steve@trillian shared]$ cat test1.yml
- name: Set up the vault 
  hosts: localhost
  gather_facts: no

  vars: 
    openldap_password: openldap_password

  tasks:

  - name: Create the password file
    copy:
      content: vault.pw
      dest: vault.pw

  - name: Create the vaulted variable
    command:
      cmd: >-
        ansible-vault
        encrypt_string
        --stdin-name vault_password
        --output test.pw.yml
        --vault-pass-file vault.pw
      stdin: "{{ openldap_password }}"

- name: Set up OpenLDAP on the target
  hosts: alice
  become: true

  tasks:

  - name: Install openldap
    dnf:
      name: openldap
      state: present

  - name: Start slapd
    systemd:
      name: slapd
      state: started

[steve@trillian shared]$ cat vault.pw 
vault.pw[steve@trillian shared]$ cat test.pw.yml 
vault_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          30373734313834613930383733323464353562383832303530383566613835386264643663643163
          6139376631326536323030666134643831653435663431640a663436356363393733623362613662
          31326438663835306536663331616333646461363037363462646532383730666664323134663033
          3663393564323065320a356661313637303430316165643430366466313338643039613234383737 
          30343732346666363966393732396139643633636332316239333964333930666231[steve@trillian shared]$
[steve@trillian shared]$ # Step 2: compare results with vault and non-vault versions of the password (requires --vault-id @vault.pw test2.yml)
[steve@trillian shared]$ cat test2.yml
- name: Test the vaulted and non-vaulted passwords
  hosts: alice 
  gather_facts: no
  become: yes
  vars:
    openldap_admin: cn=Manager,dc=example,dc=com
    openldap_password: openldap_password
    openldap_domain_dn: dc=example,dc=com
    openldap_password_hash: "{{ openldap_r_slappasswd.stdout_lines[-1] }}"  
  tasks:

  - name: Include the vaulted variable
    include_vars:
      file: test.pw.yml

  - name: Generate the slappasswd hash
    changed_when: false
    register: openldap_r_slappasswd
    expect:
      command: "slappasswd"
      responses:
        New password: "{{ openldap_password }}"
        Re-enter new password: "{{ openldap_password }}"

  - name: Block to trap the error
    block:
    - name: Set the domain suffix in the LMDB database
      community.general.ldap_attrs:
        dn: "{{ 'olcDatabase={2}mdb,cn=config' }}"
        attributes:
          olcSuffix: "{{ openldap_domain_dn }}"
        state: exact

    - name: Set the root DN and password in the LMDB database
      community.general.ldap_attrs:
        dn: "{{ 'olcDatabase={2}mdb,cn=config' }}"
        attributes:
          olcRootDN: "{{ openldap_admin }}"
          olcRootPW: "{{ openldap_password_hash }}"
        state: exact

    - debug:
        msg: "These two are ostensibly the same:"
    - debug:
        var: openldap_password
    - debug:
        var: vault_password

    - name: Attempt this with the vault password  
      community.general.ldap_entry:
        bind_dn: "{{ openldap_admin }}"
        bind_pw: "{{ vault_password }}"
        dn: "{{ openldap_domain_dn }}"
        objectClass:
        - top
        - domain

    always:

    - name: Attempt this with the insecure password  
      community.general.ldap_entry:
        bind_dn: "{{ openldap_admin }}"
        bind_pw: "{{ openldap_password }}"
        dn: "{{ openldap_domain_dn }}"
        objectClass:
        - top
        - domain

    - name: Delete this with the insecure password
      community.general.ldap_entry:
        bind_dn: "{{ openldap_admin }}"
        bind_pw: "{{ openldap_password }}"
        dn: "{{ openldap_domain_dn }}"
        objectClass:
        - top
        - domain
        state: absent

    - name: Remove root DN and password
      community.general.ldap_attrs:
        dn: "{{ 'olcDatabase={2}mdb,cn=config' }}"
        attributes:
          olcRootDN: "{{ openldap_admin }}"
          olcRootPW: "{{ openldap_password_hash }}"
        state: absent

    - name: Remove the domain suffix
      community.general.ldap_attrs:
        dn: "{{ 'olcDatabase={2}mdb,cn=config' }}"
        attributes:
          olcSuffix: "{{ openldap_domain_dn }}"
        state: absent

[steve@trillian shared]$ 

Expected Results

I expected the task named "Attempt this with the insecure password" to complete without error because the values of openldap_password and vault_password are different YAML representations of the same string and the task named "Attempt this with the insecure password" works correctly and the only difference between the two is which of those two ostensibly identical strings is used.

Actual Results

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
[steve@trillian shared]$ # Step 1: install OpenLDAP and create the vault
[steve@trillian shared]$ ansible-playbook test1.yml

PLAY [Set up the vault] **********************************************************************************************

TASK [Create the password file] **************************************************************************************
Monday 16 September 2024  15:17:43 +0100 (0:00:00.013)       0:00:00.013 ****** 
changed: [localhost]

TASK [Create the vaulted variable] ***********************************************************************************
Monday 16 September 2024  15:17:44 +0100 (0:00:00.627)       0:00:00.640 ****** 
changed: [localhost]

PLAY [Set up OpenLDAP on the target] *********************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
Monday 16 September 2024  15:17:44 +0100 (0:00:00.740)       0:00:01.381 ****** 
ok: [alice]

TASK [Install openldap] **********************************************************************************************
Monday 16 September 2024  15:17:46 +0100 (0:00:01.841)       0:00:03.223 ****** 
ok: [alice]

TASK [Start slapd] ***************************************************************************************************
Monday 16 September 2024  15:17:50 +0100 (0:00:03.535)       0:00:06.758 ****** 
ok: [alice]

PLAY RECAP ***********************************************************************************************************
alice                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost                  : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Monday 16 September 2024  15:17:50 +0100 (0:00:00.691)       0:00:07.449 ****** 
=============================================================================== 
Install openldap ---------------------------------------------------------------------------------------------- 3.54s
Gathering Facts ----------------------------------------------------------------------------------------------- 1.84s
Create the vaulted variable ----------------------------------------------------------------------------------- 0.74s
Start slapd --------------------------------------------------------------------------------------------------- 0.69s
Create the password file -------------------------------------------------------------------------------------- 0.63s
[steve@trillian shared]$ # Step 2: compare results with vault and non-vault versions of the password
[steve@trillian shared]$ ansible-playbook --vault-id @vault.pw test2.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [Test the vaulted and non-vaulted passwords] ********************************************************************

TASK [Include the vaulted variable] **********************************************************************************
Monday 16 September 2024  15:23:29 +0100 (0:00:00.013)       0:00:00.013 ****** 
ok: [alice] => changed=false 
  ansible_facts:
    vault_password: !vault |-
      $ANSIBLE_VAULT;1.1;AES256
      30373734313834613930383733323464353562383832303530383566613835386264643663643163
      6139376631326536323030666134643831653435663431640a663436356363393733623362613662
      31326438663835306536663331616333646461363037363462646532383730666664323134663033
      3663393564323065320a356661313637303430316165643430366466313338643039613234383737
      30343732346666363966393732396139643633636332316239333964333930666231
  ansible_included_var_files:
  - /srv/shares/steve/test.pw.yml

TASK [Generate the slappasswd hash] **********************************************************************************
Monday 16 September 2024  15:23:29 +0100 (0:00:00.048)       0:00:00.061 ****** 
ok: [alice] => changed=false 
  ansible_facts:
    discovered_interpreter_python: /usr/bin/python3
  cmd: slappasswd
  delta: '0:00:00.218695'
  end: '2024-09-16 15:23:30.995740'
  rc: 0
  start: '2024-09-16 15:23:30.777045'
  stdout: |-
    New password:
    Re-enter new password:
    {SSHA}LJzcQPfcsko/wea19bCHLrIcGcqM6u1K
  stdout_lines: <omitted>

TASK [Set the domain suffix in the LMDB database] ********************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:01.176)       0:00:01.238 ****** 
changed: [alice] => changed=true 
  modlist:
  - - 0
    - olcSuffix
    - - dc=example,dc=com

TASK [Set the root DN and password in the LMDB database] *************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.420)       0:00:01.659 ****** 
changed: [alice] => changed=true 
  modlist:
  - - 0
    - olcRootDN
    - - cn=Manager,dc=example,dc=com
  - - 0
    - olcRootPW
    - - '{SSHA}LJzcQPfcsko/wea19bCHLrIcGcqM6u1K'

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.365)       0:00:02.025 ****** 
ok: [alice] => 
  msg: 'These two are ostensibly the same:'

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.046 ****** 
ok: [alice] => 
  openldap_password: openldap_password

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.068 ****** 
ok: [alice] => 
  vault_password: |-
    openldap_password

TASK [Attempt this with the vault password] **************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.033)       0:00:02.101 ****** 
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ldap.INVALID_CREDENTIALS: {'msgtype': 97, 'msgid': 1, 'result': 49, 'desc': 'Invalid credentials', 'ctrls': []}
fatal: [alice]: FAILED! => changed=false 
  details: '{''msgtype'': 97, ''msgid'': 1, ''result'': 49, ''desc'': ''Invalid credentials'', ''ctrls'': []}'
  msg: Cannot bind to the server.

TASK [Attempt this with the insecure password] ***********************************************************************
Monday 16 September 2024  15:23:32 +0100 (0:00:00.431)       0:00:02.533 ****** 
changed: [alice] => changed=true

TASK [Delete this with the insecure password] ************************************************************************
Monday 16 September 2024  15:23:32 +0100 (0:00:00.309)       0:00:02.842 ****** 
changed: [alice] => changed=true

TASK [Remove root DN and password] ***********************************************************************************
Monday 16 September 2024  15:23:32 +0100 (0:00:00.342)       0:00:03.185 ****** 
changed: [alice] => changed=true 
  modlist:
  - - 1
    - olcRootDN
    - cn=Manager,dc=example,dc=com
  - - 1
    - olcRootPW
    - '{SSHA}LJzcQPfcsko/wea19bCHLrIcGcqM6u1K'

TASK [Remove the domain suffix] **************************************************************************************
Monday 16 September 2024  15:23:33 +0100 (0:00:00.336)       0:00:03.521 ****** 
changed: [alice] => changed=true 
  modlist:
  - - 1
    - olcSuffix
    - dc=example,dc=com

PLAY RECAP ***********************************************************************************************************
alice                      : ok=11   changed=6    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

Monday 16 September 2024  15:23:33 +0100 (0:00:00.362)       0:00:03.884 ****** 
=============================================================================== 
Generate the slappasswd hash ---------------------------------------------------------------------------------- 1.18s
Attempt this with the vault password -------------------------------------------------------------------------- 0.43s
Set the domain suffix in the LMDB database -------------------------------------------------------------------- 0.42s
Set the root DN and password in the LMDB database ------------------------------------------------------------- 0.37s
Remove the domain suffix -------------------------------------------------------------------------------------- 0.36s
Delete this with the insecure password ------------------------------------------------------------------------ 0.34s
Remove root DN and password ----------------------------------------------------------------------------------- 0.34s
Attempt this with the insecure password ----------------------------------------------------------------------- 0.31s
Include the vaulted variable ---------------------------------------------------------------------------------- 0.05s
debug --------------------------------------------------------------------------------------------------------- 0.03s
debug --------------------------------------------------------------------------------------------------------- 0.02s
debug --------------------------------------------------------------------------------------------------------- 0.02s
[steve@trillian shared]$ # Extract of the failing command with verbosity set to 10
[steve@trillian shared]$ ansible-playbook --vault-id @vault.pw test2.yml -vvvvvvvvvv
<previous tasks deleted
TASK [Attempt this with the vault password] **************************************************************************
task path: /srv/shares/steve/test2.yml:49
Monday 16 September 2024  15:41:19 +0100 (0:00:00.032)       0:00:03.130 ****** 
Found a vault_id (default) in the vaulttext
We have a secret associated with vault id (default), will try to use to decrypt None
Trying to use vault secret=(FileVaultSecret(filename='/srv/shares/steve/vault.pw')) id=default to decrypt None
Trying secret FileVaultSecret(filename='/srv/shares/steve/vault.pw') for vault_id=default
Decrypt successful with secret=FileVaultSecret(filename='/srv/shares/steve/vault.pw') and vault_id=default
Including module_utils file ansible/__init__.py
Including module_utils file ansible/module_utils/__init__.py
Including module_utils file ansible/module_utils/basic.py
Including module_utils file ansible/module_utils/_text.py
Including module_utils file ansible/module_utils/common/_collections_compat.py
Including module_utils file ansible/module_utils/common/__init__.py
Including module_utils file ansible/module_utils/common/_json_compat.py
Including module_utils file ansible/module_utils/common/_utils.py
Including module_utils file ansible/module_utils/common/arg_spec.py
Including module_utils file ansible/module_utils/common/file.py
Including module_utils file ansible/module_utils/common/locale.py
Including module_utils file ansible/module_utils/common/parameters.py
Including module_utils file ansible/module_utils/common/collections.py
Including module_utils file ansible/module_utils/common/process.py
Including module_utils file ansible/module_utils/common/sys_info.py
Including module_utils file ansible/module_utils/common/text/converters.py
Including module_utils file ansible/module_utils/common/text/__init__.py
Including module_utils file ansible/module_utils/common/text/formatters.py
Including module_utils file ansible/module_utils/common/validation.py
Including module_utils file ansible/module_utils/common/warnings.py
Including module_utils file ansible/module_utils/compat/selectors.py
Including module_utils file ansible/module_utils/compat/__init__.py
Including module_utils file ansible/module_utils/compat/_selectors2.py
Including module_utils file ansible/module_utils/compat/selinux.py
Including module_utils file ansible/module_utils/distro/__init__.py
Including module_utils file ansible/module_utils/distro/_distro.py
Including module_utils file ansible/module_utils/errors.py
Including module_utils file ansible/module_utils/parsing/convert_bool.py
Including module_utils file ansible/module_utils/parsing/__init__.py
Including module_utils file ansible/module_utils/pycompat24.py
Including module_utils file ansible/module_utils/six/__init__.py
Including module_utils file ansible_collections/community/general/plugins/module_utils/ldap.py
Including module_utils file ansible_collections/__init__.py
Including module_utils file ansible_collections/community/__init__.py
Including module_utils file ansible_collections/community/general/__init__.py
Including module_utils file ansible_collections/community/general/plugins/__init__.py
Including module_utils file ansible_collections/community/general/plugins/module_utils/__init__.py
Using module file /usr/share/ansible/collections/ansible_collections/community/general/plugins/modules/ldap_entry.py
Pipelining is enabled.
<alice> ESTABLISH SSH CONNECTION FOR USER: ansible
<alice> SSH: ansible.cfg set ssh_args: (-o)(ControlMaster=auto)(-o)(ControlPersist=20s)
<alice> SSH: ansible_password/ansible_ssh_password not set: (-o)(KbdInteractiveAuthentication=no)(-o)(PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey)(-o)(PasswordAuthentication=no)
<alice> SSH: ANSIBLE_REMOTE_USER/remote_user/ansible_user/user/-u set: (-o)(User="ansible")
<alice> SSH: ANSIBLE_TIMEOUT/timeout set: (-o)(ConnectTimeout=10)
<alice> SSH: Set ssh_common_args: ()
<alice> SSH: Set ssh_extra_args: ()
<alice> SSH: found only ControlPersist; added ControlPath: (-o)(ControlPath="/home/steve/.ansible/cp/f58f7f49b0")
<alice> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=20s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ansible"' -o ConnectTimeout=10 -o 'ControlPath="/home/steve/.ansible/cp/f58f7f49b0"' alice '/bin/sh -c '"'"'sudo -H -S -n  -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-qotkzgsktbkpsuakjefdldkossixrcno ; /usr/bin/python3'"'"'"'"'"'"'"'"' && sleep 0'"'"''
Escalation succeeded
<alice> (1, b'\n{"details": "{\'msgtype\': 97, \'msgid\': 1, \'result\': 49, \'desc\': \'Invalid credentials\', \'ctrls\': []}", "exception": "Traceback (most recent call last):\\n  File \\"/tmp/ansible_community.general.ldap_entry_payload_uvhtfwn4/ansible_community.general.ldap_entry_payload.zip/ansible_collections/community/general/plugins/module_utils/ldap.py\\", line 131, in _connect_to_ldap\\n    connection.simple_bind_s(self.bind_dn, self.bind_pw)\\n  File \\"/usr/lib64/python3.9/site-packages/ldap/ldapobject.py\\", line 249, in simple_bind_s\\n    resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout)\\n  File \\"/usr/lib64/python3.9/site-packages/ldap/ldapobject.py\\", line 543, in result3\\n    resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(\\n  File \\"/usr/lib64/python3.9/site-packages/ldap/ldapobject.py\\", line 553, in result4\\n    ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)\\n  File \\"/usr/lib64/python3.9/site-packages/ldap/ldapobject.py\\", line 128, in _ldap_call\\n    result = func(*args,**kwargs)\\nldap.INVALID_CREDENTIALS: {\'msgtype\': 97, \'msgid\': 1, \'result\': 49, \'desc\': \'Invalid credentials\', \'ctrls\': []}\\n", "failed": true, "msg": "Cannot bind to the server.", "invocation": {"module_args": {"bind_dn": "cn=Manager,dc=example,dc=com", "bind_pw": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "dn": "dc=example,dc=com", "objectClass": ["top", "domain"], "attributes": {}, "state": "present", "recursive": false, "referrals_chasing": "anonymous", "server_uri": "ldapi:///", "start_tls": false, "validate_certs": true, "sasl_class": "external", "xorder_discovery": "auto", "ca_path": null, "client_cert": null, "client_key": null}}}\n', b"OpenSSH_8.7p1, OpenSSL 3.0.7 1 Nov 2022\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug3: /etc/ssh/ssh_config line 55: Including file /etc/ssh/ssh_config.d/50-redhat.conf depth 0\r\ndebug1: Reading configuration data /etc/ssh/ssh_config.d/50-redhat.conf\r\ndebug2: checking match for 'final all' host alice originally alice\r\ndebug3: /etc/ssh/ssh_config.d/50-redhat.conf line 3: not matched 'final'\r\ndebug2: match not found\r\ndebug3: /etc/ssh/ssh_config.d/50-redhat.conf line 5: Including file /etc/crypto-policies/back-ends/openssh.config depth 1 (parse only)\r\ndebug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config\r\ndebug3: gss kex names ok: [gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-]\r\ndebug3: kex names ok: [curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512]\r\ndebug1: configuration requests final Match pass\r\ndebug1: re-parsing configuration\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug3: /etc/ssh/ssh_config line 55: Including file /etc/ssh/ssh_config.d/50-redhat.conf depth 0\r\ndebug1: Reading configuration data /etc/ssh/ssh_config.d/50-redhat.conf\r\ndebug2: checking match for 'final all' host alice originally alice\r\ndebug3: /etc/ssh/ssh_config.d/50-redhat.conf line 3: matched 'final'\r\ndebug2: match found\r\ndebug3: /etc/ssh/ssh_config.d/50-redhat.conf line 5: Including file /etc/crypto-policies/back-ends/openssh.config depth 1\r\ndebug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config\r\ndebug3: gss kex names ok: [gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-]\r\ndebug3: kex names ok: [curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512]\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/steve/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/steve/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 34349\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 1\r\n")
<alice> Failed to connect to the host via ssh: OpenSSH_8.7p1, OpenSSL 3.0.7 1 Nov 2022
debug1: Reading configuration data /etc/ssh/ssh_config
debug3: /etc/ssh/ssh_config line 55: Including file /etc/ssh/ssh_config.d/50-redhat.conf depth 0
debug1: Reading configuration data /etc/ssh/ssh_config.d/50-redhat.conf
debug2: checking match for 'final all' host alice originally alice
debug3: /etc/ssh/ssh_config.d/50-redhat.conf line 3: not matched 'final'
debug2: match not found
debug3: /etc/ssh/ssh_config.d/50-redhat.conf line 5: Including file /etc/crypto-policies/back-ends/openssh.config depth 1 (parse only)
debug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config
debug3: gss kex names ok: [gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-]
debug3: kex names ok: [curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512]
debug1: configuration requests final Match pass
debug1: re-parsing configuration
debug1: Reading configuration data /etc/ssh/ssh_config
debug3: /etc/ssh/ssh_config line 55: Including file /etc/ssh/ssh_config.d/50-redhat.conf depth 0
debug1: Reading configuration data /etc/ssh/ssh_config.d/50-redhat.conf
debug2: checking match for 'final all' host alice originally alice
debug3: /etc/ssh/ssh_config.d/50-redhat.conf line 3: matched 'final'
debug2: match found
debug3: /etc/ssh/ssh_config.d/50-redhat.conf line 5: Including file /etc/crypto-policies/back-ends/openssh.config depth 1
debug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config
debug3: gss kex names ok: [gss-curve25519-sha256-,gss-nistp256-sha256-,gss-group14-sha256-,gss-group16-sha512-]
debug3: kex names ok: [curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512]
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/steve/.ssh/known_hosts'
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/steve/.ssh/known_hosts2'
debug1: auto-mux: Trying existing master
debug2: fd 3 setting O_NONBLOCK
debug2: mux_client_hello_exchange: master version 4
debug3: mux_client_forwards: request forwardings: 0 local, 0 remote
debug3: mux_client_request_session: entering
debug3: mux_client_request_alive: entering
debug3: mux_client_request_alive: done pid = 34349
debug3: mux_client_request_session: session request sent
debug1: mux_client_request_session: master session id: 2
debug3: mux_client_read_packet: read header failed: Broken pipe
debug2: Received exit status from master 1
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_community.general.ldap_entry_payload_uvhtfwn4/ansible_community.general.ldap_entry_payload.zip/ansible_collections/community/general/plugins/module_utils/ldap.py", line 131, in _connect_to_ldap
    connection.simple_bind_s(self.bind_dn, self.bind_pw)
  File "/usr/lib64/python3.9/site-packages/ldap/ldapobject.py", line 249, in simple_bind_s
    resp_type, resp_data, resp_msgid, resp_ctrls = self.result3(msgid,all=1,timeout=self.timeout)
  File "/usr/lib64/python3.9/site-packages/ldap/ldapobject.py", line 543, in result3
    resp_type, resp_data, resp_msgid, decoded_resp_ctrls, retoid, retval = self.result4(
  File "/usr/lib64/python3.9/site-packages/ldap/ldapobject.py", line 553, in result4
    ldap_result = self._ldap_call(self._l.result4,msgid,all,timeout,add_ctrls,add_intermediates,add_extop)
  File "/usr/lib64/python3.9/site-packages/ldap/ldapobject.py", line 128, in _ldap_call
    result = func(*args,**kwargs)
ldap.INVALID_CREDENTIALS: {'msgtype': 97, 'msgid': 1, 'result': 49, 'desc': 'Invalid credentials', 'ctrls': []}
fatal: [alice]: FAILED! => changed=false 
  details: '{''msgtype'': 97, ''msgid'': 1, ''result'': 49, ''desc'': ''Invalid credentials'', ''ctrls'': []}'
  invocation:
    module_args:
      attributes: {}
      bind_dn: cn=Manager,dc=example,dc=com
      bind_pw: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
      ca_path: null
      client_cert: null
      client_key: null
      dn: dc=example,dc=com
      objectClass:
      - top
      - domain
      recursive: false
      referrals_chasing: anonymous
      sasl_class: external
      server_uri: ldapi:///
      start_tls: false
      state: present
      validate_certs: true
      xorder_discovery: auto
  msg: Cannot bind to the server.
<following tasks deleted>

Code of Conduct

ansibullbot commented 2 months ago

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the !component bot command.

click here for bot help

ansibullbot commented 2 months ago

cc @jtyr click here for bot help

felixfontein commented 2 months ago

Since handling vault encrypted passwords is done entirely by ansible-core and the module has no part in it at all, this looks like a problem with ansible-core or your playbook. The module arguments are serialized as JSON and send to the module, which includes the bind_pw argument as a string.

One interesting part is:


TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.046 ****** 
ok: [alice] => 
  openldap_password: openldap_password

TASK [debug] *********************************************************************************************************
Monday 16 September 2024  15:23:31 +0100 (0:00:00.021)       0:00:02.068 ****** 
ok: [alice] => 
  vault_password: |-
    openldap_password

This indicates that the two strings are not identical. Check out the code of the community.general.yaml callback: https://github.com/ansible-collections/community.general/blob/main/plugins/callback/yaml.py#L54-L76

My guess is that there's some whitespace - maybe a trailing newline? Try to run it without using the community.general.yaml callback, that should make it easier to compare the actual strings.

If you look at the task that generates the vaulted password file:

  - name: Create the vaulted variable
    command:
      cmd: >-
        ansible-vault
        encrypt_string
        --stdin-name vault_password
        --output test.pw.yml
        --vault-pass-file vault.pw
      stdin: "{{ openldap_password }}"

A newline is always added to stdin, unless you explicitly tell the command module not to do that by adding stdin_add_newline: false (https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html#parameter-stdin_add_newline).

Maybe try that?

tsgsh commented 2 months ago

Thanks for the quick response. I had compared the two strings in the "real" version of this problem, which is spread over two different roles and they were being reported as being equal. However, you are right, in the test case, they aren't equal and there is a trailing newline so I introduced that problem trying to isolate a different one.. Arguably a problem with the community.general.yaml callback and it should report it as:

ok: [alice] => 
  vault_password: |
    openldap_password

instead of

ok: [alice] => 
  vault_password: |-
    openldap_password

but obviously not a problem with ldap_entry.

Thanks for your help.