diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb3a49af..0368cf3e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,14 +37,14 @@ repos: exclude: package.lock.json - repo: https://github.com/gitleaks/gitleaks - rev: v8.18.1 + rev: v8.18.2 hooks: - id: gitleaks args: ['--baseline-path', '.config/.gitleaks-report.json'] exclude: .config/.secrets.baseline - repo: https://github.com/ansible-community/ansible-lint - rev: v6.22.0 + rev: v24.2.0 hooks: - id: ansible-lint name: Ansible-lint @@ -63,6 +63,6 @@ repos: - ansible-core>=2.10.1 - repo: https://github.com/adrienverge/yamllint.git - rev: v1.33.0 # or higher tag + rev: v1.35.1 # or higher tag hooks: - id: yamllint diff --git a/Changelog.md b/Changelog.md index d6f9b2e9..20a34030 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,37 @@ # Changes to rhel8CIS +## 1.5.16 - Based on CIS v2.0.0 + +- updated min ansibleversion to 2.11.1 + +- changes to 5.6.1.[ 1, 2, 3] + - ability to change current users + - variables added to defaults/main.yml to enable + +- ability to choose remove for mask for nfs,rpc and rsync + +## 1.5.15 - based on CIS v2.0.0 + +### Audit + +- ability to run audit_only + - var audit_only: true + - tidy up of audit variables to var/audit.yml and some in defaults/main.ym +- goss version increased to 0.3.23 - Doesn't run with latest version 0.4+ + +- updated 5.4.1 and 5.4.2 for authselect + +- Update to 2.1.2. sysconfig for chronyd + +- Added optional control thanks to @bbaassssiiee + - #273 - ability to use crypto Future with options - optional control added + - #329 - pam remove nullok - optional control added + +- update to audit thanks you @aaosopra + - #336 + - #337 + - #338 + ## 1.5.14 based on CIS v2.0.0 - audit updates @@ -157,10 +189,6 @@ Issues. - #228 Thanks to benbulll - audit binary copy var missing -<<<<<<< HEAD -======= - ->>>>>>> devel ## 1.4.0 - workflow improvements diff --git a/README.md b/README.md index 3dd2da52..4c3ba530 100644 --- a/README.md +++ b/README.md @@ -212,3 +212,9 @@ local testing uses: ```sh pre-commit run ``` + +## Credits and Thanks + +Massive thanks to the fantastic community and all its members. +This includes a huge thanks and credit to the original authors and maintainers. +Josh Springer, Daniel Shepherd, Bas Meijeri, James Cassell, Mike Renfro, DFed, George Nalen, Mark Bolwell diff --git a/defaults/main.yml b/defaults/main.yml index 1af35b6f..a52598f8 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -35,25 +35,50 @@ benchmark_version: v2.0.0 # Whether to skip the reboot skip_reboot: true -#### Basic external goss audit enablement settings #### -#### Precise details - per setting can be found at the bottom of this file #### +### +### Settings for associated Audit role using Goss +### -### Goss is required on the remote host +########################################## +### Goss is required on the remote host ### +## Refer to vars/auditd.yml for any other settings ## + +# Allow audit to setup the requirements including installing git (if option chosen and downloading and adding goss binary to system) setup_audit: false -# How to retrive goss + +# enable audits to run - this runs the audit and get the latest content +run_audit: false + +# Only run Audit do not remediate +audit_only: false +# As part of audit_only +# This will enable files to be copied back to control node +fetch_audit_files: false +# Path to copy the files to will create dir structure +audit_capture_files_dir: /some/location to copy to on control node + +# How to retrieve audit binary # Options are copy or download - detailed settings at the bottom of this file # you will need to access to either github or the file already dowmloaded -get_goss_file: download +get_audit_binary_method: download + +## if get_audit_binary_method - copy the following needs to be updated for your environment +## it is expected that it will be copied from somewhere accessible to the control node +## e.g copy from ansible control node to remote host +audit_bin_copy_location: /some/accessible/path # how to get audit files onto host options -# options are git/copy/get_url - use local if already available to to the host (adjust paths accordingly) +# options are git/copy/get_url other e.g. if you wish to run from already downloaded conf audit_content: git -# enable audits to run - this runs the audit and get the latest content -run_audit: false +# archive or copy: +audit_conf_copy: "some path to copy from" -# Timeout for those cmds that take longer to run where timeout set -audit_cmd_timeout: 30000 +# get_url: +audit_files_url: "some url maybe s3?" + +# Run heavy tests - some tests can have more impact on a system enabling these can have greater impact on a system +audit_run_heavy_tests: true ### End Goss enablements #### #### Detailed settings found at the end of this document #### @@ -94,10 +119,6 @@ rhel8cis_rule_1_1_7_5: true rhel8cis_rule_1_1_8_1: true rhel8cis_rule_1_1_8_2: true rhel8cis_rule_1_1_8_3: true -rhel8cis_rule_1_1_18: true -rhel8cis_rule_1_1_19: true -rhel8cis_rule_1_1_20: true -rhel8cis_rule_1_1_21: true rhel8cis_rule_1_1_9: true rhel8cis_rule_1_1_10: true rhel8cis_rule_1_2_1: true @@ -367,11 +388,17 @@ rhel8cis_ftp_server: false rhel8cis_httpd_server: false rhel8cis_is_mail_server: false rhel8cis_net_snmp_server: false +# Ability to choose between remove or mask(default) rhel8cis_nfs_server: false +rhel8cis_nfs_mask: true rhel8cis_nginx_server: false rhel8cis_nis_server: false +# Ability to choose between remove or mask(default) rhel8cis_rpc_server: false +rhel8cis_rpc_mask: true +# Ability to choose between remove or mask(default) rhel8cis_rsync_server: false +rhel8cis_rsync_mask: true rhel8cis_samba_server: false rhel8cis_squid_server: false rhel8cis_telnet_server: false @@ -439,7 +466,7 @@ rhel8cis_ipv6_required: true rhel8cis_ipv6_sysctl_force: true -## Optional - Understand the impact of making the following two control to true +## Optional - Understand the impact of making the following controls to true # By default, we do not disable IPv6 on localhost, as it's important for multiple # components. If you want to disable it anyway, change the following # value to true refer https://access.redhat.com/solutions/8709 @@ -450,6 +477,13 @@ rhel8cis_ipv6_disable_localhost: false rhel8cis_ipv6_sshd_disable: false # disable chrony on ipv6 rhel8cis_ipv6_chrony_disable: false +## +# Pam Change remove null ok from system/password-auth files manual change for 5.4.1 if not using authselect +rhel8cis_remove_nullok: true +# Allow override of crypto polices tyo enable some alternate repos etc when policy is FIPS or FUTURE +rhel8cis_optional_crypto_module: false +rhel8cis_optional_key_exchange: 'ECDHE RSA DHE DHE-RSA PSK DHE-PSK ECDHE-PSK ECDHE-GSS DHE-GSS' +rhel8cis_optional_rsa_size: 2048 # AIDE rhel8cis_config_aide: true @@ -465,7 +499,16 @@ rhel8cis_aide_cron: aide_weekday: '*' # SELinux policy -rhel8cis_selinux_pol: targeted + +# SELinux can run in one of three modes: disabled, permissive, or enforcing: +# CIS strongly discourages disabled +# NOTE: +# Section 1.6.1.5 forces Enforcing to be set. So If rhel8cis_rule_1_6_1_3: true +# make sure enforcing is set below for idempotency for taks 1.6.1.3 - 5 +rhel8cis_selinux_state: enforcing +# Configure SELinux to meet or exceed the default targeted policy, which constrains daemons and system software only. +# Valid Inputs: targeted or mls +rhel8cis_selinux_policy: targeted # Whether or not to run tasks related to auditing/patching the desktop environment rhel8cis_gui: false @@ -483,6 +526,10 @@ rhel8cis_time_synchronization_servers: - 2.pool.ntp.org - 3.pool.ntp.org +# rhel8cis_ansible_chrony_managed uses the template built into this remediation to be copied to /etc/chrony.conf +# If you are using your own self managed /etc/chrony.conf set this to false. +rhel8cis_chrony_ansible_managed: true + rhel8cis_chrony_server_options: "minpoll 8" rhel8cis_ntp_server_options: "iburst" @@ -637,14 +684,28 @@ rhel8cis_authselect: # Any other value does nothing rhel8cis_pamd_manual_risks: NEVER -# 5.6.1.1 -# 5.6.1.2 -# 5.6.1.3 +# 5.6.1.x rhel8cis_pass: max_days: 365 min_days: 7 warn_age: 7 +# 5.6.1.1 +## Set the following to true if you wish to adjust accounts greater than rhel8cis_pass['max_days'] +rhel8cis_5_6_1_1_set_max_expiry: false + +## Add users to be skipped if required +rhel8cis_5_6_1_1_user_skip_list: + root + +# 5.6.1.2 +## Set the following to true if you wish to adjust accounts greater than rhel8cis_pass['min_days'] +rhel8cis_5_6_1_1_set_min_days_change: false + +# 5.6.1.3 +## Set the following to true if you wish to adjust accounts greater than rhel8cis_pass['warn_age'] +rhel8cis_5_6_1_3_set_warn_age_change: false + # 5.6.1.4 rhel8cis_inactivelock: lock_days: 30 @@ -686,52 +747,3 @@ rhel_08_6_2_9_follow_home_symlinks: false # 6.2.12 rhel8cis_dotperm_ansiblemanaged: true -#### Goss Configuration Settings #### - -### Goss binary settings ### -goss_version: - release: v0.3.21 - checksum: 'sha256:9a9200779603acf0353d2c0e85ae46e083596c10838eaf4ee050c924678e4fe3' -audit_bin_path: /usr/local/bin/ -audit_bin: "{{ audit_bin_path }}goss" -audit_format: json - -# if get_goss_file == download change accordingly -goss_url: "https://github.com/goss-org/goss/releases/download/{{ goss_version.release }}/goss-linux-amd64" - -## if get_goss_file - copy the following needs to be updated for your environment -## it is expected that it will be copied from somewhere accessible to the control node -## e.g copy from ansible control node to remote host -copy_goss_from_path: /some/accessible/path - -### Goss Audit Benchmark file ### -## managed by the control audit_content -# git -audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" -audit_git_version: "benchmark_{{ benchmark_version }}_rh8" - -# copy: -audit_local_copy: "some path to copy from" - -# get_url: -audit_files_url: "some url maybe s3?" - -## Goss configuration information -# Set correct env for the run_audit.sh script from https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" -audit_run_script_environment: - AUDIT_BIN: "{{ audit_bin }}" - AUDIT_FILE: 'goss.yml' - AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" -# Where the goss configs and outputs are stored -audit_out_dir: '/opt' -audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit" -pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_pre_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" -post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_post_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" - -## The following should not need changing -goss_file: "{{ audit_conf_dir }}goss.yml" -audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_hostname }}.yml" -audit_results: | - The pre remediation results are: {{ pre_audit_summary }}. - The post remediation results are: {{ post_audit_summary }}. - Full breakdown can be found in {{ audit_out_dir }} diff --git a/files/etc/systemd/system/tmp.mount b/files/etc/systemd/system/tmp.mount deleted file mode 100644 index 47ca6625..00000000 --- a/files/etc/systemd/system/tmp.mount +++ /dev/null @@ -1,25 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[Unit] -Description=Temporary Directory -Documentation=man:hier(7) -Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems -ConditionPathIsSymbolicLink=!/tmp -DefaultDependencies=no -Conflicts=umount.target -Before=local-fs.target umount.target - -[Mount] -What=tmpfs -Where=/tmp -Type=tmpfs -Options=mode=1777,strictatime,noexec,nodev,nosuid - -# Make 'systemctl enable tmp.mount' work: -[Install] -WantedBy=local-fs.target diff --git a/handlers/main.yml b/handlers/main.yml index d10a91b2..f2e6e8f1 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -37,6 +37,10 @@ masked: false state: reloaded +- name: Systemd_daemon_reload + ansible.builtin.systemd: + daemon-reload: true + - name: remount tmp ansible.builtin.shell: mount -o remount /tmp diff --git a/meta/main.yml b/meta/main.yml index e11479e3..2ffe148d 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,12 +1,12 @@ --- galaxy_info: - author: "Sam Doran, Josh Springer, Daniel Shepherd, Bas Meijeri, James Cassell, Mike Renfro, DFed, George Nalen, Mark Bolwell" + author: "MindPoint Group" description: "Apply the DISA RHEL 8 CIS" company: "MindPoint Group" license: MIT namespace: mindpointgroup role_name: rhel8_cis - min_ansible_version: 2.9.0 + min_ansible_version: 2.11.1 platforms: - name: EL versions: diff --git a/tasks/LE_audit_setup.yml b/tasks/LE_audit_setup.yml index b216360b..7ef94b4a 100644 --- a/tasks/LE_audit_setup.yml +++ b/tasks/LE_audit_setup.yml @@ -1,22 +1,34 @@ --- -- name: Audit Setup | Download audit binary +- name: Pre Audit Setup | Set audit package name + block: + - name: Pre Audit Setup | Set audit package name | 64bit + ansible.builtin.set_fact: + audit_pkg_arch_name: AMD64 + when: ansible_facts.machine == "x86_64" + + - name: Pre Audit Setup | Set audit package name | ARM64 + ansible.builtin.set_fact: + audit_pkg_arch_name: ARM64 + when: ansible_facts.machine == "arm64" + +- name: Pre Audit Setup | Download audit binary ansible.builtin.get_url: - url: "{{ goss_url }}" + url: "{{ audit_bin_url }}{{ audit_pkg_arch_name }}" dest: "{{ audit_bin }}" owner: root group: root - checksum: "{{ goss_version.checksum }}" - mode: 0555 + checksum: "{{ audit_bin_version[audit_pkg_arch_name + '_checksum'] }}" + mode: '0555' when: - - get_goss_file == 'download' + - get_audit_binary_method == 'download' -- name: Audit Setup | Copy audit binary +- name: Pre Audit Setup | Copy audit binary ansible.builtin.copy: - src: "{{ copy_goss_from_path }}" + src: "{{ audit_bin_copy_location }}" dest: "{{ audit_bin }}" - mode: 0555 + mode: '0555' owner: root group: root when: - - get_goss_file == 'copy' + - get_audit_binary_method == 'copy' diff --git a/tasks/audit_only.yml b/tasks/audit_only.yml new file mode 100644 index 00000000..864f5bbe --- /dev/null +++ b/tasks/audit_only.yml @@ -0,0 +1,30 @@ +--- + +- name: Audit_Only | Create local Directories for hosts + ansible.builtin.file: + mode: '0755' + path: "{{ audit_capture_files_dir }}/{{ inventory_hostname }}" + recurse: true + state: directory + when: fetch_audit_files + delegate_to: localhost + become: false + +- name: Audit_only | Get audits from systems and put in group dir + ansible.builtin.fetch: + dest: "{{ audit_capture_files_dir }}/{{ inventory_hostname }}/" + flat: true + mode: '0644' + src: "{{ pre_audit_outfile }}" + when: fetch_audit_files + +- name: Audit_only | Show Audit Summary + when: + - audit_only + ansible.builtin.debug: + msg: "The Audit results are: {{ pre_audit_summary }}." + +- name: Audit_only | Stop Playbook Audit Only selected + when: + - audit_only + ansible.builtin.meta: end_play diff --git a/tasks/main.yml b/tasks/main.yml index bb93e6fb..126c0e6b 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -98,10 +98,20 @@ - prelim_tasks - always -- name: Run pre_remediation audit - ansible.builtin.import_tasks: pre_remediation_audit.yml +- name: Include audit specific variables + ansible.builtin.include_vars: audit.yml when: + - run_audit or audit_only + - setup_audit + tags: + - setup_audit - run_audit + +- name: Include pre-remediation audit tasks + ansible.builtin.import_tasks: pre_remediation_audit.yml + when: + - run_audit or audit_only + - setup_audit tags: - run_audit @@ -179,12 +189,11 @@ - post_tasks - always -- name: Run post_remediation audit - ansible.builtin.import_tasks: post_remediation_audit.yml +- name: Run post audit + ansible.builtin.import_tasks: + file: post_remediation_audit.yml when: - run_audit - tags: - - run_audit - name: Show Audit Summary ansible.builtin.debug: diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index a2280529..2c51bbb0 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,44 +1,46 @@ --- -- name: "Post Audit | Run post_remediation {{ benchmark }} audit" - ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path | quote }} -o {{ post_audit_outfile | quote }} -g {{ group_names | quote }}" - environment: "{{ audit_run_script_environment | default ({}) }}" - vars: - warn: false +- name: Post Audit | Run post_remediation {{ benchmark }} audit + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g \"{{ group_names }}\"" + changed_when: true + environment: + AUDIT_BIN: "{{ audit_bin }}" + AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_FILE: goss.yml - name: Post Audit | ensure audit files readable by users ansible.builtin.file: path: "{{ item }}" - mode: 0644 + mode: '0644' state: file loop: - "{{ post_audit_outfile }}" - "{{ pre_audit_outfile }}" - name: Post Audit | Capture audit data if json format + when: + - audit_format == "json" block: - - name: Post Audit | "capture data {{ post_audit_outfile }}" - ansible.builtin.shell: "cat {{ post_audit_outfile | quote }}" + - name: capture data {{ post_audit_outfile }} + ansible.builtin.shell: cat {{ post_audit_outfile }} register: post_audit changed_when: false - - name: Post Audit | Capture post-audit result + - name: Capture post-audit result ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout | from_json | json_query(summary) }}" vars: - summary: 'summary."summary-line"' - when: - - audit_format == "json" + summary: summary."summary-line" - name: Post Audit | Capture audit data if documentation format + when: + - audit_format == "documentation" block: - - name: "Post Audit | capture data {{ post_audit_outfile }}" - ansible.builtin.shell: "tail -2 {{ post_audit_outfile | quote }}" + - name: Post Audit | capture data {{ post_audit_outfile }} + ansible.builtin.shell: tail -2 {{ post_audit_outfile }} register: post_audit changed_when: false - name: Post Audit | Capture post-audit result ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout_lines }}" - when: - - audit_format == "documentation" diff --git a/tasks/pre_remediation_audit.yml b/tasks/pre_remediation_audit.yml index 861db222..e3a261e7 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -1,111 +1,118 @@ --- -- name: Pre Audit | Setup the audit - ansible.builtin.import_tasks: LE_audit_setup.yml +- name: Pre Audit Setup | Setup the LE audit when: - setup_audit tags: - setup_audit + ansible.builtin.include_tasks: LE_audit_setup.yml -- name: "Pre Audit | Ensure {{ audit_conf_dir }} exists" +- name: Pre Audit Setup | Ensure {{ audit_conf_dir }} exists ansible.builtin.file: path: "{{ audit_conf_dir }}" state: directory mode: '0755' -- name: Pre Audit | If using git for content set up +- name: Pre Audit Setup | If using git for content set up + when: + - audit_content == 'git' block: - - name: Pre Audit | Install git + - name: Pre Audit Setup | Install git ansible.builtin.package: name: git state: present - - name: Pre Audit | retrieve audit content files from git + - name: Pre Audit Setup | Retrieve audit content files from git ansible.builtin.git: repo: "{{ audit_file_git }}" dest: "{{ audit_conf_dir }}" version: "{{ audit_git_version }}" - when: - - audit_content == 'git' -- name: Pre Audit | copy to audit content files to server +- name: Pre Audit Setup | Copy to audit content files to server + when: + - audit_content == 'copy' ansible.builtin.copy: src: "{{ audit_local_copy }}" - dest: "{{ audit_conf_dir }}" + dest: "{{ audit_conf_dest }}" mode: preserve + +- name: Pre Audit Setup | Unarchive audit content files on server when: - - audit_content == 'copy' + - audit_content == 'archived' + ansible.builtin.unarchive: + src: "{{ audit_conf_copy }}" + dest: "{{ audit_conf_dir }}" -- name: Pre Audit | get audit content from url +- name: Pre Audit Setup | Get audit content from url + when: + - audit_content == 'get_url' ansible.builtin.get_url: url: "{{ audit_files_url }}" dest: "{{ audit_conf_dir }}" - mode: 0755 - when: - - audit_content == 'get_url' -- name: Pre Audit | Check Goss is available +- name: Pre Audit Setup | Check Goss is available + when: + - run_audit block: - - name: Pre Audit | Check for goss file + - name: Pre Audit Setup | Check for goss file ansible.builtin.stat: path: "{{ audit_bin }}" register: goss_available - - name: Pre Audit | If audit ensure goss is available - ansible.builtin.assert: - msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" + - name: Pre Audit Setup | If audit ensure goss is available when: - not goss_available.stat.exists - when: - - run_audit + ansible.builtin.assert: + msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" -- name: "Pre Audit | Check whether machine is UEFI-based" - ansible.builtin.stat: - path: /sys/firmware/efi - register: rhel8_efi_boot +- name: Pre Audit Setup | Copy ansible default vars values to test audit tags: - goss_template - -- name: Pre Audit | Copy ansible default vars values to test audit + - run_audit + when: + - run_audit ansible.builtin.template: src: ansible_vars_goss.yml.j2 dest: "{{ audit_vars_path }}" - mode: 0600 - when: - - run_audit - tags: - - goss_template + mode: '0600' -- name: "Pre Audit | Run pre_remediation {{ benchmark }} audit" - ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path | quote }} -o {{ pre_audit_outfile | quote }} -g {{ group_names | quote }}" - environment: "{{ audit_run_script_environment | default ({}) }}" - vars: - warn: false +- name: Pre Audit | Run pre_remediation {{ benchmark }} audit + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g \"{{ group_names }}\"" + changed_when: true + environment: + AUDIT_BIN: "{{ audit_bin }}" + AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_FILE: goss.yml - name: Pre Audit | Capture audit data if json format + when: + - audit_format == "json" block: - - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - ansible.builtin.shell: "cat {{ pre_audit_outfile | quote }}" - changed_when: false + - name: capture data {{ pre_audit_outfile }} + ansible.builtin.shell: cat {{ pre_audit_outfile }} register: pre_audit + changed_when: false - name: Pre Audit | Capture pre-audit result ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout | from_json | json_query(summary) }}" vars: - summary: 'summary."summary-line"' - when: - - audit_format == "json" + summary: summary."summary-line" - name: Pre Audit | Capture audit data if documentation format + when: + - audit_format == "documentation" block: - - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - ansible.builtin.shell: "tail -2 {{ pre_audit_outfile | quote }}" + - name: Pre Audit | capture data {{ pre_audit_outfile }} | documentation format + ansible.builtin.shell: tail -2 {{ pre_audit_outfile }} register: pre_audit changed_when: false - - name: Pre Audit | Capture pre-audit result + - name: Pre Audit | Capture pre-audit result | documentation format ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout_lines }}" + +- name: Audit_Only | Run Audit Only when: - - audit_format == "documentation" + - audit_only + ansible.builtin.import_tasks: audit_only.yml diff --git a/tasks/prelim.yml b/tasks/prelim.yml index ed95b19e..3b672b1b 100644 --- a/tasks/prelim.yml +++ b/tasks/prelim.yml @@ -103,7 +103,7 @@ path: /etc/systemd/coredump.conf register: systemd_coredump when: - - rhel8cis_rule_1_6_1 + - rhel8cis_rule_1_5_1 tags: - always @@ -233,3 +233,36 @@ - rhel8cis_level_2 tags: - always + +# Not assigned directly to any control but a requirement - manual change for 5.4.1 if not using authselect +- name: "PRELIM | Optional | Remove the ability to login without a password" + ansible.builtin.replace: + path: "{{ item }}" + regexp: '\snullok' + replace: '' + loop: + - /etc/pam.d/system-auth + - /etc/pam.d/password-auth + when: rhel8cis_remove_nullok + +# Allow other keys to be used while setting FUTURE or FIPS in crypto Policy +# These settings may need to be changed to personal preference +- name: "PRELIM | Optional | Override Ciphers/Keys/Macs if required on some crypto policies" + block: + - name: "PRELIM | Optional | Add Override file Ciphers/Keys/Macs if required on some crypto policies" + ansible.builtin.template: + src: crypto_policy_RSA.j2 + dest: /etc/crypto-policies/policies/modules/RSA-2048 + mode: '0440' + owner: root + group: root + register: rhel8cis_crypto_override_added + + - name: "PRELIM | Optional | Add fact to append crypto_update_command" + ansible.builtin.set_fact: + rhel8cis_crypto_override: true + when: rhel8cis_crypto_override_added is defined + when: + - rhel8cis_optional_crypto_module + - rhel8cis_crypto_policy == 'FIPS' or rhel8cis_crypto_policy == 'FUTURE' + - rhel8cis_rule_1_10 diff --git a/tasks/section_1/cis_1.10.yml b/tasks/section_1/cis_1.10.yml index 87fb482a..765b5241 100644 --- a/tasks/section_1/cis_1.10.yml +++ b/tasks/section_1/cis_1.10.yml @@ -8,7 +8,7 @@ - name: "1.10 | PATCH | Ensure system-wide crypto policy is not legacy" ansible.builtin.shell: | - update-crypto-policies --set "{{ rhel8cis_full_crypto_policy }}" + update-crypto-policies --set "{{ rhel8cis_full_crypto_policy }}{% if rhel8cis_crypto_override %}:RSA-2048{% endif %}" update-crypto-policies notify: change_requires_reboot when: diff --git a/tasks/section_1/cis_1.6.1.x.yml b/tasks/section_1/cis_1.6.1.x.yml index 55574be5..fde021c7 100644 --- a/tasks/section_1/cis_1.6.1.x.yml +++ b/tasks/section_1/cis_1.6.1.x.yml @@ -33,8 +33,8 @@ - name: "1.6.1.3 | PATCH | Ensure SELinux policy is configured" ansible.posix.selinux: conf: /etc/selinux/config - policy: "{{ rhel8cis_selinux_pol }}" - state: enforcing + policy: "{{ rhel8cis_selinux_policy }}" + state: "{{ rhel8cis_selinux_state }}" when: - not rhel8cis_selinux_disable - rhel8cis_rule_1_6_1_3 @@ -50,15 +50,15 @@ - name: "1.6.1.4 | PATCH | Ensure the SELinux mode is not disabled" ansible.posix.selinux: conf: /etc/selinux/config - policy: "{{ rhel8cis_selinux_pol }}" - state: enforcing + policy: "{{ rhel8cis_selinux_policy }}" + state: "{{ rhel8cis_selinux_state }}" when: - not rhel8cis_selinux_disable - rhel8cis_rule_1_6_1_4 tags: - level1-server - level1-workstation - - auotmated + - automated - selinux - patch - rule_1.6.1.4 @@ -66,7 +66,7 @@ - name: "1.6.1.5 | PATCH | Ensure the SELinux state is enforcing" ansible.posix.selinux: conf: /etc/selinux/config - policy: "{{ rhel8cis_selinux_pol }}" + policy: "{{ rhel8cis_selinux_policy }}" state: enforcing when: - not rhel8cis_selinux_disable diff --git a/tasks/section_2/cis_2.1.x.yml b/tasks/section_2/cis_2.1.x.yml index 97ab61b8..33143d23 100644 --- a/tasks/section_2/cis_2.1.x.yml +++ b/tasks/section_2/cis_2.1.x.yml @@ -23,14 +23,17 @@ owner: root group: root mode: 0644 + when: rhel8cis_chrony_ansible_managed - - name: "2.1.2 | PATCH | Ensure chrony is configured | modify /etc/sysconfig/chronyd | 1" + - name: "2.1.2 | PATCH | Ensure chrony is configured | modify /etc/sysconfig/chronyd" ansible.builtin.lineinfile: path: /etc/sysconfig/chronyd - regexp: "^(#)?OPTIONS" - line: "OPTIONS=\"-u chrony\"" + regexp: OPTIONS=\"(.*)(?!-u chrony)(.*)" + line: OPTIONS="\1\2 -u chrony" create: true - mode: 0644 + backrefs: true + mode: '0644' + when: - rhel8cis_time_synchronization == "chrony" - rhel8cis_rule_2_1_2 diff --git a/tasks/section_2/cis_2.2.x.yml b/tasks/section_2/cis_2.2.x.yml index a2cd9c2c..5b759b26 100644 --- a/tasks/section_2/cis_2.2.x.yml +++ b/tasks/section_2/cis_2.2.x.yml @@ -127,7 +127,7 @@ - vsftpd - rule_2.2.8 -- name: "2.2.9 | PACH | Ensure TFTP Server is not installed" +- name: "2.2.9 | PATCH | Ensure TFTP Server is not installed" ansible.builtin.package: name: tftp-server state: absent @@ -291,14 +291,28 @@ - rule_2.2.17 # The name title of the service says mask the service or remove packages -# We went with masking the service due to ipa-client and other elements that are dependant on nfs-utils +# Option available - name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked" - ansible.builtin.systemd: - name: nfs-utils - masked: true - state: stopped + block: + - name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | Remove package" + ansible.builtin.package: + name: nfs-utils + state: absent + when: + - not rhel8cis_nfs_server + - not rhel8cis_nfs_mask + + - name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | Mask service" + notify: Systemd_daemon_reload + ansible.builtin.systemd: + name: nfs-server.service + enabled: false + state: stopped + masked: true + when: + - not rhel8cis_nfs_server + - rhel8cis_nfs_mask when: - - not rhel8cis_nfs_server - "'nfs-utils' in ansible_facts.packages" - rhel8cis_rule_2_2_18 tags: @@ -311,15 +325,30 @@ - rule_2.2.18 # The name title of the service says mask the service or remove packages -# We went with masking the service due to ipa-client and other elements that are dependant on rpcbind +# Option available - name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked" - ansible.builtin.systemd: - name: "{{ item }}" - masked: true - state: stopped - with_items: - - rpcbind - - rpcbind.socket + block: + - name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | Remove package" + ansible.builtin.package: + name: cups + state: absent + when: + - not rhel8cis_rpc_server + - not rhel8cis_rpc_mask + + - name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | Mask service" + notify: Systemd_daemon_reload + ansible.builtin.systemd: + name: "{{ item }}" + enabled: false + state: stopped + masked: true + loop: + - rpcbind.service + - rpcbind.socket + when: + - not rhel8cis_rpc_server + - rhel8cis_rpc_mask when: - not rhel8cis_rpc_server - "'rpcbind' in ansible_facts.packages" @@ -332,15 +361,34 @@ - rpc - rule_2.2.19 -# The name title of the service says mask the service, but the fix allows for both options -# We went with removing to remove the security/update overhead with having the package installed -- name: "2.2.20 | PATCH | Ensure rsync service is not enabled " - ansible.builtin.package: - name: rsync - state: absent +# The name title of the service says mask the service or remove packages +# Option available +- name: "2.2.20 | PATCH | Ensure rsync is not installed or the rsyncd service is masked" + block: + - name: "2.2.20 | PATCH | Ensure rsync is not installed or the rsyncd service is masked | Remove package" + ansible.builtin.package: + name: rsync-daemon + state: absent + when: + - not rhel8cis_rsync_server + - not rhel8cis_rsync_mask + + - name: "2.2.20 | PATCH | Ensure rsync is not installed or the rsyncd service is masked | Mask service" + notify: Systemd_daemon_reload + ansible.builtin.systemd: + name: "{{ item }}" + enabled: false + state: stopped + masked: true + loop: + - 'rsyncd.socket' + - 'rsyncd.service' + when: + - not rhel8cis_rsync_server + - rhel8cis_rsync_mask when: - not rhel8cis_rsync_server - - "'rsync' in ansible_facts.packages" + - "'rsync-daemon' in ansible_facts.packages" - rhel8cis_rule_2_2_20 tags: - level1-server diff --git a/tasks/section_4/cis_4.1.3.x.yml b/tasks/section_4/cis_4.1.3.x.yml index 0bb8acb5..184912c9 100644 --- a/tasks/section_4/cis_4.1.3.x.yml +++ b/tasks/section_4/cis_4.1.3.x.yml @@ -99,7 +99,7 @@ - automated - patch - auditd - - rule_4.1.3_7 + - rule_4.1.3.7 - name: "4.1.3.8 | PATCH | Ensure events that modify user/group information are collected" ansible.builtin.set_fact: diff --git a/tasks/section_4/cis_4.2.1.x.yml b/tasks/section_4/cis_4.2.1.x.yml index 47468e69..a2dfba43 100644 --- a/tasks/section_4/cis_4.2.1.x.yml +++ b/tasks/section_4/cis_4.2.1.x.yml @@ -127,7 +127,7 @@ local2,local3.* -/var/log/localmessages local4,local5.* -/var/log/localmessages local6,local7.* -/var/log/localmessages - *.emrg :omusrmsg:* + *.emerg :omusrmsg:* insertafter: '#### RULES ####' notify: restart rsyslog diff --git a/tasks/section_5/cis_5.4.x.yml b/tasks/section_5/cis_5.4.x.yml index fa73ac38..a300d9f5 100644 --- a/tasks/section_5/cis_5.4.x.yml +++ b/tasks/section_5/cis_5.4.x.yml @@ -3,7 +3,7 @@ - name: "5.4.1 | PATCH | Ensure custom authselect profile is used" block: - name: "5.4.1 | AUDIT | Ensure custom authselect profile is used | Gather profiles" - ansible.builtin.shell: 'authselect current | grep "Profile ID: custom/"' + ansible.builtin.shell: authselect list | grep custom | awk '{print $2}' failed_when: false changed_when: false check_mode: false @@ -15,9 +15,22 @@ - "Below are the current custom profiles" - "{{ rhel8cis_5_4_1_profiles.stdout_lines }}" + - name: "5.4.1 | AUDIT | Ensure custom authselect profile is used | see if profile already exists" + ansible.builtin.stat: + path: "/etc/authselect/custom/{{ rhel8cis_authselect['custom_profile_name'] }}" + register: rhel8cis_5_4_1_auth_select_profile + - name: "5.4.1 | PATCH | Ensure custom authselect profile is used | Create custom profiles" ansible.builtin.shell: authselect create-profile {{ rhel8cis_authselect['custom_profile_name'] }} -b {{ rhel8cis_authselect['default_file_to_copy'] }} - when: rhel8cis_authselect_custom_profile_create + when: + - not rhel8cis_5_4_1_auth_select_profile.stat.exists + - rhel8cis_authselect_custom_profile_create + + - name: "5.4.1 | PATCH | Ensure custom authselect profile is used | select custom profiles" + ansible.builtin.shell: authselect select custom/{{ rhel8cis_authselect['custom_profile_name'] }} --force + when: + - rhel8cis_5_4_1_auth_select_profile.stat.exists + - rhel8cis_authselect_custom_profile_create when: - rhel8cis_rule_5_4_1 tags: @@ -44,7 +57,7 @@ - "{{ rhel8cis_5_4_2_profiles_faillock.stdout_lines }}" - name: "5.4.2 | PATCH | Ensure authselect includes with-faillock | Create custom profiles" - ansible.builtin.shell: "authselect select custom/{{ rhel8cis_authselect['custom_profile_name'] }} with-faillock" + ansible.builtin.shell: "authselect select custom/{{ rhel8cis_authselect['custom_profile_name'] }} with-faillock without-nullok --force" when: rhel8cis_authselect_custom_profile_select - name: 5.4.2 | PATCH | Ensure authselect includes with-faillock | not auth select profile" diff --git a/tasks/section_5/cis_5.6.1.x.yml b/tasks/section_5/cis_5.6.1.x.yml index 441b36e6..2cc5abd2 100644 --- a/tasks/section_5/cis_5.6.1.x.yml +++ b/tasks/section_5/cis_5.6.1.x.yml @@ -1,10 +1,44 @@ --- - name: "5.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 {{ rhel8cis_pass['max_days'] }}" + block: + - name: "5.6.1.1 | AUDIT | Ensure password expiration is 365 days or less | Capture accounts more than 365" + ansible.builtin.shell: "grep -E '^[^:]+:[^!*]' /etc/shadow | awk -F':' '$5>{{ rhel8cis_pass['max_days'] }} { print $1 }'" + changed_when: false + failed_when: rhel8cis_5_6_1_1_pass_max_expire.rc not in [ 0, 1 ] + register: rhel8cis_5_6_1_1_pass_max_expire + + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | update login.defs" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MAX_DAYS' + line: "PASS_MAX_DAYS {{ rhel8cis_pass['max_days'] }}" + + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | Ensure all accounts set to 365" + ansible.builtin.user: + name: "{{ item }}" + password_expire_max: "{{ rhel8cis_pass['max_days'] }}" + loop: "{{ rhel8cis_5_6_1_1_pass_max_expire.stdout_lines | default([]) }}" + when: + - rhel8cis_5_6_1_1_set_max_expiry + - item not in rhel8cis_5_6_1_1_user_skip_list + + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | Warning" + block: + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | Output list if not set to change" + ansible.builtin.debug: + msg: | + "Warning!! The following account are set beyond the expected expiration date: + {{ rhel8cis_5_6_1_1_pass_max_expire.stdout_lines | default([]) }}" + + - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less | set warning fact" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '5.6.1.1' + when: + - rhel8cis_5_6_1_1_pass_max_expire is defined + - rhel8cis_5_6_1_1_pass_max_expire.stdout | length > 0 + - not rhel8cis_5_6_1_1_set_max_expiry when: - rhel8cis_rule_5_6_1_1 tags: @@ -16,10 +50,43 @@ - rule_5.6.1.1 - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more" - ansible.builtin.lineinfile: - path: /etc/login.defs - regexp: '^PASS_MIN_DAYS' - line: "PASS_MIN_DAYS {{ rhel8cis_pass['min_days'] }}" + block: + - name: "5.6.1.2 | AUDIT | Ensure minimum days between password changes is 7 or more | Capture accounts 7 or more" + ansible.builtin.shell: "grep -E '^[^:]+:[^!*]' /etc/shadow | awk -F':' '$4>{{ rhel8cis_pass['min_days'] }} { print $1 }'" + changed_when: false + failed_when: rhel8cis_5_6_1_2_min_day_change.rc not in [ 0, 1 ] + register: rhel8cis_5_6_1_2_min_day_change + + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MIN_DAYS' + line: "PASS_MIN_DAYS {{ rhel8cis_pass['min_days'] }}" + + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more | Change users found" + ansible.builtin.user: + name: "{{ item }}" + password_expire_min: "{{ rhel8cis_pass['min_days'] }}" + loop: "{{ rhel8cis_5_6_1_2_min_day_change.stdout_lines | default([]) }}" + when: + - rhel8cis_5_6_1_2_set_min_days_change + + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more | Warning" + block: + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more | Output list if not set to change" + ansible.builtin.debug: + msg: | + "Warning!! The following account are set beyond the expected minimum days between passwords: + {{ rhel8cis_5_6_1_2_min_day_change.stdout_lines }}" + + - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more | set warning fact" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '5.6.1.2' + when: + - rhel8cis_5_6_1_2_min_day_change is defined + - rhel8cis_5_6_1_2_min_day_change.stdout | length > 0 + - not rhel8cis_5_6_1_2_set_min_days_change when: - rhel8cis_rule_5_6_1_2 tags: @@ -31,12 +98,45 @@ - rule_5.6.1.2 - name: "5.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 {{ rhel8cis_pass['warn_age'] }}" + block: + - name: "5.6.1.3 | AUDIT | Ensure password expiration warning days is 7 or more | capture accounts" + ansible.builtin.shell: "grep -E '^[^:]+:[^!*]' /etc/shadow | awk -F':' '$4>{{ rhel8cis_pass['warn_age'] }} { print $1 }'" + changed_when: false + failed_when: rhel8cis_5_6_1_3_warn_age_change.rc not in [ 0, 1 ] + register: rhel8cis_5_6_1_3_warn_age_change + + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | set default" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_WARN_AGE' + line: "PASS_WARN_AGE {{ rhel8cis_pass['warn_age'] }}" + + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Change users found" + ansible.builtin.shell: "change --warndays {{ rhel8cis_pass['warn_age'] }} {{ item }}" + loop: "{{ rhel8cis_5_6_1_3_warn_age_change.stdout_lines | default([]) }}" + when: + - rhel8cis_5_6_1_3_set_warn_age_change + + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | warning" + block: + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Output list if not set to change" + ansible.builtin.debug: + msg: | + "Warning!! The following account are set beyond the expected warning date: + {{ rhel8cis_5_6_1_3_warn_age_change.stdout_lines }}" + + - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more | set warning fact" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '5.6.1.3' + when: + - rhel8cis_5_6_1_3_warn_age_change is defined + - rhel8cis_5_6_1_3_warn_age_change.stdout | length > 0 + - not rhel8cis_5_6_1_3_set_warn_age_change + when: - rhel8cis_rule_5_6_1_3 + tags: - level1-server - level1-workstation @@ -47,6 +147,12 @@ - name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less" block: + - name: "5.6.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | Getting user list" + ansible.builtin.shell: "awk -F: '/^[^#:]+:[^\\!\\*:]*:[^:]*:[^:]*:[^:]*:[^:]*:(\\s*|-1|3[1-9]|[4-9][0-9]|[1-9][0-9][0-9]+):[^:]*:[^:]*\\s*$/ {print $1}' /etc/shadow" + changed_when: false + check_mode: false + register: rhel_8_5_6_1_4_user_list + - name: "5.6.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | Check current settings" ansible.builtin.shell: useradd -D | grep INACTIVE={{ rhel8cis_inactivelock.lock_days }} | cut -f2 -d= changed_when: false @@ -58,12 +164,6 @@ ansible.builtin.shell: useradd -D -f {{ rhel8cis_inactivelock.lock_days }} when: rhel8cis_5_6_1_4_inactive_settings.stdout | length == 0 - - name: "5.6.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | Getting user list" - ansible.builtin.shell: "awk -F: '/^[^#:]+:[^\\!\\*:]*:[^:]*:[^:]*:[^:]*:[^:]*:(\\s*|-1|3[1-9]|[4-9][0-9]|[1-9][0-9][0-9]+):[^:]*:[^:]*\\s*$/ {print $1}' /etc/shadow" - changed_when: false - check_mode: false - register: rhel_8_5_6_1_4_user_list - - name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less | Apply Inactive setting to existing accounts" ansible.builtin.shell: "chage --inactive {{ rhel8cis_inactivelock.lock_days }} {{ item }}" with_items: diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index d4f2b8ac..94dbe775 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -6,7 +6,7 @@ failed_when: false with_items: "{{ empty_password_accounts.stdout_lines }}" when: - - empty_password_accounts.rc + - empty_password_accounts.stdout is defined - rhel8cis_rule_6_2_1 tags: - level1-server @@ -218,7 +218,7 @@ failed_when: false with_items: "{{ rhel8cis_uid_zero_accounts_except_root.stdout_lines }}" when: - - rhel8cis_uid_zero_accounts_except_root.rc + - rhel8cis_uid_zero_accounts_except_root.stdout is defined - rhel8cis_rule_6_2_8 tags: - level1-server @@ -235,7 +235,7 @@ ansible.builtin.stat: path: "{{ item }}" register: rhel_08_6_2_9_audit - with_items: "{{ rhel8cis_passwd | selectattr('uid', '>=', rhel8uid_interactive_uid_start | int ) | selectattr('uid', '<=', rhel8uid_interactive_uid_stop | int ) | map(attribute='dir') | list }}" + with_items: "{{ rhel8cis_passwd | selectattr('shell', '!=', '/bin/false') | selectattr('shell', '!=', '/usr/bin/nologin') | selectattr('uid', '>=', rhel8uid_interactive_uid_start | int ) | selectattr('uid', '<=', rhel8uid_interactive_uid_stop | int ) | map(attribute='dir') | list }}" - name: "6.2.9 | AUDIT | Ensure all users' home directories exist" ansible.builtin.shell: find -H {{ item.0 | quote }} -type d -not -type l -perm /027 @@ -323,7 +323,7 @@ - name: "6.2.11 | AUDIT | Ensure users' home directories permissions are 750 or more restrictive" ansible.builtin.stat: path: "{{ item }}" - with_items: "{{ rhel8cis_passwd | selectattr('uid', '>=', rhel8uid_interactive_uid_start | int) | selectattr('uid', '<=', rhel8uid_interactive_uid_stop | int) | map(attribute='dir') | list }}" + with_items: "{{ rhel8cis_passwd | selectattr('shell', '!=', '/bin/false') | selectattr('shell', '!=', '/usr/bin/nologin') | selectattr('uid', '>=', rhel8uid_interactive_uid_start | int) | selectattr('uid', '<=', rhel8uid_interactive_uid_stop | int) | map(attribute='dir') | list }}" register: rhel_08_6_2_11_audit - name: "6.2.11 | AUDIT | Ensure users' home directories permissions are 750 or more restrictive" diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 index c9fbf873..d86b83d3 100644 --- a/templates/ansible_vars_goss.yml.j2 +++ b/templates/ansible_vars_goss.yml.j2 @@ -10,6 +10,9 @@ host_os_distribution: {{ ansible_distribution }} # timeout for each command to run where set - default = 10seconds/10000ms timeout_ms: {{ audit_cmd_timeout }} +# Allow the audit to run heavy tests which may utilise resources and impact performance +run_heavy_tests: {{ audit_run_heavy_tests }} + # Taken from LE rhel8-cis rhel8cis_section1: {{ rhel8cis_section1 }} rhel8cis_section2: {{ rhel8cis_section2 }} @@ -29,7 +32,6 @@ run_heavy_tests: true rhel8cis_legacy_boot: {{ rhel8cis_legacy_boot }} {% endif %} -rhel8cis_set_boot_pass: {{ rhel8cis_set_boot_pass }} # These variables correspond with the CIS rule IDs or paragraph numbers defined in # the CIS benchmark documents. # PLEASE NOTE: These work in coordination with the section # group variables and tags. @@ -69,7 +71,7 @@ rhel8cis_rule_1_1_8_3: {{ rhel8cis_rule_1_1_8_3 }} rhel8cis_rule_1_1_9: {{ rhel8cis_rule_1_1_9 }} rhel8cis_rule_1_1_10: {{ rhel8cis_rule_1_1_10 }} -rhel8cis_rule_1_2_1: {% if ansible_distribution == "RedHat" %}True{% else %}False{% endif %} # Only run if Redhat and Subscribed +rhel8cis_rule_1_2_1: {% if ansible_distribution == "RedHat" and rhel8cis_rule_1_2_1 %}True{% else %}False{% endif %} # Only run if Redhat and Subscribed rhel8cis_rule_1_2_2: {{ rhel8cis_rule_1_2_2 }} rhel8cis_rule_1_2_3: {{ rhel8cis_rule_1_2_3 }} rhel8cis_rule_1_2_4: {{ rhel8cis_rule_1_2_4 }} diff --git a/templates/chrony.conf.j2 b/templates/chrony.conf.j2 index e0c31130..12581798 100644 --- a/templates/chrony.conf.j2 +++ b/templates/chrony.conf.j2 @@ -1,95 +1,42 @@ -## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! - -# This the default chrony.conf file for the Debian chrony package. After -# editing this file use the command 'invoke-rc.d chrony restart' to make -# your changes take effect. John Hasler 1998-2008 - -# See www.pool.ntp.org for an explanation of these servers. Please -# consider joining the project if possible. If you can't or don't want to -# use these servers I suggest that you try your ISP's nameservers. We mark -# the servers 'offline' so that chronyd won't try to connect when the link -# is down. Scripts in /etc/ppp/ip-up.d and /etc/ppp/ip-down.d use chronyc -# commands to switch it on when a dialup link comes up and off when it goes -# down. Code in /etc/init.d/chrony attempts to determine whether or not -# the link is up at boot time and set the online status accordingly. If -# you have an always-on connection such as cable omit the 'offline' -# directive and chronyd will default to online. -# -# Note that if Chrony tries to go "online" and dns lookup of the servers -# fails they will be discarded. Thus under some circumstances it is -# better to use IP numbers than host names. +{{ ansible_managed | comment }} +# Use public servers from the pool.ntp.org project. +# Please consider joining the pool (http://www.pool.ntp.org/join.html). {% for server in rhel8cis_time_synchronization_servers -%} server {{ server }} {{ rhel8cis_chrony_server_options }} {% endfor %} -# Look here for the admin password needed for chronyc. The initial -# password is generated by a random process at install time. You may -# change it if you wish. - -keyfile /etc/chrony/chrony.keys - -# Set runtime command key. Note that if you change the key (not the -# password) to anything other than 1 you will need to edit -# /etc/ppp/ip-up.d/chrony, /etc/ppp/ip-down.d/chrony, /etc/init.d/chrony -# and /etc/cron.weekly/chrony as these scripts use it to get the password. - -commandkey 1 - -# I moved the driftfile to /var/lib/chrony to comply with the Debian -# filesystem standard. - -driftfile /var/lib/chrony/chrony.drift - -# Comment this line out to turn off logging. - -log tracking measurements statistics -logdir /var/log/chrony - -# Stop bad estimates upsetting machine clock. - -maxupdateskew 100.0 - -# Dump measurements when daemon exits. - -dumponexit +# Record the rate at which the system clock gains/losses time. +driftfile /var/lib/chrony/drift -# Specify directory for dumping measurements. +# Allow the system clock to be stepped in the first three updates +# if its offset is larger than 1 second. +makestep 1.0 3 -dumpdir /var/lib/chrony +# Enable kernel synchronization of the real-time clock (RTC). +rtcsync -# Let computer be a server when it is unsynchronised. +# Enable hardware timestamping on all interfaces that support it. +#hwtimestamp * -local stratum 10 +# Increase the minimum number of selectable sources required to adjust +# the system clock. +#minsources 2 -# Allow computers on the unrouted nets to use the server. +# Allow NTP client access from local network. +#allow 192.168.0.0/16 -#allow 10/8 -#allow 192.168/16 -#allow 172.16/12 +# Serve time even if not synchronized to a time source. +#local stratum 10 -# This directive forces `chronyd' to send a message to syslog if it -# makes a system clock adjustment larger than a threshold value in seconds. +# Specify file containing keys for NTP authentication. +keyfile /etc/chrony.keys -logchange 0.5 +# Get TAI-UTC offset and leap seconds from the system tz database. +leapsectz right/UTC -# This directive defines an email address to which mail should be sent -# if chronyd applies a correction exceeding a particular threshold to the -# system clock. - -# mailonchange root@localhost 0.5 - -# This directive tells chrony to regulate the real-time clock and tells it -# Where to store related data. It may not work on some newer motherboards -# that use the HPET real-time clock. It requires enhanced real-time -# support in the kernel. I've commented it out because with certain -# combinations of motherboard and kernel it is reported to cause lockups. - -# rtcfile /var/lib/chrony/chrony.rtc +# Specify directory for log files. +logdir /var/log/chrony -# If the last line of this file reads 'rtconutc' chrony will assume that -# the CMOS clock is on UTC (GMT). If it reads '# rtconutc' or is absent -# chrony will assume local time. The line (if any) was written by the -# chrony postinst based on what it found in /etc/default/rcS. You may -# change it if necessary. -rtconutc +# Select which information is logged. +#log measurements statistics tracking diff --git a/templates/crypto_policy_RSA.j2 b/templates/crypto_policy_RSA.j2 new file mode 100644 index 00000000..a88f452e --- /dev/null +++ b/templates/crypto_policy_RSA.j2 @@ -0,0 +1,4 @@ +# Enable additional options to crypto policies +# Set via ansible-lockdown project - sponsored by MindPoint Group +key_exchange = {{ rhel8cis_optional_key_exchange }} +min_rsa_size = {{ rhel8cis_optional_rsa_size }} diff --git a/templates/etc/systemd/system/tmp.mount.j2 b/templates/etc/systemd/system/tmp.mount.j2 index 2e3a1896..6bfcd6a9 100644 --- a/templates/etc/systemd/system/tmp.mount.j2 +++ b/templates/etc/systemd/system/tmp.mount.j2 @@ -1,4 +1,4 @@ -## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! +{{ ansible_managed | comment }} # SPDX-License-Identifier: LGPL-2.1+ # diff --git a/templates/ntp.conf.j2 b/templates/ntp.conf.j2 index 1fc240a3..34fe555f 100644 --- a/templates/ntp.conf.j2 +++ b/templates/ntp.conf.j2 @@ -1,4 +1,4 @@ -## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! +{{ ansible_managed | comment }} # For more information about this file, see the man pages # ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5). diff --git a/vars/audit.yml b/vars/audit.yml new file mode 100644 index 00000000..c7471f35 --- /dev/null +++ b/vars/audit.yml @@ -0,0 +1,43 @@ +--- + +#### Audit Configuration Settings #### + +# This variable specifies the timeout (in ms) for audit commands that +# take a very long time: if a command takes too long to complete, +# it will be forcefully terminated after the specified duration. +audit_cmd_timeout: 120000 + +# if get_audit_binary_method == download change accordingly +audit_bin_url: "https://github.com/goss-org/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-" + +### Goss Audit Benchmark file ### +## managed by the control audit_content +# git +audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" +audit_git_version: "benchmark_{{ benchmark_version }}_rh8" + +## Goss configuration information +# Where the goss configs and outputs are stored +audit_out_dir: '/opt' +# Where the goss audit configuration will be stored +audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit" + +# If changed these can affect other products +pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" +post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" + +## The following should not need changing + +### Audit binary settings ### +audit_bin_version: + release: v0.3.23 + AMD64_checksum: 'sha256:9e9f24e25f86d6adf2e669a9ffbe8c3d7b9b439f5f877500dea02ba837e10e4d' +audit_bin_path: /usr/local/bin/ +audit_bin: "{{ audit_bin_path }}goss" +audit_format: json + +audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_facts.hostname }}.yml" +audit_results: | + The pre remediation results are: {{ pre_audit_summary }}. + The post remediation results are: {{ post_audit_summary }}. + Full breakdown can be found in {{ audit_out_dir }} diff --git a/vars/main.yml b/vars/main.yml index 21dba24e..9868ccb1 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,11 +1,12 @@ --- # vars file for RHEL8-CIS -min_ansible_version: 2.10.1 +min_ansible_version: 2.11.1 rhel8cis_allowed_crypto_policies: - 'DEFAULT' - 'FUTURE' - 'FIPS' +rhel8cis_crypto_override: false rhel8cis_allowed_crypto_policies_modules: - 'OSPP'