diff --git a/Changelog.md b/Changelog.md index eb789e2..aed863c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,24 @@ # Amazon 2023 CIS - 26th June 2023 +## 1.0.1 + +- thanks to @DianaMariaDDM + - #59 + - #60 + - #61 + - #62 + +- #64 thanks to @tom-henderson + +- extended with new options to force changes for 4.6.1.1|2|3 default false + - amzn2023cis_force_user_maxdays + - amzn2023cis_force_user_mindays + - amzn2023cis_force_user_warndays + +- pre-commit updates + +- general tidy up + ## 1.0 Multiple changes - Audit binary updated goss 0.4.4 diff --git a/defaults/main.yml b/defaults/main.yml index e098097..5460119 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -961,6 +961,15 @@ amzn2023cis_pass: # CIS requires a value of at least 7. warn_age: 7 +# 4.6.1.1 Allow the interactive users not in compliance to force the maxdays change +amzn2023cis_force_user_maxdays: false + +# 4.6.1.2 Allow the interactive users not in compliance to force the mindays change +amzn2023cis_force_user_mindays: false + +# 4.6.1.3 Allow the interactive users not in compliance to force the warndays change +amzn2023cis_force_user_warnage: false + ## Control 4.6.1.4 - Ensure inactive password lock is 30 days or less amzn2023cis_inactivelock: # The following variable refers to the period of time when diff --git a/tasks/main.yml b/tasks/main.yml index 10a3907..b8ad6ba 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -2,12 +2,12 @@ # tasks file for AMAZON2023 - name: Check OS version and family - when: - - os_check ansible.builtin.assert: that: (ansible_facts.distribution == 'Amazon' and ansible_facts.distribution_major_version is version_compare('2023', '==')) fail_msg: "This role can only be run against Supported OSs. {{ ansible_facts.distribution }} {{ ansible_facts.distribution_major_version }} is not supported." success_msg: "This role is running against a supported OS {{ ansible_facts.distribution }} {{ ansible_facts.distribution_major_version }}" + when: + - os_check tags: - always @@ -42,8 +42,8 @@ - name: Setup rules if container when: - - ansible_connection == 'docker' or - ansible_facts.virtualization_type in ["docker", "lxc", "openvz", "podman", "container"] + - ansible_connection == 'docker' or ( ansible_virtualization_type is defined and + ansible_virtualization_type in ["docker", "lxc", "openvz", "podman", "container"]) block: - name: Discover and set container variable if required ansible.builtin.set_fact: diff --git a/tasks/prelim.yml b/tasks/prelim.yml index 915e408..3214e45 100644 --- a/tasks/prelim.yml +++ b/tasks/prelim.yml @@ -2,25 +2,34 @@ # Preliminary tasks that should always be run # List users in order to look files inside each home directory -- name: "PRELIM | List users accounts" - ansible.builtin.shell: "awk -F: '{print $1}' /etc/passwd" - changed_when: false - check_mode: false - register: users + +- name: "PRELIM | AUDIT | Interactive Users" tags: - - level1-server - - users + - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $1 }' + changed_when: false + register: discovered_interactive_usernames -- name: "PRELIM | capture /etc/password variables" - ansible.builtin.include_tasks: - file: parse_etc_password.yml +- name: "PRELIM | AUDIT | Interactive User accounts home directories" tags: - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $6 }' + changed_when: false + register: discovered_interactive_users_home -- name: "PRELIM | Interactive User accounts" - ansible.builtin.shell: 'cat /etc/passwd | grep -Ev "nologin|/sbin" | cut -d: -f6' +- name: "PRELIM | AUDIT | Interactive UIDs" + tags: + - always + ansible.builtin.shell: > + grep -E -v '^(root|halt|sync|shutdown)' /etc/passwd | awk -F: '(!index($7, "sbin/nologin") && $7 != "/bin/nologin" && $7 != "/bin/false") { print $3 }' changed_when: false - register: interactive_users_home + register: discovered_interactive_uids + +- name: "PRELIM | capture /etc/password variables" + ansible.builtin.include_tasks: + file: parse_etc_password.yml tags: - always @@ -175,6 +184,31 @@ tags: - always +- name: "PRELIM | 4.3.4 | Find all sudoers files." + ansible.builtin.shell: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" + changed_when: false + failed_when: false + check_mode: false + register: amzn2023cis_sudoers_files + when: + - amzn2023cis_rule_4_3_4 + tags: + - rule_4.3.4 + - sudo + - patch + +- name: "PRELIM | 4.4.x | Install authselect" + ansible.builtin.package: + name: authselect + state: present + become: true + when: + - '"authselect" not in ansible_facts.packages' + - amzn2023cis_rule_4_4_1 + - amzn2023cis_rule_4_4_2 + tags: + - always + - name: "PRELIM | Section 5.2 | Configure System Accounting (auditd)" ansible.builtin.package: name: audit @@ -208,19 +242,6 @@ - rule_5.2.4.6 - rule_5.2.4.7 -- name: "PRELIM | 4.3.4 | Find all sudoers files." - ansible.builtin.shell: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" - changed_when: false - failed_when: false - check_mode: false - register: amzn2023cis_sudoers_files - when: - - amzn2023cis_rule_4_3_4 - tags: - - rule_4.3.4 - - sudo - - patch - - name: "PRELIM | Discover Interactive UID MIN and MIN from logins.def" block: - name: "PRELIM | Capture UID_MIN information from logins.def" diff --git a/tasks/section_4/cis_4.6.1.x.yml b/tasks/section_4/cis_4.6.1.x.yml index 82094ba..9a4e526 100644 --- a/tasks/section_4/cis_4.6.1.x.yml +++ b/tasks/section_4/cis_4.6.1.x.yml @@ -1,10 +1,29 @@ --- - name: "4.6.1.1 | PATCH | Ensure password expiration is 365 days or less" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_MAX_DAYS' - line: "PASS_MAX_DAYS {{ amzn2023cis_pass['max_days'] }}" + block: + - name: "4.6.1.1 | PATCH | Ensure password expiration is 365 days or less" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MAX_DAYS' + line: "PASS_MAX_DAYS {{ amzn2023cis_pass['max_days'] }}" + + - name: "4.6.1.1 | AUDIT | Ensure password expiration is 365 days or less | Get existing users PASS_MAX_DAYS" + ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($5> {{ amzn2023cis_pass['max_days'] }} || $5< {{ amzn2023cis_pass['max_days'] }} || $5 == -1)){print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_max_days + + - name: "4.6.1.1 | PATCH | Ensure password expiration is 365 days or less | Set existing users PASS_MAX_DAYS" + ansible.builtin.user: + name: "{{ item }}" + password_expire_max: "{{ amzn2023cis_pass['max_days'] }}" + loop: "{{ discovered_max_days.stdout_lines }}" + when: + - discovered_max_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - amzn2023cis_force_user_maxdays + when: - amzn2023cis_rule_4_6_1_1 tags: @@ -19,10 +38,29 @@ - nist_sp800-53r5_IA-5 - name: "4.6.1.2 | PATCH | Ensure minimum days between password changes is configured" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_MIN_DAYS' - line: "PASS_MIN_DAYS {{ amzn2023cis_pass['min_days'] }}" + block: + - name: "4.6.1.2 | PATCH | Ensure minimum days between password changes is configured | set login.defs" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MIN_DAYS' + line: "PASS_MIN_DAYS {{ amzn2023cis_pass['min_days'] }}" + + - name: "4.6.1.2 | AUDIT | Ensure minimum days between password changes is configured | Get existing users PASS_MIN_DAYS" + ansible.builtin.shell: "awk -F: '/^[^:]+:[^!*]/ && $4< {{ amzn2023cis_pass['min_days'] }} {print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_min_days + + - name: "4.6.1.2 | PATCH | Ensure minimum days between password changes is configured | Set existing users PASS_MIN_DAYS" + ansible.builtin.user: + name: "{{ item }}" + password_expire_max: "{{ amzn2023cis_pass['min_days'] }}" + loop: "{{ discovered_min_days.stdout_lines }}" + when: + - discovered_min_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - amzn2023cis_force_user_mindays + when: - amzn2023cis_rule_4_6_1_2 tags: @@ -37,10 +75,26 @@ - nist_sp800-53r5_IA-5 - name: "4.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_WARN_AGE' - line: "PASS_WARN_AGE {{ amzn2023cis_pass['warn_age'] }}" + block: + - name: "4.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | set login.defs" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_WARN_AGE' + line: "PASS_WARN_AGE {{ amzn2023cis_pass['warn_age'] }}" + + - name: "4.6.1.3 | AUDIT | Ensure password expiration warning days is 7 or more | Get existing users WARN_DAYS" + ansible.builtin.shell: "awk -F: '/^[^:]+:[^!*]/ && $6< {{ amzn2023cis_pass['warn_age'] }} {print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: discovered_warn_days + + - name: "4.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Set existing users WARN_DAYS" + ansible.builtin.shell: "chage --warndays {{ amzn2023cis_pass['warn_age'] }} {{ item }}" + loop: "{{ discovered_warn_days.stdout_lines }}" + when: + - discovered_warn_days.stdout_lines | length > 0 + - item in discovered_interactive_usernames.stdout + - amzn2023cis_force_user_warnage when: - amzn2023cis_rule_4_6_1_3 tags: diff --git a/tasks/section_5/cis_5.2.4.x.yml b/tasks/section_5/cis_5.2.4.x.yml index 1aff30d..d6aedf1 100644 --- a/tasks/section_5/cis_5.2.4.x.yml +++ b/tasks/section_5/cis_5.2.4.x.yml @@ -63,12 +63,11 @@ - name: "5.2.4.5 | PATCH | Ensure audit configuration files are 640 or more restrictive" ansible.builtin.file: path: "{{ item.path }}" - mode: '0640' - loop: "{{ auditd_conf_files.files }}" + mode: g-wx,o-rwx + loop: "{{ auditd_conf_files.files | default([]) }}" loop_control: label: "{{ item.path }}" when: - - item.mode != '06(0|4)0' - amzn2023cis_rule_5_2_4_5 tags: - level2-server diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index 20a8dda..ef25e44 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -317,7 +317,7 @@ etype: group permissions: rx state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Set other ACL" @@ -327,7 +327,7 @@ etype: other permissions: 0 state: present - loop: "{{ interactive_users_home.stdout_lines }}" + loop: "{{ discovered_interactive_users_home.stdout_lines }}" when: not system_is_container when: - amzn2023cis_rule_6_2_10 @@ -368,9 +368,9 @@ path: "{{ item }}" state: absent loop: - - "{{ interactive_users_home.stdout_lines }}/.netrc" - - "{{ interactive_users_home.stdout_lines }}/.rhosts" - - "{{ interactive_users_home.stdout_lines }}/.forward" + - "{{ discovered_interactive_users_home.stdout_lines }}/.netrc" + - "{{ discovered_interactive_users_home.stdout_lines }}/.rhosts" + - "{{ discovered_interactive_users_home.stdout_lines }}/.forward" when: amzn2023cis_remove_other_dot_files when: