sudoi
sudoi

Reputation: 31

Filter result with selectattr in an Ansible playbook

I'm trying to get the playbook show only the filtered result. But instead it showing filtered and non-filtered result. e.g: I just want it to show only the files that not owned by root in the output.

- set_fact:
    target_file: /var/log/apache2/
  
- name: verify that the logs ownership/perms are belong to system administrators and service accounts.
  block: 

    - find: 
        paths: "{{ target_file }}"
        patterns: "*.log"
      register: ownership
      failed_when: ownership.files | selectattr('pw_name', '!=', 'root')

    - debug:
       var: ownership


    - find: 
        paths: "{{ target_file }}"
        patterns: "*.log"
      register: group
      failed_when: group.files | selectattr('gr_name', '!=', 'adm')

    - find: 
        paths: "{{ target_file }}"
        patterns: "*.log"
      register: permissions
      failed_when: permissions.files | selectattr('mode', '!=', '0640')

    - set_fact:
        stig_text: "PASSED"

  rescue:

    - name: change the permission and ownership of the files
      become: true
      file:
        path: "{{ item.path }}"
        owner: root
        group: adm
        mode: 0640
        with_items: "{{ ownership.files }}"
      register: change_perms

    - set_fact:
        stig_text: "PASSED"
      when: change_perms.changed == true

Debug output:

TASK [debug] ************************************************************
ok: [localhost] => {
    "ownership": {
        "changed": false,
        "examined": 18,
        "failed": false,
        "failed_when_result": false,
        "files": [
            {
                "atime": 1652373886.9567592,
                "ctime": 1652373886.9567592,
                "dev": 2,
                "gid": 0,
                "gr_name": "root",
                "inode": 3096224743982700,
                "isblk": false,
                "ischr": false,
                "isdir": false,
                "isfifo": false,
                "isgid": false,
                "islnk": false,
                "isreg": true,
                "issock": false,
                "isuid": false,
                "mode": "0644",
                "mtime": 1652373886.9567592,
                "nlink": 1,
                "path": "/var/log/alternatives.log",
                "pw_name": "root",
                "rgrp": true,
                "roth": true,
                "rusr": true,
                "size": 49270,
                "uid": 0,
                "wgrp": false,
                "woth": false,
                "wusr": true,
                "xgrp": false,
                "xoth": false,
                "xusr": false
            },
            {
                "atime": 1623178394.0,
                "ctime": 1652791696.643692,
                "dev": 2,
                "gid": 0,
                "gr_name": "root",
                "inode": 3377699720693451,
                "isblk": false,
                "ischr": false,
                "isdir": false,
                "isfifo": false,
                "isgid": false,
                "islnk": false,
                "isreg": true,
                "issock": false,
                "isuid": false,
                "mode": "0600",
                "mtime": 1623117753.0,
                "nlink": 1,
                "path": "/var/log/ubuntu-advantage.log",
                "pw_name": "root",
                "rgrp": false,
                "roth": false,
                "rusr": true,
                "size": 0,
                "uid": 0,
                "wgrp": false,
                "woth": false,
                "wusr": true,
                "xgrp": false,
                "xoth": false,
                "xusr": false
            }
        ],
        "matched": 6,
        "msg": ""
    }
}

Upvotes: 1

Views: 1520

Answers (1)

β.εηοιτ.βε
β.εηοιτ.βε

Reputation: 39089

Because your debug is just after a command that would fail if pw_name is not root, due to your failed_when condition, the debug just won't happen if there is any problematic file, and enters the rescue tasks instead, so when you do see the any debug task result, it means there was no problematic file, in terms on ownership, really.

Now the reason being pointed, you have to understand that Ansible is all about idempotency:

An operation is idempotent if the result of performing it once is exactly the same as the result of performing it repeatedly without any intervening actions.

Source: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html

So, you don't even have to do anything complex here you just have to set the rights and ownership to what it should:

