ansible-collections / microsoft.ad

Ansible collection for Active Directory management
GNU General Public License v3.0
41 stars 24 forks source link

microsoft.ad.ldap inventory plugin attribute values ("__ansible_unsafe") #65

Closed pribytkin-nts closed 1 year ago

pribytkin-nts commented 1 year ago
SUMMARY

Hello! I'm trying to use microsoft.ad collection with AWX 22.5.0 I've prepared custom AWX-EE with all dependencies and everything looks ok, except custom attributes of inventory plugin. Inventory plugin documentation say:

Attributes that are denoted as single value in the LDAP schema are returned as that single value, multi valued attributes are returned as a list of values.

But when i am trying to get attribute from AD:

attributes: operatingSystem: operating_system:

It returns dictionary like that:

{
  "ansible_host": "hostname",
  "microsoft_ad_distinguished_name": "CN=hostname,OU=Servers,DC=EXAMPLE,DC=local",
  "operating_system": {
    "__ansible_unsafe": "Windows Server 2016 Standard"
  }
}

This behaviour tested on versions of ansible core 2.12 and 2.15.3rc1 and it is same for all custom attributes, but standart fields like microsoft_ad_distinguished_name and ansible_host looks good.

ISSUE TYPE
COMPONENT NAME

microsoft.ad.ldap inventory plugin

ANSIBLE VERSION
ansible-inventory [core 2.15.3rc1]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  ansible collection location = /runner/requirements_collections:/root/.ansible/collections:/usr/share/ansible/collections:/usr/share/automation-controller/collections
  executable location = /usr/local/bin/ansible-inventory
  python version = 3.9.17 (main, Jun 26 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
COLLECTION VERSION
Collection              Version
----------------------- -------
microsoft.ad            1.2.0
CONFIGURATION
CONFIG_FILE() = None
OS / ENVIRONMENT

Microsoft AD Server

STEPS TO REPRODUCE
plugin: microsoft.ad.ldap
tls_mode: ldaps
cert_validation: ignore
search_base: DC=EXAMPLE,DC=local
filter: (&(objectCategory=CN=Computer,CN=Schema,CN=Configuration,DC=EXAMPLE,DC=local))
# Only allow Kerberos authentication.
auth_protocol: kerberos

attributes:
  operatingSystem:
    operating_system:
EXPECTED RESULTS

{ "ansible_host": "hostname", "microsoft_ad_distinguished_name": "CN=hostname,OU=Servers,DC=EXAMPLE,DC=local", "operating_system": "Windows Server 2016 Standard" }

ACTUAL RESULTS

{ "ansible_host": "hostname", "microsoft_ad_distinguished_name": "CN=hostname,OU=Servers,DC=EXAMPLE,DC=local", "operating_system": { "__ansible_unsafe": "Windows Server 2016 Standard" } }

jborean93 commented 1 year ago

The inventory plugin wraps all external data with an unsafe delcaration which is special in Ansible. This declaration tells Ansible not to re-template the variable and the actual structure of the data is an implementation detail. There is actually work going on right now to change how it all works with data tagging but that's not really relevant to this topic right now.

Essentially all the attributes are sourced from an external repository so we can't rely on the data to be safe when being templated in the playbooks. The only exceptions are the ansible_host and microsoft_ad_distinguished_name as you've pointed out because they come from known sources with a fixed set of values.

Putting this into an example say I had an inventory configuration with the following and a playbook with:

plugin: microsoft.ad.ldap

attributes:
  comment:
- hosts: WINHOST
  gather_facts: false
  tasks:
  - win_ping:
      data: '{{ comment }}'

This is going to set the comment variable for each host to the value of the comment attribute. If a malicious attacker was able to set this attribute (or any other attribute that was being sourced by your inventory) they could set the value to {{ lookup("file", "/etc/passwd") }} or any other jinja2 compatible code.

image

When the data is not wrapped as unsafe and I run the playbook this is the output

TASK [win_ping] **************************************************************************
PSRP: EXEC (via pipeline wrapper)
ok: [SERVER2022] =>
    changed: false
    invocation:
        module_args:
            data: This is a secret
    ping: This is a secret

We can see that the LDAP attribute value has been templated and has actually printed out the data inside /tmp/secret.txt. With the attributes marked as unsafe we now get:

TASK [win_ping] **************************************************************************
PSRP: EXEC (via pipeline wrapper)
ok: [SERVER2022] =>
    changed: false
    invocation:
        module_args:
            data: '{{ lookup(''file'', ''/tmp/secret.txt'') }}'
    ping: '{{ lookup(''file'', ''/tmp/secret.txt'') }}'

Now it's the raw string the inventory retrieved from the LDAP server without it actually being templated. When you use these values inside Ansible they will act list what the documents described, it's only when dumping the raw values that you can see the __ansible_unsafe implementation.

rathbunr commented 1 year ago

Where's a good place to ask questions on this topic? I tried setting up dynamic inventory on Ansible Automation Platform (AAP) 2.4 following the instructions from the guy from Red Hat, but it's pretty clear I am getting something wrong. It appears to be a very easy method to set this up in and I figured this is a really good place get advice. https://autodotes.com/posts/xnl1o59Sdls9p7vZleBf

My inventory file looks like the example here:

# Name the file microsoft.ad.ldap.yml

plugin: microsoft.ad.ldap

### Supplied by custom credential type ###
# server: 
# port:
# username:
# password:
### End custom credential type ###

search_base: OU=Computers,OU=lab,DC=autodotes,DC=local

# see plugin documentation for additional configuration properties for filtering, grouping, etc.
jborean93 commented 1 year ago

This issue list is mostly more for issues on actual plugins in this collections. I usually try and direct people towards IRC or the mailing list https://docs.ansible.com/ansible/latest/community/communication.html. I'm not online all the time but I frequently look through those areas and reply to areas I'm involved with so you are free to ask questions there. You'll find that others who know about these topics can also help there as well.

I don't know as much about the Controller/AWX/Tower side personally but it should definitely work there.

rathbunr commented 1 year ago

Thank you, and I appreciate all the work you are putting into this. Setting up AAP is a piece of cake, and the nice part is you can sign up for a Redhat Developer account and play for free. Unfortunately, the community of information relating to its usage is pretty sparse at the moment.

jborean93 commented 1 year ago

Closing this issue as the underlying question was answered above and there were no more follow up questions on that side.