0

We have a unique issue and we've spent some time understanding the problem but we are unsure with whether the approach we are taking is in fact correct. Our objective is to validate specific UNIX system files for example /etc/login.def and their specific values.

For the purpose of our hardening requirements we would like to capture for example PASS_MAX_DAYS within the logins.def configuration file and report back its status as (Green Ok) or (Red or Yellow), meaning we need to check further. Our objective is not to make changes but to capture the state of such configuration files.

Here is a code snippet:


---
- name: My Machine
  hosts: localhost
  gather_facts: true

  tasks:
    - name: "PASS_MAX_DAYS - 90 Days - Validator"
      become: yes
      become_user: ansible
      tags: PASS_MAX_DAYS
      lineinfile:
        path: /etc/login.defs
        regex: '^PASS_MAX_DAYS\s+'
        line: 'PASS_MAX_DAYS    90'
        state: present
      check_mode: yes
      register: PASS_MAX_DAYS_output

The idea behind this is simple: Check the file if PASS_MAX_DAYS is set to 90 days, and if so, report back as (Green OK) or Skip without issues. If the value deviates from the 90 day value proceed to flag it as an issue (Red or Orange) and capture the value (using register/debug), output a meaningful msg to the executor and capture the value to a file.

Note: We looked at when conditions but weren't to sure if this would be possible to use. We always had issues with the state of the output being set to "changed" with the msg "line replaced".

Note: We need to keep the regex as generic as possible (why, you may ask) because some sys admins use single spaces, others use tabs and others use multiple spaces. So we would like to not care about how many spaces between each value because we would only like to match the values.

2 Answers 2

1

I would use the "raw"module, and simply use something like:

- name: fetch PASS_MAX_DAYS
  raw: awk '/^PASS_MAX_DAYS/ {print $2}' /etc/login.defs
  register: PASS_MAX_DAYS_output
- name: check PASS_MAX_DAYS ok
  assert:
    that:
      - "PASS_MAX_DAYS_output.stdout != 90"
    msg: "PASS_MAX_DAYS is not 90"

This is flexible enough to easily adapt to other checks.

0

Q: "Check the file if PASS_MAX_DAYS is set to 90 days, and if so, report back as (Green OK) or Skip without issues. If the value deviates from the 90 day value proceed to flag it as an issue (Red or Orange)"

A: The idempotency of Ansible modules is the core paradigma. In this respect, the Ansible code should be viewed rather a definition of a state than a procedure. As a result, just run the Ansible code to make sure the defined state of the system has been set. For example the playbook

$ cat test.yml 
- hosts: localhost
  gather_facts: false
  tasks:
    - lineinfile:
        path: /scratch/tmp/login.defs
        regex: '^PASS_MAX_DAYS\s+'
        line: 'PASS_MAX_DAYS    90'

reported one task has been changed (in any color you might want to configure in ANSIBLE_COLOR_CHANGED)

$ ansible-playbook test.yml 

PLAY [localhost]
TASK [lineinfile] 
changed: [localhost]
PLAY RECAP 
localhost: ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
$ cat /scratch/tmp/login.defs 
PASS_MAX_DAYS    90

Running the playbook again reported 1 task has been ok (in any color you might want to configure in ANSIBLE_COLOR_OK)

PLAY RECAP
localhost: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Audit

If you don't want to make any changes to the system and instead, want to find out the potential differences only, run the playbook in the check mode

-C, --check
    don't make any changes; instead, try to predict some of the changes that may occur

For example the playbook

$ cat login.defs 
PASS_MAX_DAYS    80
$ ansible-playbook test.yml --check

will report that 1 task will be changed. In other words, the audit did not pass.

PLAY [localhost]
TASK [lineinfile] 
changed: [localhost]
PLAY RECAP
localhost: ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Reporting

There are quite a lot of callback plugins available. If none of them fits your purpose it's possible to write a custom plugin, or to use ansible-runner and analyze the artifacts. Python module is available.


Q: "The /etc/login.defs file has tabbed values within the file. If you run the exact same test against a unix platform you'll see that the run status of your ansible playbook will always come back as changed."

A: Make your choice. Either you want to replace the spacing, or want to keep the spacing as is. Both spaces and tabs are valid separators in login.defs. It's possible to use backrefs, change the regex and keep the spaces in the line as is. For example the task below

    - lineinfile:
        path: /scratch/tmp/login.defs
        regex: '^PASS_MAX_DAYS(\s+)(\S*)$'
        line: 'PASS_MAX_DAYS\g<1>90'
        backrefs: true

will not "always come back as changed".

2
  • Thanks @VladimirBotka your rational in-regards to the audit and running a check prior to making the changes is exactly my thinking process. Now i've run through the tests you have suggested BUT capturing a state of changed versus not changed seems to not be working. The /etc/login.defs file has tabbed values within the file. If you run the exact same test against a unix platform you'll see that the run status of your ansible playbook will always come back as changed.
    – Eddie
    Feb 4, 2020 at 5:25
  • I've updated the answer with the example. Open a new question if you have more like this. Feb 4, 2020 at 8:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .