ansible-plugin-lookup_ldap icon indicating copy to clipboard operation
ansible-plugin-lookup_ldap copied to clipboard

Using delegate_to: breaks plugin?

Open cfiske opened this issue 6 years ago • 7 comments

I have a rather odd issue. I have everything working great to query my LDAP server, retrieve a list of users and do what I need with it. However, if I add delegate_to: locahost to the task it causes the plugin to error with SSLHandshake() failed: misc. bad certificate (-9825)'.

So this works:

---
- hosts: fakehost
  connection: local
  gather_facts: no

  vars:
    my_users: []
    ldap_base: dc=domain,dc=com
    people_base: "ou=People,{{ ldap_base }}"
    ldap_lookup_config:
      url: 'ldaps://ldapserver.domain.com'
      base: "{{ people_base }}"
      binddn: "uid=myuser,ou=People,{{ ldap_base }}"
      bindpw: "mypassword"

    ldap_users:
      base: "{{ people_base }}"
      value: uid
      filter: '(!(loginShell=/bin/false))'

  tasks:
    - name: Fetch LDAP users
      set_fact:
        my_users: "{{ my_users }} + [ '{{ item }}' ]"
      with_ldap:
        - context: ldap_users
        - myldapgroup

    - name: Show users
      debug: msg="User {{ my_users }}"

  roles:
    - quinot.lookup_ldap

but this fails with the SSL cert error:

---
- hosts: fakehost
  gather_facts: no

  vars:
    my_users: []
    ldap_base: dc=domain,dc=com
    people_base: "ou=People,{{ ldap_base }}"
    ldap_lookup_config:
      url: 'ldaps://ldapserver.domain.com'
      base: "{{ people_base }}"
      binddn: "uid=myuser,ou=People,{{ ldap_base }}"
      bindpw: "mypassword"

    ldap_users:
      base: "{{ people_base }}"
      value: uid
      filter: '(!(loginShell=/bin/false))'

  tasks:
    - name: Fetch LDAP users
      delegate_to: localhost
      set_fact:
        my_users: "{{ my_users }} + [ '{{ item }}' ]"
      with_ldap:
        - context: ldap_users
        - myldapgroup

    - name: Show users
      debug: msg="User {{ my_users }}"

  roles:
    - quinot.lookup_ldap

I wondered if this might be a more general bug in Ansible with delegate_to but I don't believe so. If I replace with_ldap with a generic with_items then it works fine with delegate_to. I inserted some debug logging and it's at the lo.simple_bind_s() stage that it fails when using delegate_to: localhost. Also, while yes, it does work with connection: local instead, that does not solve for my use case. I need to be able to delegate facts to localhost. Please let me know if I need to provide more details. This is with Ansible 2.7.5, Python 2.7.15 on MacOS High Sierra 10.13.5.

cfiske avatar Jan 09 '19 23:01 cfiske

Lookup plugins run on the control machine, ie they already run on local host https://docs.ansible.com/ansible/latest/plugins/lookup.html

Like all templating, these plugins are evaluated on the Ansible control machine, not on the target/remote.

evilhamsterman avatar Oct 29 '19 03:10 evilhamsterman

The plugin runs on the control machine, but the resulting facts are tied to the target host. So when I run this across multiple targets it has to repeat the identical lookup request for every host. I just want to query once and have that data accessible for every host in the run.

cfiske avatar Oct 29 '19 18:10 cfiske

The point I'm trying to make is you are dealing with limitations of Ansible, not this plugin. Lookups don't support delegation https://github.com/ansible/ansible/issues/14940.

However you can run lookups in the vars section rather than using set_fact I.E. something like this

---
- hosts: fakehost
  gather_facts: no

  vars:
    my_users: []
    ldap_base: dc=domain,dc=com
    people_base: "ou=People,{{ ldap_base }}"
    ldap_lookup_config:
      url: 'ldaps://ldapserver.domain.com'
      base: "{{ people_base }}"
      binddn: "uid=myuser,ou=People,{{ ldap_base }}"
      bindpw: "mypassword"

    ldap_users:
      base: "{{ people_base }}"
      value: uid
      filter: '(!(loginShell=/bin/false))'
    
    user:  "{{ lookup('ldap', 'myldapgroup', context='ldap_users') }}"

the with_ function is deprecated in newer Ansible versions and doesn't sound like what you want anyway. https://docs.ansible.com/ansible/latest/plugins/lookup.html

Honestly I don't think this plugin is going to get fixed, there are very few commits and none in over a year. Multiple people have tried to get pull requests to add Python 3 support but the dev won't pull them, and he still uses a weird methodology using the with_ format that isn't supported. There is a pull request for a proper LDAP lookup plugin for upstream Ansible that appears much better written and you could drop in and use instead. https://github.com/ansible/ansible/pull/52899

evilhamsterman avatar Oct 29 '19 20:10 evilhamsterman

Thanks for the clarification. I'll keep an eye on the new plugin if/when it gets merged, though I think it still doesn't solve the root issue of avoiding a repeat lookup for each target.

My guess is I'll need to do this with local facts and have a separate command to populate the fact file at the start of the run.

cfiske avatar Oct 29 '19 22:10 cfiske

It is an issue with Ansible that lookups aren't cached

evilhamsterman avatar Oct 29 '19 23:10 evilhamsterman

Just a few comments... I'm still alive :-) The reason why the Python3 support was never merged is because it was done without regard for compatibility with Python2. At that time this was an issue for me. I suspect I could revisit this.

I don't quite get the comment about with_* vs calling lookup(). All lookup plugins support both usages, and as far as I can tell from the Ansible documentation, with_* is not going away.

From https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html:

* We have not deprecated the use of with_<lookup> - that syntax will still be valid for the foreseeable future.

That said, thanks for the pointer to ansible/ansible#52899. Several aspects in the approach there are interesting.

quinot avatar Oct 30 '19 17:10 quinot

@quinot I made the statement that nothing is happening because PR #26 has been available since December of last year with no comments from you haven't made any updates since April of last year despite Python 3 being the way forward and the default soon in Ansible even and Py2 being EOL in 2 months. Based on that it looked like you had abandoned this project.

I mispoke a bit, you are right with_ is not currently on the deprecated list, but it is no longer best practice and is recommended to move away from it if possible. On the same page they also say

  • We added loop in Ansible 2.5. It is not yet a full replacement for with_, but we recommend it for most use cases. With the release of Ansible 2.5, the recommended way to perform loops is the use the new loop keyword instead of with_X style loops.

Mostly I was just trying to point out to @cfiske his issue is with Ansible limitations not your plugin and a possible way to work around it a bit.

evilhamsterman avatar Oct 30 '19 19:10 evilhamsterman