ansible-community / ara

ARA Records Ansible and makes it easier to understand and troubleshoot.
https://ara.recordsansible.org
GNU General Public License v3.0
1.86k stars 173 forks source link

no_log on individual loop items #489

Open nlvw opened 1 year ago

nlvw commented 1 year ago

A cool, but probably unknown feature, of looping in Ansible is that you can set no_log on a per item basis. Unfortunately ARA doesn't recognize this and discards the entire tasks output instead of just the loop/item marked with no_log.

The benefit of the below is you get logs for loops where no password is specified or a locked/empty password */! is specified. This helps troubleshooting or viewing things like AD user home creations, local accounts without passwords, and so on.

It would be nice if ARA could selective hide loop output based on no_log value the same as ansible.

Ansible Task

- name: set local and adusers users
  ansible.builtin.user:
    name: "{{ item.name }}"
    shell: "{{ item.shell | default('/bin/bash') }}"
    uid: "{{ item.uid | default(omit) }}"
    password: "{{ item.password | default(omit) }}" # result is masked/hidden
    update_password: "{{ item.update_password | default(omit) }}"
    password_lock: "{{ item.password_lock | default(omit) }}"
    home: "{{ item.home | default(omit) }}"
    create_home: "{{ item.create_home | default(omit) }}"
    move_home: "{{ item.move_home | default(omit) }}"
    group: "{{ item.group | default(omit) }}"
    groups: "{{ item.groups | default(omit) }}"
    umask: "{{ item.umask | default(omit) }}"
    system: "{{ item.system | default(omit) }}"
    local: "{{ item.local | default(omit) }}"
    state: "{{ item.state | default('present') }}"
    remove: "{{ item.remove | default(omit) }}"
    expires: "{{ item.expires | default(omit) }}"
  when:
    - item.name is defined
    - item.name | length > 0
  loop: "{{ lookup('community.general.merge_variables', '^users_users($|_.*$)', pattern_type='regex', initial_value=[]) }}"
  loop_control:
    label: "{{ item.name }}"
  no_log: "{{ true if ((item.password is defined) and (item.password != '*') and (item.password != '!')) else false }}"

Console Output (red marked out names displayed correctly and 'None' denotes a no_log item)

image

ARA Task Result:

image
dmsimard commented 1 year ago

Hey @nlvw,

Thanks for the issue and apologies for the delay, I've been away.

I understand what you'd like to achieve and I know troubleshooting with no_log can be tricky.

I must point out that getting ansible callbacks to reliably discard sensitive data has been a challenging topic and the result of security concerns (like https://github.com/ansible/ansible/issues/22505 and https://bugzilla.redhat.com/show_bug.cgi?id=1440912).

If you'd like to experiment with what this looks like from the perspective of the callback interface the current sanitization occurs here I believe: https://github.com/ansible-community/ara/blob/94a7a4fd781c14fb50e09cf5c43f5afc6c5dc3b0/ara/plugins/callback/ara_default.py#L767-L777 (we used dump_results until https://github.com/ansible-community/ara/commit/dce9f3a5171991f849b9817faca11d17983e8229)

I think the main challenge regarding your use case is that ara doesn't granularly store results for each item of a task and so wouldn't individually consider the value of no_log for each item of a loop.

In other words, when a task runs on a host, it will be stored as a single result whether there is a loop or not. If there is a loop, there would be a list called results with the results nested inside.

I do not have a good suggestion at this time but let me know if you have any ideas.

nlvw commented 1 year ago

@dmsimard Thanks for looking into this. I thought to point this out as the behavior was different compared to the stdout_callback ansible implementation. This is not critical just a nice to have as it may be pretty niche.

For loop tasks semaphore seems to store things as a single task but it has each loop seperately recorded inside that task. You would just need to implement the no_log censorship at the item/loop level instead of the overall task level. Keyword 'just' :).

Feel free to close this unless it's something you want to implement.

dmsimard commented 1 year ago

There is no need to close it, it's something we could do one day.