ansible-lockdown / UBUNTU22-CIS

Ansible role for Ubuntu22 CIS Baseline
https://ansible-lockdown.readthedocs.io/en/latest/
MIT License
155 stars 68 forks source link

Controls 5.5.1.1-3 (Password expiration) have incorrect return values defined #153

Closed LoZZoL closed 7 months ago

LoZZoL commented 8 months ago

Describe the Issue Controls 5.5.1.1-3 under cis_5.5.x.yml do not work as intended for a number of reasons:

1) Their loops are reliant on a non-existent return value (stdout_list). 2) There are flaws in the shell command logic in 5.5.1.1 and 5.5.1.2 3) with_items: should be used instead of loop: 4) They all use a conditional of (item != 'root') and (not ubtu22cis_uses_root) which will never affect the root account even if ubtu22cis_users_root is set to false

Running these controls does not change the password expiration settings on existing accounts.

Expected Behavior Running these controls should result in existing users under /etc/shadow having their password expiration settings updated if they are defined under variables ubtu22cis_pass.max_days; ubtu22cis_pass.min_days; ubtu22cis_pass.warn_days.

Actual Behavior Password expiration settings listed above are not updated. Running ansible-playbook -vvv shows something similar to (truncated):

invocation": {
        "module_args": {
            "_raw_params": "chage --mindays 1 ubtu22cis_5_5_1_1_min_days.stdout_list",
            "_uses_shell": true,
            "argv": null,

Environment (please complete the following information):

Possible Solution I have replaced the control tasks with the following and tested successfully:

      - name: "5.5.1.1 | PATCH | Ensure minimum days between password changes is configured | Set /etc/login.defs PASS_MIN_DAYS"
        ansible.builtin.lineinfile:
            path: /etc/login.defs
            regexp: '^PASS_MIN_DAYS|^#PASS_MIN_DAYS'
            line: 'PASS_MIN_DAYS {{ ubtu22cis_pass.min_days }}'

      - name: "5.5.1.1 | PATCH | Ensure minimum days between password changes is configured | Get existing users PASS_MIN_DAYS"
        ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($4<{{ ubtu22cis_pass.min_days }})) {print $1}' /etc/shadow"
        changed_when: false
        failed_when: false
        register: ubtu22cis_5_5_1_1_min_days

      - name: "5.5.1.1 | PATCH | Ensure minimum days between password changes is configured | Set existing users PASS_MIN_DAYS"
        ansible.builtin.shell: chage --mindays {{ ubtu22cis_pass.min_days }} {{ item }}
        failed_when: false
        changed_when: ubtu22cis_5_5_1_1_min_days.stdout |length > 0
        with_items:
            - "{{ ubtu22cis_5_5_1_1_min_days.stdout_lines }}"
        when:
            - ubtu22cis_disruption_high
            - (item != 'root') or (not ubtu22cis_uses_root)
  when:
      - ubtu22cis_rule_5_5_1_1
  tags:
      - level1-server
      - level1-workstation
      - automated
      - patch
      - rule_5.5.1.1
      - user
      - login

- name: "5.5.1.2 | PATCH | Ensure password expiration is 365 days or less"
  block:
      - name: "5.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Set /etc/login.defs PASS_MAX_DAYS"
        ansible.builtin.lineinfile:
            path: /etc/login.defs
            regexp: '^PASS_MAX_DAYS|^#PASS_MAX_DAYS'
            line: 'PASS_MAX_DAYS {{ ubtu22cis_pass.max_days }}'
            insertafter: '# Password aging controls'

      - name: "5.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Get existing users PASS_MAX_DAYS"
        ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($5>{{ ubtu22cis_pass.max_days }} || $5<{{ ubtu22cis_pass.min_days }} || $5 == -1)){print $1}' /etc/shadow"
        changed_when: false
        failed_when: false
        register: ubtu22cis_5_5_1_2_max_days

      - name: "5.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Set existing users PASS_MAX_DAYS"
        ansible.builtin.shell: chage --maxdays {{ ubtu22cis_pass.max_days }} {{ item }}
        failed_when: false
        changed_when: ubtu22cis_5_5_1_2_max_days.stdout | length > 0
        with_items:
            - "{{ ubtu22cis_5_5_1_2_max_days.stdout_lines }}"
        when:
            - ubtu22cis_disruption_high
            - (item != 'root') or (not ubtu22cis_uses_root)
  when:
      - ubtu22cis_rule_5_5_1_2
  tags:
      - level1-server
      - level1-workstation
      - automated
      - patch
      - rule_5.5.1.2
      - user
      - login
      - skip_ansible_lint

- name: "5.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more"
  block:
      - name: "5.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Set /etc/login.defs PASS_WARN_AGE"
        ansible.builtin.lineinfile:
            path: /etc/login.defs
            regexp: '^PASS_WARN_AGE|^#PASS_WARN_AGE'
            line: 'PASS_WARN_AGE {{ ubtu22cis_pass.warn_age }}'

      - name: "5.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Get existing users PASS_WARN_AGE"
        ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && $6<{{ ubtu22cis_pass.warn_age }}){print $1}' /etc/shadow"
        changed_when: false
        failed_when: false
        register: ubtu22cis_5_5_1_3_warn_days

      - name: "5.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Set existing users PASS_WARN_AGE"
        ansible.builtin.shell: chage --maxdays {{ ubtu22cis_pass.warn_age }} {{ item }}
        failed_when: false
        changed_when: ubtu22cis_5_5_1_3_warn_days.stdout | length > 0
        with_items:
            - "{{ ubtu22cis_5_5_1_3_warn_days.stdout_lines }}"
        when:
            - ubtu22cis_disruption_high
            - (item != 'root') or (not ubtu22cis_uses_root)