- name: change the permission and ownership of the files
  file:
    path: "{{ item }}"
    owner: root
    group: adm
    mode: 0640
  with_fileglob:
    - /var/log/apache2/*.log
  register: permission_audit

And then, if you want to make an audit report, you can do something like:

- name: List of security remediation run during this audit
  debug:
    var: >-
      permission_audit.results 
      | selectattr('changed') 
      | map(attribute='diff')

Which would yield something like:

TASK [List of security remediation run during this audit] ********************
ok: [localhost] => 
  permission_audit.results | selectattr('changed') | map(attribute='diff'):
  - after:
      mode: '0640'
      path: /var/log/apache2/access.1.log
    before:
      mode: '0777'
      path: /var/log/apache2/access.1.log
  - after:
      mode: '0640'
      path: /var/log/apache2/access.2.log
    before:
      mode: '0777'
      path: /var/log/apache2/access.2.log

If you want to show the success or the failure of the audit in the result of the playbook you can even go one step further and assert the length of this list:

- name: Security audit result
  assert:
    that: permission_changes | length == 0
    fail_msg: Audit on Apache log file rights failed
    success_msg: Audit on Apache log file rights passed
  vars:
    permission_changes: >-
      {{
        permission_audit.results
        | selectattr('changed')
        | map(attribute='diff')
      }}

And now our playbook will either end in

TASK [Security audit result] **********************************************
ok: [localhost] => changed=false 
  msg: Audit on Apache log file rights passed

PLAY RECAP ****************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

Or in

TASK [Security audit result] **********************************************
fatal: [localhost]: FAILED! => changed=false 
  assertion: permission_changes | length == 0
  evaluated_to: false
  msg: Audit on Apache log file rights failed

PLAY RECAP ****************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=1   

Given ten log files; two having rights issues and the two tasks:

- file:
    path: "{{ item }}"
    owner: root
    group: adm
    mode: 0640
  with_fileglob:
    - /var/log/apache2/*.log
  register: permission_audit

- name: List of security remediation run during this audit
  debug:
    var: >-
      permission_audit.results
      | selectattr('changed')
      | map(attribute='diff')

This would yield:

TASK [file] *****************************************************************
changed: [localhost] => (item=/var/log/apache2/access.1.log)
ok: [localhost] => (item=/var/log/apache2/access.10.log)
ok: [localhost] => (item=/var/log/apache2/access.4.log)
ok: [localhost] => (item=/var/log/apache2/access.9.log)
ok: [localhost] => (item=/var/log/apache2/access.7.log)
ok: [localhost] => (item=/var/log/apache2/access.3.log)
ok: [localhost] => (item=/var/log/apache2/access.5.log)
ok: [localhost] => (item=/var/log/apache2/access.8.log)
ok: [localhost] => (item=/var/log/apache2/access.6.log)
changed: [localhost] => (item=/var/log/apache2/access.2.log)

TASK [List of security remediation run during this audit] *******************
ok: [localhost] => 
  permission_audit.results | selectattr('changed') | map(attribute='diff'):
  - after:
      mode: '0640'
      path: /var/log/apache2/access.1.log
    before:
      mode: '0777'
      path: /var/log/apache2/access.1.log
  - after:
      mode: '0640'
      path: /var/log/apache2/access.2.log
    before:
      mode: '0777'
      path: /var/log/apache2/access.2.log

And if you rerun those same two tasks again, then it ends up in:

TASK [file] *****************************************************************
ok: [localhost] => (item=/var/log/apache2/access.1.log)
ok: [localhost] => (item=/var/log/apache2/access.10.log)
ok: [localhost] => (item=/var/log/apache2/access.4.log)
ok: [localhost] => (item=/var/log/apache2/access.9.log)
ok: [localhost] => (item=/var/log/apache2/access.7.log)
ok: [localhost] => (item=/var/log/apache2/access.3.log)
ok: [localhost] => (item=/var/log/apache2/access.5.log)
ok: [localhost] => (item=/var/log/apache2/access.8.log)
ok: [localhost] => (item=/var/log/apache2/access.6.log)
ok: [localhost] => (item=/var/log/apache2/access.2.log)

TASK [List of security remediation run during this audit] *******************
ok: [localhost] => 
  permission_audit.results | selectattr('changed') | map(attribute='diff'): []

Now, with an empty list on the last debug, we know our security audit passed everything with a green check.

Upvotes: 1

Related Questions