diff --git a/.ansible-lint b/.ansible-lint index c3dfee3..e582a58 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,18 +1,20 @@ parseable: true quiet: true skip_list: - - 'schema' - - 'no-changed-when' - - 'fqcn-builtins' - - 'experimental' - - 'name[casing]' - - 'name[template]' - - 'jinja[spacing]' - - '204' - - '305' - - '303' - - '403' - - '306' - - '602' + - 'schema' + - 'no-changed-when' + - 'var-spacing' + - 'experimental' + - 'name[play]' + - 'name[casing]' + - 'name[template]' + - 'fqcn[action]' + - '204' + - '305' + - '303' + - '403' + - '306' + - '602' + - '208' use_default_rules: true verbosity: 0 diff --git a/.github/workflows/OS.tfvars b/.github/workflows/OS.tfvars index a5e2fda..634512b 100644 --- a/.github/workflows/OS.tfvars +++ b/.github/workflows/OS.tfvars @@ -1,5 +1,5 @@ #Ami Alma 9 -ami_id = "ami-02881bd671eb4ac61" +ami_id = "ami-0845395779540e3cb" ami_os = "rhel9" ami_username = "ec2-user" ami_user_home = "/home/ec2-user" diff --git a/.github/workflows/linux_benchmark_testing.yml b/.github/workflows/linux_benchmark_testing.yml index 2c972d5..48e7eed 100644 --- a/.github/workflows/linux_benchmark_testing.yml +++ b/.github/workflows/linux_benchmark_testing.yml @@ -26,12 +26,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1.1.1 + - uses: actions/first-interaction@main with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: |- Congrats on opening your first pull request and thank you for taking the time to help improve Ansible-Lockdown! - Please join in the conversation happening on the [Discord Server](https://discord.io/iansible-lockdown) as well. + Please join in the conversation happening on the [Discord Server](https://discord.io/ansible-lockdown) as well. # This workflow contains a single job called "build" build: # The type of runner that the job will run on @@ -44,7 +44,7 @@ jobs: steps: # Checks-out your repository under $GITHUB_WORKSPACE, # so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} @@ -87,6 +87,12 @@ jobs: run: sleep 60s shell: bash +# Set up requirements for random root password CIS 5.6.6 + - name: add urandom passwd to root account + shell: bash + run: | + ANSIBLE_HOST_KEY_CHECKING=False && ansible all -i .github/workflows/hosts.yml -m shell -a "cat /dev/urandom | tr -dc ‘[:print:]’ | head -c50 | passwd --stdin root" --private-key ${{ secrets.SSH_PRV_KEY }} -b + # Run the ansible playbook - name: Run_Ansible_Playbook uses: arillso/action.playbook@master diff --git a/.github/workflows/main.tf b/.github/workflows/main.tf index 61da17c..dad096f 100644 --- a/.github/workflows/main.tf +++ b/.github/workflows/main.tf @@ -78,6 +78,7 @@ resource "local_file" "inventory" { run_audit: true system_is_ec2: true audit_git_version: devel + skip_reboot: false EOF } diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh deleted file mode 100644 index 1a7202a..0000000 --- a/.github/workflows/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -RHEL7=$(grep -c RHEL7 OS.tfvars) -if [ `echo $?` != 0 ]; then - exit 0 -fi - - diff --git a/.yamllint b/.yamllint index 693eec6..4823010 100644 --- a/.yamllint +++ b/.yamllint @@ -9,12 +9,6 @@ ignore: | extends: default rules: - indentation: - # Requiring 4 space indentation - spaces: 4 - # Requiring consistent indentation within a file, either indented or not - indent-sequences: consistent - truthy: disable braces: max-spaces-inside: 1 level: error @@ -32,4 +26,4 @@ rules: trailing-spaces: enable truthy: allowed-values: ['true', 'false'] - check-keys: false + check-keys: true diff --git a/Changelog.md b/Changelog.md index 506b67a..35e9e44 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,29 @@ # Changes to rhel9CIS +## Initial CIS v1.0.0 - released Dec 2022 + +### Official CIS release + +Jan-2023 release + +- updated ansible minimum to 2.10 +- Lint file updates and improvements +- auditd now shows diff ater initial template added +- many control rewritten +- Many controls moved ID references +- Audit updates aligned +- Command warn arg removed +- Ansible 2.14 now supported +- makefile added (hopefully help some) +- fqcn added to all controls +- some controls rewritten using module rather than shell +- typo fixes from rhel_08 inheritance +- workflow update for 5.6.6 to set random root password to allow for testing +- incorporates issues + - #23 + - #24 +- New option to add faillock for users without authselect - defaults/main 5.4.2 + ## 0.5 - audit path updated and output file name diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..5d7a9b3 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.PHONY: all help galaxy-install ansible-list yamllint pip-requirements + + +GALAXY=ansible-galaxy +ANSIBLE_LINT='/usr/local/bin/ansible-lint' +ANSIBLE_FILE=site.yml + +all: help + +help: + @echo "Make command examples for Ansible" + @echo "Command for assisting with ansible setup" + @echo " galaxy-install to install roles using ansible-galaxy" + @echo " ansible-lint to lint playbook files" + @echo " yamllint to lint playbook files" + @echo " pip-requirements add pip required file" + + +galaxy-install: + $(GALAXY) install -r ./collections/requirements.yml + +ansible-lint: + $(ANSIBLE_LINT) $(ANSIBLE_FILE) + +yamllint: + git ls-files "*.yml"|xargs yamllint + +pip-requirements: + @echo 'Python dependencies:' + @cat requirements.txt + pip3 install -r requirements.txt + diff --git a/README.md b/README.md index 00d5a39..9513374 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,15 @@ -# Development Only -## RHEL 9 CIS (predicted) - Beta - CIS baselines or OS not yet GA +# RHEL 9 CIS -## Testing if you have access to the RH developer branches - -### This should work on RHEL8 and derivatives currently +## v1.0.0 - released Dec 2022 ![Build Status](https://img.shields.io/github/workflow/status/ansible-lockdown/RHEL9-CIS/CommunityToDevel?label=Devel%20Build%20Status&style=plastic) ![Build Status](https://img.shields.io/github/workflow/status/ansible-lockdown/RHEL9-CIS/DevelToMain?label=Main%20Build%20Status&style=plastic) ![Release](https://img.shields.io/github/v/release/ansible-lockdown/RHEL9-CIS?style=plastic) -Configure RHEL 9 machine to be [CIS](https://www.cisecurity.org/cis-benchmarks/) compliant with RHEL8 settings (RHEL9 not yet released) -Based on v2.0.0 RHEL8 +Configure RHEL 9 machine to be [CIS](https://www.cisecurity.org/cis-benchmarks/) -Based on [CIS RedHat Enterprise Linux 8 Benchmark v2.0.0. - 02-23-2022 ](https://www.cisecurity.org/cis-benchmarks/) +Based on [CIS RedHat Enterprise Linux 9 Benchmark v1.0.0. - 11-30-2022 ](https://www.cisecurity.org/cis-benchmarks/) ## Join us @@ -41,9 +37,20 @@ RHEL 9 Almalinux 9 Rocky 9 +ansible 2.10 +jmespath +relevant collections + - Access to download or add the goss binary and content to the system if using auditing (other options are available on how to get the content to the system.) -**General:** +## Tested with + +ansible-base 2.10.17 - python 3.8 +ansible-core 2.13.4 - python 3.10 + +- makefile - this is there purely for testing and initial setup purposes. + +## General - Basic knowledge of Ansible, below are some links to the Ansible documentation to help get started if you are unfamiliar with Ansible - [Main Ansible documentation page](https://docs.ansible.com) @@ -61,11 +68,13 @@ Rocky 9 - Ansible 2.9+ - python-def (should be included in RHEL 9) - libselinux-python -- jmespath +- pip packages + - jmespath ( complete list found in requirements.txt) +- collections found in collections/requirememnts.yml ## Role Variables -This role is designed that the end user should not have to edit the tasks themselves. All customizing should be done via the defaults/main.yml file or with extra vars within the project, job, workflow, etc. These variables can be found [here](https://github.com/ansible-lockdown/RHEL9-CIS/wiki/Main-Variables) in the Main Variables Wiki page. All variables are listed there along with descriptions. +This role is designed that the end user should not have to edit the tasks themselves. All customizing should be done by overriding the required varaibles as found in defaults/main.yml file. e.g. using inventory, group_vars, extra_vars ## Tags @@ -83,3 +92,10 @@ Below is an example of the tag section from a control within this role. Using th - patch - rule_2.2.4 ``` + +### Known Issues + +CIS 1.2.4 - repo_gpgcheck is not carried out for RedHat hosts as the default repos do not have this function. This also affect EPEL(not covered by var). + - Rocky and Alma not affected. +Variable used to unset. +rhel9cis_rhel_default_repo: true # to be set to false if using repo that does have this ability diff --git a/ansible.cfg b/ansible.cfg index 831f01d..3bc6e07 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -5,7 +5,13 @@ system_warnings=False command_warnings=False nocows=1 retry_files_save_path=/dev/null -library=~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules:./library +pipelining=true + +# Use the YAML callback plugin. +stdout_callback = yaml +# Use the stdout_callback when running ad-hoc commands. +bin_ansible_callbacks = True + [privilege_escalation] @@ -14,6 +20,7 @@ record_host_keys=False [ssh_connection] transfer_method=scp +ssh_args = -o ControlMaster=auto -o ControlPersist=60s [accelerate] diff --git a/defaults/main.yml b/defaults/main.yml index 2cbbbc8..b3f73d8 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -71,7 +71,6 @@ audit_cmd_timeout: 60000 # Section 1 rules rhel9cis_rule_1_1_1_1: true rhel9cis_rule_1_1_1_2: true -rhel9cis_rule_1_1_1_3: true rhel9cis_rule_1_1_2_1: true rhel9cis_rule_1_1_2_2: true rhel9cis_rule_1_1_2_3: true @@ -79,7 +78,6 @@ rhel9cis_rule_1_1_2_4: true rhel9cis_rule_1_1_3_1: true rhel9cis_rule_1_1_3_2: true rhel9cis_rule_1_1_3_3: true -rhel9cis_rule_1_1_3_4: true rhel9cis_rule_1_1_4_1: true rhel9cis_rule_1_1_4_2: true rhel9cis_rule_1_1_4_3: true @@ -95,26 +93,24 @@ rhel9cis_rule_1_1_6_4: true rhel9cis_rule_1_1_7_1: true rhel9cis_rule_1_1_7_2: true rhel9cis_rule_1_1_7_3: true -rhel9cis_rule_1_1_7_4: true -rhel9cis_rule_1_1_7_5: true rhel9cis_rule_1_1_8_1: true rhel9cis_rule_1_1_8_2: true rhel9cis_rule_1_1_8_3: true +rhel9cis_rule_1_1_8_4: true rhel9cis_rule_1_1_18: true rhel9cis_rule_1_1_19: true rhel9cis_rule_1_1_20: true rhel9cis_rule_1_1_21: true rhel9cis_rule_1_1_9: true -rhel9cis_rule_1_1_10: true rhel9cis_rule_1_2_1: true rhel9cis_rule_1_2_2: true rhel9cis_rule_1_2_3: true rhel9cis_rule_1_2_4: true rhel9cis_rule_1_3_1: true rhel9cis_rule_1_3_2: true +rhel9cis_rule_1_3_3: true rhel9cis_rule_1_4_1: true rhel9cis_rule_1_4_2: true -rhel9cis_rule_1_4_3: true rhel9cis_rule_1_5_1: true rhel9cis_rule_1_5_2: true rhel9cis_rule_1_5_3: true @@ -125,6 +121,7 @@ rhel9cis_rule_1_6_1_4: true rhel9cis_rule_1_6_1_5: true rhel9cis_rule_1_6_1_6: true rhel9cis_rule_1_6_1_7: true +rhel9cis_rule_1_6_1_8: true rhel9cis_rule_1_7_1: true rhel9cis_rule_1_7_2: true rhel9cis_rule_1_7_3: true @@ -136,6 +133,11 @@ rhel9cis_rule_1_8_2: true rhel9cis_rule_1_8_3: true rhel9cis_rule_1_8_4: true rhel9cis_rule_1_8_5: true +rhel9cis_rule_1_8_6: true +rhel9cis_rule_1_8_7: true +rhel9cis_rule_1_8_8: true +rhel9cis_rule_1_8_9: true +rhel9cis_rule_1_8_10: true rhel9cis_rule_1_9: true rhel9cis_rule_1_10: true @@ -160,21 +162,16 @@ rhel9cis_rule_2_2_15: true rhel9cis_rule_2_2_16: true rhel9cis_rule_2_2_17: true rhel9cis_rule_2_2_18: true -rhel9cis_rule_2_2_19: true -rhel9cis_rule_2_2_20: true rhel9cis_rule_2_3_1: true rhel9cis_rule_2_3_2: true rhel9cis_rule_2_3_3: true rhel9cis_rule_2_3_4: true -rhel9cis_rule_2_3_5: true -rhel9cis_rule_2_3_6: true rhel9cis_rule_2_4: true - Section 3 rules +# Section 3 rules rhel9cis_rule_3_1_1: true rhel9cis_rule_3_1_2: true rhel9cis_rule_3_1_3: true -rhel9cis_rule_3_1_4: true rhel9cis_rule_3_2_1: true rhel9cis_rule_3_2_2: true rhel9cis_rule_3_3_1: true @@ -188,11 +185,6 @@ rhel9cis_rule_3_3_8: true rhel9cis_rule_3_3_9: true rhel9cis_rule_3_4_1_1: true rhel9cis_rule_3_4_1_2: true -rhel9cis_rule_3_4_1_3: true -rhel9cis_rule_3_4_1_4: true -rhel9cis_rule_3_4_1_5: true -rhel9cis_rule_3_4_1_6: true -rhel9cis_rule_3_4_1_7: true rhel9cis_rule_3_4_2_1: true rhel9cis_rule_3_4_2_2: true rhel9cis_rule_3_4_2_3: true @@ -200,11 +192,6 @@ rhel9cis_rule_3_4_2_4: true rhel9cis_rule_3_4_2_5: true rhel9cis_rule_3_4_2_6: true rhel9cis_rule_3_4_2_7: true -rhel9cis_rule_3_4_2_8: true -rhel9cis_rule_3_4_2_9: true -rhel9cis_rule_3_4_2_10: true -rhel9cis_rule_3_4_2_11: true - # Section 4 rules rhel9cis_rule_4_1_1_1: true @@ -235,6 +222,16 @@ rhel9cis_rule_4_1_3_18: true rhel9cis_rule_4_1_3_19: true rhel9cis_rule_4_1_3_20: true rhel9cis_rule_4_1_3_21: true +rhel9cis_rule_4_1_4_1: true +rhel9cis_rule_4_1_4_2: true +rhel9cis_rule_4_1_4_3: true +rhel9cis_rule_4_1_4_4: true +rhel9cis_rule_4_1_4_5: true +rhel9cis_rule_4_1_4_6: true +rhel9cis_rule_4_1_4_7: true +rhel9cis_rule_4_1_4_8: true +rhel9cis_rule_4_1_4_9: true +rhel9cis_rule_4_1_4_10: true rhel9cis_rule_4_2_1_1: true rhel9cis_rule_4_2_1_2: true rhel9cis_rule_4_2_1_3: true @@ -253,9 +250,7 @@ rhel9cis_rule_4_2_2_5: true rhel9cis_rule_4_2_2_6: true rhel9cis_rule_4_2_2_7: true rhel9cis_rule_4_2_3: true -rhel9cis_rule_4_3_1: true -rhel9cis_rule_4_3_2: true -rhel9cis_rule_4_3_3: true +rhel9cis_rule_4_3: true # Section 5 rules rhel9cis_rule_5_1_1: true @@ -310,6 +305,7 @@ rhel9cis_rule_5_6_2: true rhel9cis_rule_5_6_3: true rhel9cis_rule_5_6_4: true rhel9cis_rule_5_6_5: true +rhel9cis_rule_5_6_6: true # Section 6 rules rhel9cis_rule_6_1_1: true @@ -369,11 +365,18 @@ rhel9cis_rh_sub_password: password # RedHat Satellite Subscription items rhel9cis_rhnsd_required: false -# 1.4.2 Bootloader password -rhel9cis_bootloader_password_hash: 'grub.pbkdf2.sha512.changethispassword' -rhel9cis_bootloader_password: random -rhel9cis_set_boot_pass: false +# 1.2.4 repo_gpgcheck +rhel9cis_rhel_default_repo: true +# 1.4.2 Bootloader password +rhel9cis_bootloader_password_hash: 'grub.pbkdf2.sha512.10000.9306A36764A7BEA3BF492D1784396B27F52A71812E9955A58709F94EE70697F9BD5366F36E07DEC41B52279A056E2862A93E42069D7BBB08F5DFC2679CD43812.6C32ADA5449303AD5E67A4C150558592A05381331DE6B33463469A236871FA8E70738C6F9066091D877EF88A213C86825E093117F30E9E1BF158D0DB75E7581B' +rhel9cis_bootloader_password: random +rhel9cis_set_boot_pass: true + +# 1.8 Gnome Desktop +rhel9cis_dconf_db_name: local +rhel9cis_screensaver_idle_delay: 900 # Set max value for idle-delay in seconds (between 1 and 900) +rhel9cis_screensaver_lock_delay: 5 # Set max value for lock-delay in seconds (between 0 and 5) # 1.10 Set crypto policy DEFAULT # Control 1.10 states not to use LEGACY @@ -400,6 +403,8 @@ rhel9cis_aide_cron: # SELinux policy rhel9cis_selinux_pol: targeted +# chose onf or enfocing or permissive +rhel9cis_selinux_enforce: enforcing # Whether or not to run tasks related to auditing/patching the desktop environment @@ -417,13 +422,12 @@ rhel9cis_chrony_server_options: "minpoll 8" ### 2.2 Special Purposes ##### Service configuration booleans set true to keep service -rhel9cis_xinetd_server: false rhel9cis_gui: false rhel9cis_avahi_server: false rhel9cis_cups_server: false rhel9cis_dhcp_server: false rhel9cis_dns_server: false -rhel9cis_ftp_server: false +rhel9cis_dnsmasq_server: false rhel9cis_vsftpd_server: false rhel9cis_tftp_server: false rhel9cis_httpd_server: false @@ -433,7 +437,6 @@ rhel9cis_imap_server: false rhel9cis_samba_server: false rhel9cis_squid_server: false rhel9cis_snmp_server: false -rhel9cis_nis_server: false rhel9cis_telnet_server: false rhel9cis_is_mail_server: false # Note the options @@ -450,12 +453,10 @@ rhel9cis_use_rsync_server: false rhel9cis_use_rsync_service: false #### 2.3 Service clients -rhel9cis_ypbind_required: false -rhel9cis_rsh_required: false -rhel9cis_talk_required: false rhel9cis_telnet_required: false rhel9cis_openldap_clients_required: false rhel9cis_tftp_client: false +rhel9cis_ftp_client: false ## Section3 vars @@ -473,18 +474,20 @@ rhel9cis_firewall: firewalld ##### firewalld rhel9cis_default_zone: public -rhel9cis_firewalld_nftables_state: masked # Note if absent removes the firewalld pkg dependancy + + +# These are added to demonstrate how this can be done +rhel9cis_firewalld_ports: + - number: 80 + protocol: tcp #### nftables -rhel9cis_nftables_firewalld_state: masked rhel9cis_nft_tables_autonewtable: true rhel9cis_nft_tables_tablename: filter rhel9cis_nft_tables_autochaincreate: true - # Warning Banner Content (issue, issue.net, motd) -rhel9cis_warning_banner: | - Authorized uses only. All activity may be monitored and reported. +rhel9cis_warning_banner: Authorized uses only. All activity may be monitored and reported. # End Banner ## Section4 vars @@ -518,10 +521,17 @@ rhel9cis_auditd_extra_conf: {} ## Preferred method of logging ## Whether rsyslog or journald preferred method for local logging ## Affects rsyslog cis 4.2.1.3 and journald cis 4.2.2.5 -rhel9cis_preferred_log_capture: rsyslog +rhel9cis_syslog: rsyslog +rhel9cis_rsyslog_ansiblemanaged: true #### 4.2.1.6 remote and destation log server name -rhel9cis_remote_log_server: logagg.example.com +rhel9cis_remote_log_server: false +rhel9cis_remote_log_host: logagg.example.com +rhel9cis_remote_log_port: 514 +rhel9cis_remote_log_protocol: tcp +rhel9cis_remote_log_retrycount: 100 +rhel9cis_remote_log_queuesize: 1000 + #### 4.2.1.7 rhel9cis_system_is_log_server: false @@ -588,9 +598,6 @@ rhel9cis_pass: max_days: 365 min_days: 7 warn_age: 7 -# Syslog system - either rsyslog or syslog-ng -rhel9cis_syslog: rsyslog -rhel9cis_rsyslog_ansiblemanaged: true # 5.5.1 ## PAM @@ -615,6 +622,14 @@ rhel9cis_sudolog_location: "/var/log/sudo.log" #### 5.3.6 rhel9cis_sudo_timestamp_timeout: 15 +### 5.4.2 authselect and faillock +## This option is used at your own risk it will enable faillock for users +## Only to be used on a new clean system if not using authselect +## THIS CAN BREAK ACCESS EVEN FOR ROOT - UNDERSTAND RISKS ## +rhel9cis_add_faillock_without_authselect: false +# This needs to be set to ACCEPT +rhel9cis_5_4_2_risks: NEVER + # RHEL-09-5.4.5 # Session timeout setting file (TMOUT setting can be set in multiple files) # Timeout value is in seconds. (60 seconds * 10 = 600) @@ -627,7 +642,7 @@ rhel9cis_futurepwchgdate_autofix: true # 5.7 # rhel9cis_sugroup: sugroup # change accordingly wheel is default -# wheel users list +# wheel users list please supply comma seperated e.g. "vagrant,root" rhel9cis_sugroup_users: "root" ## Section6 vars @@ -650,14 +665,14 @@ audit_run_script_environment: ### Goss binary settings ### goss_version: - release: v0.3.18 - checksum: 'sha256:432308ebca0caf8165d45bd27e3262126aad9d15572ac8cb3149b3c91f75aace' + 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/aelsabbahy/goss/releases/download/{{ goss_version.release }}/goss-linux-amd64" +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 diff --git a/handlers/main.yml b/handlers/main.yml index 0fae419..212eacc 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,16 +1,13 @@ --- # handlers file for RHEL9-CIS -- name: reload sysctl - shell: sysctl --system - args: - warn: false +- name: Reload sysctl + ansible.builtin.shell: sysctl --system when: - sysctl_updated.changed -- name: sysctl flush ipv4 route table - become: true - sysctl: +- name: Sysctl flush ipv4 route table + ansible.posix.sysctl: name: net.ipv4.route.flush value: '1' sysctl_set: true @@ -18,12 +15,9 @@ when: - flush_ipv4_route - not system_is_container - tags: - - skip_ansible_lint -- name: sysctl flush ipv6 route table - become: true - sysctl: +- name: Sysctl flush ipv6 route table + ansible.posix.sysctl: name: net.ipv6.route.flush value: '1' sysctl_set: true @@ -31,98 +25,81 @@ - flush_ipv6_route - not system_is_container -- name: systemd restart tmp.mount - become: true - systemd: +- name: Systemd restart tmp.mount + ansible.builtin.systemd: name: tmp.mount - daemon_reload: true + daemon_Reload: true enabled: true masked: false - state: reloaded + state: Reloaded -- name: systemd restart var-tmp.mount - become: true - systemd: - name: var-tmp.mount - daemon_reload: true - enabled: true - masked: false - state: reloaded +- name: Remount tmp + ansible.posix.mount: + path: /tmp + state: remounted -- name: remount tmp - shell: mount -o remount /tmp - args: - warn: false - -- name: restart firewalld - service: +- name: Restart firewalld + ansible.builtin.systemd: name: firewalld state: restarted -- name: restart sshd - service: +- name: Restart sshd + ansible.builtin.systemd: name: sshd state: restarted -- name: restart postfix - service: +- name: Restart postfix + ansible.builtin.systemd: name: postfix state: restarted -- name: reload dconf - shell: dconf update - args: - warn: false +- name: Reload dconf + ansible.builtin.shell: dconf update -- name: grub2cfg - shell: "grub2-mkconfig -o /boot/grub2/grub.cfg" - args: - warn: false +- name: Grub2cfg + ansible.builtin.shell: "grub2-mkconfig -o /boot/grub2/grub.cfg" ignore_errors: true # noqa ignore-errors tags: - skip_ansible_lint -- name: restart rsyslog - become: true - service: +- name: Restart rsyslog + ansible.builtin.systemd: name: rsyslog state: restarted -- name: restart journald - service: +- name: Restart journald + ansible.builtin.systemd: name: systemd-journald state: restarted -- name: restart systemd_journal_upload - service: +- name: Restart systemd_journal_upload + ansible.builtin.systemd: name: systemd-journal-upload state: restarted -- name: systemd_daemon_reload - systemd: +- name: Systemd daemon reload + ansible.builtin.systemd: daemon-reload: true ## Auditd tasks note order for handlers to run -- name: auditd_immutable_check - shell: grep -c "^-e 2" /etc/audit/rules.d/99_auditd.rules +- name: Auditd immutable check + ansible.builtin.shell: grep -c "^-e 2" /etc/audit/rules.d/99_auditd.rules changed_when: false register: auditd_immutable_check -- name: audit_immutable_fact - debug: +- name: Audit immutable fact + ansible.builtin.debug: msg: "Reboot required for auditd to apply new rules as immutable set" - notify: change_requires_reboot + notify: Change_requires_reboot when: - auditd_immutable_check.stdout == '1' -- name: restart auditd - shell: service auditd restart - args: - warn: false +- name: Restart auditd + ansible.builtin.shell: service auditd restart tags: - skip_ansible_lint -- name: change_requires_reboot - set_fact: +- name: Change_requires_reboot + ansible.builtin.set_fact: change_requires_reboot: true diff --git a/meta/main.yml b/meta/main.yml index b4a804e..c60c6a7 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,7 +1,7 @@ --- galaxy_info: author: "Sam Doran, Josh Springer, Daniel Shepherd, Bas Meijeri, James Cassell, Mike Renfro, DFed, George Nalen, Mark Bolwell" - description: "Apply the RHEL 8 CIS" + description: "Apply the RHEL 9 CIS" company: "MindPoint Group" license: MIT role_name: rhel9_cis @@ -10,7 +10,7 @@ galaxy_info: platforms: - name: EL versions: - - 9 + - "9" galaxy_tags: - system - security @@ -22,6 +22,9 @@ galaxy_info: - complianceascode - disa - rhel9 + - cis + - rocky + - alma collections: - community.general - community.crypto diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..52cb84d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +passlib +lxml +xmltodict +jmespath +yamllint diff --git a/site.yml b/site.yml index 4446d3e..148ca0b 100644 --- a/site.yml +++ b/site.yml @@ -1,4 +1,5 @@ --- + - hosts: all become: true roles: diff --git a/tasks/LE_audit_setup.yml b/tasks/LE_audit_setup.yml index 98f3855..bc929ae 100644 --- a/tasks/LE_audit_setup.yml +++ b/tasks/LE_audit_setup.yml @@ -1,7 +1,7 @@ --- - name: Download audit binary - get_url: + ansible.builtin.get_url: url: "{{ goss_url }}" dest: "{{ audit_bin }}" owner: root @@ -11,8 +11,8 @@ when: - get_goss_file == 'download' -- name: copy audit binary - copy: +- name: Copy audit binary + ansible.builtin.copy: src: dest: "{{ audit_bin }}" mode: 0555 @@ -21,8 +21,8 @@ when: - get_goss_file == 'copy' -- name: install git if not present - package: +- name: Install git if not present + ansible.builtin.package: name: git state: present register: git_installed diff --git a/tasks/auditd.yml b/tasks/auditd.yml index 74830ca..f2dd122 100644 --- a/tasks/auditd.yml +++ b/tasks/auditd.yml @@ -1,26 +1,37 @@ --- +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file + ansible.builtin.stat: + path: /etc/audit/rules.d/99_auditd.rules + register: auditd_file -- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added - template: +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | setup file + ansible.builtin.template: src: audit/99_auditd.rules.j2 dest: /etc/audit/rules.d/99_auditd.rules owner: root group: root - mode: 0600 + mode: 0640 + diff: "{{ auditd_file.stat.exists }}" # Only run diff if not a new file register: audit_rules_updated notify: - - auditd_immutable_check - - audit_immutable_fact - - restart auditd + - Auditd immutable check + - Audit immutable fact + - Restart auditd -- name: POST | Set up auditd user logging exceptions - template: +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file + ansible.builtin.stat: + path: /etc/audit/rules.d/98_auditd_exceptions.rules + register: auditd_exception_file + +- name: POST | Set up auditd user logging exceptions | setup file + ansible.builtin.template: src: audit/98_auditd_exception.rules.j2 dest: /etc/audit/rules.d/98_auditd_exceptions.rules owner: root group: root - mode: 0600 - notify: restart auditd + mode: 0640 + diff: "{{ auditd_exception_file.stat.exists }}" + notify: Restart auditd when: - allow_auditd_uid_user_exclusions - rhel9cis_auditd_uid_exclude | length > 0 diff --git a/tasks/check_prereqs.yml b/tasks/check_prereqs.yml index abe6248..dcfee57 100644 --- a/tasks/check_prereqs.yml +++ b/tasks/check_prereqs.yml @@ -1,7 +1,7 @@ --- - name: "PREREQ | If required install libselinux package to manage file changes." - package: + ansible.builtin.package: name: libselinux-python3 state: present when: diff --git a/tasks/main.yml b/tasks/main.yml index 0d272b1..d083319 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -2,7 +2,7 @@ # tasks file for RHEL9-CIS - name: Check OS version and family - assert: + ansible.builtin.assert: that: (ansible_distribution != 'CentOS' and ansible_os_family == 'RedHat' or ansible_os_family == "Rocky") and ansible_distribution_major_version is version_compare('9', '==') fail_msg: "This role can only be run against Supported OSs. {{ ansible_distribution }} {{ ansible_distribution_major_version }} is not supported." success_msg: "This role is running against a supported OS {{ ansible_distribution }} {{ ansible_distribution_major_version }}" @@ -13,7 +13,7 @@ - always - name: Check ansible version - assert: + ansible.builtin.assert: that: ansible_version.full is version_compare(min_ansible_version, '>=') fail_msg: "You must use Ansible {{ min_ansible_version }} or greater" success_msg: "This role is running a supported version of ansible {{ ansible_version.full }} >= {{ min_ansible_version }}" @@ -23,14 +23,14 @@ - name: "Check password set for {{ ansible_user }}" block: - name: Capture current password state of "{{ ansible_user }}" - shell: "grep {{ ansible_user }} /etc/shadow | awk -F: '{print $2}'" + ansible.builtin.shell: "grep {{ ansible_user }} /etc/shadow | awk -F: '{print $2}'" changed_when: false failed_when: false check_mode: false register: ansible_user_password_set - name: "Assert that password set for {{ ansible_user }} and account not locked" - assert: + ansible.builtin.assert: that: ansible_user_password_set.stdout | length != 0 and ansible_user_password_set.stdout != "!!" fail_msg: "You have {{ sudo_password_rule }} enabled but the user = {{ ansible_user }} has no password set - It can break access" success_msg: "You a password set for the {{ ansible_user }}" @@ -42,18 +42,40 @@ tags: - user_passwd +- name: "Ensure root password is set" + block: + - name: "Ensure root password is set" + ansible.builtin.shell: passwd -S root | grep "Password set, SHA512 crypt" + changed_when: false + register: root_passwd_set + + - name: "Ensure root password is set" + ansible.builtin.assert: + that: root_passwd_set.rc == 0 + fail_msg: "You have rule 5.6.6 enabled this requires that you have a root password set" + success_msg: "You have a root password set" + when: + - rhel9cis_rule_5_6_6 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - root + - rule_5.6.6 + - name: Setup rules if container block: - name: Discover and set container variable if required - set_fact: + ansible.builtin.set_fact: system_is_container: true - name: Load variable for container - include_vars: + ansible.builtin.include_vars: file: "{{ container_vars_file }}" - - name: output if discovered is a container - debug: + - name: Output if discovered is a container + ansible.builtin.debug: msg: system has been discovered as a container when: - system_is_container @@ -65,13 +87,13 @@ - always - name: Check crypto-policy input - assert: + ansible.builtin.assert: that: rhel9cis_crypto_policy in rhel9cis_allowed_crypto_policies fail_msg: "Crypto policy is not a permitted version" success_msg: "Crypto policy is a permitted version" - name: Check rhel9cis_bootloader_password_hash variable has been changed - assert: + ansible.builtin.assert: that: rhel9cis_bootloader_password_hash.find('grub.pbkdf2.sha512') != -1 and rhel9cis_bootloader_password_hash != 'grub.pbkdf2.sha512.changethispassword' msg: "This role will not be able to run single user password commands as rhel9cis_bootloader_password_hash variable has not been set correctly" when: @@ -80,132 +102,90 @@ tags: - always -- name: "check sugroup exists if used" - block: - - name: "Check su group exists if defined" - shell: grep -w "{{ rhel9cis_sugroup }}" /etc/group - args: - warn: false - register: sugroup_exists - changed_when: false - failed_when: sugroup_exists.rc >= 2 - tags: - - skip_ansible_lint - - - name: Check sugroup if defined exists before continuing - assert: - that: sugroup_exists.rc == 0 - msg: "The variable rhel9cis_sugroup is defined but does not exist please rectify" - when: - - rhel9cis_sugroup is defined - - rhel9cis_rule_5_7 - tags: - - rule_5.7 - name: Gather the package facts - package_facts: + ansible.builtin.package_facts: manager: auto tags: - always - name: Include OS specific variables - include_vars: "{{ ansible_distribution }}.yml" + ansible.builtin.include_vars: "{{ ansible_distribution }}.yml" tags: - always - name: Include preliminary steps - import_tasks: prelim.yml + ansible.builtin.import_tasks: prelim.yml tags: - prelim_tasks - always - name: run pre_remediation audit - include_tasks: pre_remediation_audit.yml + ansible.builtin.include_tasks: pre_remediation_audit.yml when: - run_audit -- name: Gather the package facts after prelim - package_facts: - manager: auto - tags: - - always - -- name: capture /etc/password variables - include_tasks: parse_etc_password.yml - when: - - rhel9cis_section6 - tags: - - rule_5.5.2 - - rule_5.6.2 - - rule_6.2.9 - - rule_6.2.10 - - rule_6.2.11 - - rhel9cis_section5 - - rhel9cis_section6 - - name: run Section 1 tasks - import_tasks: section_1/main.yml + ansible.builtin.import_tasks: section_1/main.yml when: rhel9cis_section1 tags: - rhel9cis_section1 - name: run Section 2 tasks - import_tasks: section_2/main.yml + ansible.builtin.import_tasks: section_2/main.yml when: rhel9cis_section2 tags: - rhel9cis_section2 - name: run Section 3 tasks - import_tasks: section_3/main.yml + ansible.builtin.import_tasks: section_3/main.yml when: rhel9cis_section3 tags: - rhel9cis_section3 - name: run Section 4 tasks - import_tasks: section_4/main.yml + ansible.builtin.import_tasks: section_4/main.yml when: rhel9cis_section4 tags: - rhel9cis_section4 - name: run Section 5 tasks - import_tasks: section_5/main.yml + ansible.builtin.import_tasks: section_5/main.yml when: rhel9cis_section5 tags: - rhel9cis_section5 - name: run Section 6 tasks - import_tasks: section_6/main.yml + ansible.builtin.import_tasks: section_6/main.yml when: rhel9cis_section6 tags: - rhel9cis_section6 - name: run auditd logic - import_tasks: auditd.yml - when: - - update_audit_template + ansible.builtin.import_tasks: auditd.yml + when: update_audit_template tags: - always - name: run post remediation tasks - import_tasks: post.yml + ansible.builtin.import_tasks: post.yml tags: - post_tasks - always - name: run post_remediation audit - import_tasks: post_remediation_audit.yml + ansible.builtin.import_tasks: post_remediation_audit.yml when: - run_audit - name: Show Audit Summary - debug: + ansible.builtin.debug: msg: "{{ audit_results.split('\n') }}" - when: - - run_audit + when: run_audit - name: If Warnings found Output count and control IDs affected - debug: - msg: "You have {{ warn_count }} Warning(s) that require investigating that are related to the following benchmark ID(s) {{ control_number }}" + ansible.builtin.debug: + msg: "You have {{ warn_count }} Warning(s) that require investigating that are related to the following benchmark ID(s) {{ warn_control_list }}" when: warn_count != 0 tags: - always diff --git a/tasks/parse_etc_password.yml b/tasks/parse_etc_password.yml index 6a9ef7b..8ff13fd 100644 --- a/tasks/parse_etc_password.yml +++ b/tasks/parse_etc_password.yml @@ -3,15 +3,15 @@ - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" block: - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" - shell: cat /etc/passwd + ansible.builtin.shell: cat /etc/passwd changed_when: false check_mode: false register: rhel9cis_passwd_file_audit - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" - set_fact: + ansible.builtin.set_fact: rhel9cis_passwd: "{{ rhel9cis_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" - with_items: "{{ rhel9cis_passwd_file_audit.stdout_lines }}" + loop: "{{ rhel9cis_passwd_file_audit.stdout_lines }}" vars: ld_passwd_regex: >- ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) diff --git a/tasks/post.yml b/tasks/post.yml index 3b5c3f2..3a2426e 100644 --- a/tasks/post.yml +++ b/tasks/post.yml @@ -1,27 +1,22 @@ --- # Post tasks -- name: Perform DNF package cleanup - dnf: - autoremove: true - changed_when: false - - name: Gather the package facts after remediation - package_facts: + ansible.builtin.package_facts: manager: auto tags: - always -- name: update sysctl - template: +- name: Update sysctl + ansible.builtin.template: src: "etc/sysctl.d/{{ item }}.j2" dest: "/etc/sysctl.d/{{ item }}" owner: root group: root mode: 0600 register: sysctl_updated - notify: reload sysctl - with_items: + notify: Reload sysctl + loop: - 60-kernel_sysctl.conf - 60-disable_ipv6.conf - 60-netipv4_sysctl.conf @@ -31,19 +26,19 @@ - not system_is_container - "'procps-ng' in ansible_facts.packages" -- name: flush handlers - meta: flush_handlers +- name: Flush handlers + ansible.builtin.meta: flush_handlers - name: POST | reboot system if changes require it and not skipped block: - name: POST | Reboot system if changes require it and not skipped - reboot: + ansible.builtin.reboot: when: - change_requires_reboot - not skip_reboot - name: POST | Warning a reboot required but skip option set - debug: + ansible.builtin.debug: msg: "Warning!! changes have been made that require a reboot to be implemented but skip reboot was set - Can affect compliance check results" changed_when: true when: @@ -51,13 +46,12 @@ - skip_reboot - name: "POST | Warning a reboot required but skip option set | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'Reboot_required' ]" - warn_count: "{{ warn_count | int + 1 }}" + ansible.builtin.import_tasks: warning_facts.yml when: - change_requires_reboot - skip_reboot - + vars: + warn_control_id: Reboot_required tags: - grub - level1-server diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index 4429b7e..0eb7608 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,15 +1,13 @@ --- - name: "Post Audit | Run post_remediation {{ benchmark }} audit" - shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" environment: "{{ audit_run_script_environment | default({}) }}" changed_when: audit_run_post_remediation.rc == 0 register: audit_run_post_remediation - args: - warn: false - name: Post Audit | ensure audit files readable by users - file: + ansible.builtin.file: path: "{{ item }}" mode: 0644 state: file @@ -19,15 +17,13 @@ - name: Post Audit | Capture audit data if json format block: - - name: "capture data {{ post_audit_outfile }}" - shell: "cat {{ post_audit_outfile }}" - args: - warn: false + - name: "Capture data {{ post_audit_outfile }}" + ansible.builtin.shell: "cat {{ post_audit_outfile }}" register: post_audit changed_when: false - name: Capture post-audit result - set_fact: + ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout | from_json | json_query(summary) }}" vars: summary: 'summary."summary-line"' @@ -37,14 +33,12 @@ - name: Post Audit | Capture audit data if documentation format block: - name: "Post Audit | capture data {{ post_audit_outfile }}" - shell: "tail -2 {{ post_audit_outfile }}" - args: - warn: false + ansible.builtin.shell: "tail -2 {{ post_audit_outfile }}" register: post_audit changed_when: false - name: Post Audit | Capture post-audit result - set_fact: + 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 93c4985..c05ddb3 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -1,20 +1,20 @@ --- - name: Pre Audit | Setup the audit - include_tasks: LE_audit_setup.yml + ansible.builtin.include_tasks: LE_audit_setup.yml when: - setup_audit tags: - setup_audit - name: "Pre Audit | Ensure {{ audit_conf_dir }} exists" - file: + ansible.builtin.file: path: "{{ audit_conf_dir }}" state: directory mode: '0755' - name: Pre Audit | retrieve audit content files from git - git: + ansible.builtin.git: repo: "{{ audit_file_git }}" dest: "{{ audit_conf_dir }}" version: "{{ audit_git_version }}" @@ -22,7 +22,7 @@ - audit_content == 'git' - name: Pre Audit | copy to audit content files to server - copy: + ansible.builtin.copy: src: "{{ audit_local_copy }}" dest: "{{ audit_conf_dir }}" mode: 0644 @@ -30,7 +30,7 @@ - audit_content == 'copy' - name: Pre Audit | get audit content from url - get_url: + ansible.builtin.get_url: url: "{{ audit_files_url }}" dest: "{{ audit_conf_dir }}" owner: root @@ -42,27 +42,26 @@ - name: Pre Audit | Check Goss is available block: - name: Pre Audit | Check for goss file - stat: + ansible.builtin.stat: path: "{{ audit_bin }}" register: goss_available - name: Pre Audit | Alert if goss not available - assert: + ansible.builtin.assert: that: goss_available.stat.exists fail_msg: "Audit binary file {{ audit_bin }} does not exist" - success_msg: "Audit binary file {{ audit_bin }} exists" when: - run_audit - name: "Pre Audit | Check whether machine is UEFI-based" - stat: + ansible.builtin.stat: path: /sys/firmware/efi register: rhel9_efi_boot tags: - goss_template - name: Pre Audit | Copy ansible default vars values to test audit - template: + ansible.builtin.template: src: ansible_vars_goss.yml.j2 dest: "{{ audit_vars_path }}" mode: 0600 @@ -72,24 +71,20 @@ - goss_template - name: "Pre Audit | Run pre_remediation {{ benchmark }} audit" - shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" environment: "{{ audit_run_script_environment | default({}) }}" changed_when: audit_run_pre_remediation.rc == 0 register: audit_run_pre_remediation - args: - warn: false - name: Pre Audit | Capture audit data if json format block: - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - shell: "cat {{ pre_audit_outfile }}" - args: - warn: false + ansible.builtin.shell: "cat {{ pre_audit_outfile }}" register: pre_audit changed_when: false - name: Pre Audit | Capture pre-audit result - set_fact: + ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout | from_json | json_query(summary) }}" vars: summary: 'summary."summary-line"' @@ -99,14 +94,12 @@ - name: Pre Audit | Capture audit data if documentation format block: - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - shell: "tail -2 {{ pre_audit_outfile }}" - args: - warn: false + ansible.builtin.shell: "tail -2 {{ pre_audit_outfile }}" register: pre_audit changed_when: false - name: Pre Audit | Capture pre-audit result - set_fact: + ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout_lines }}" when: - audit_format == "documentation" diff --git a/tasks/prelim.yml b/tasks/prelim.yml index 80a273b..18e0c17 100644 --- a/tasks/prelim.yml +++ b/tasks/prelim.yml @@ -3,9 +3,7 @@ # Preliminary tasks that should always be run # List users in order to look files inside each home directory - name: "PRELIM | List users accounts" - shell: "awk -F: '{print $1}' /etc/passwd" - args: - warn: false + ansible.builtin.shell: "awk -F: '{print $1}' /etc/passwd" changed_when: false check_mode: false register: users @@ -14,10 +12,27 @@ - level1-workstation - users +- name: "PRELIM | capture /etc/password variables" + ansible.builtin.include_tasks: parse_etc_password.yml + tags: + - rule_5.5.2 + - rule_5.6.2 + - rule_6.2.9 + - rule_6.2.10 + - rule_6.2.11 + - rhel9cis_section5 + - rhel9cis_section6 + - level1-server + +- name: "PRELIM | Interactive User accounts" + ansible.builtin.shell: 'cat /etc/passwd | grep -Ev "nologin|/sbin" | cut -d: -f6' + changed_when: false + register: interactive_users_home + tags: + - always + - name: "PRELIM | Gather accounts with empty password fields" - shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" - args: - warn: false + ansible.builtin.shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" changed_when: false check_mode: false register: empty_password_accounts @@ -27,14 +42,12 @@ - passwords - name: "PRELIM | Gather UID 0 accounts other than root" - shell: "cat /etc/passwd | awk -F: '($3 == 0 && $1 != \"root\") {i++;print $1 } END {exit i}'" - args: - warn: false + ansible.builtin.shell: "cat /etc/passwd | awk -F: '($3 == 0 && $1 != \"root\") {i++;print $1 } END {exit i}'" changed_when: false check_mode: false register: rhel9cis_uid_zero_accounts_except_root tags: - - rule_6.2.8 + - rule_6.2.9 - level1-server - level1-workstation - users @@ -42,16 +55,14 @@ - name: "PRELIM | Setup crypto-policy" block: - name: "PRELIM | Install crypto-policies" - dnf: + ansible.builtin.package: name: - crypto-policies - crypto-policies-scripts state: present - name: "PRELIM | Gather system-wide crypto-policy" - shell: update-crypto-policies --show - args: - warn: false + ansible.builtin.shell: update-crypto-policies --show changed_when: false check_mode: false register: system_wide_crypto_policy @@ -64,7 +75,7 @@ - crypto - name: "PRELIM | if systemd coredump" - stat: + ansible.builtin.stat: path: /etc/systemd/coredump.conf register: systemd_coredump when: @@ -76,14 +87,14 @@ - systemd - name: "PRELIM | Section 1.1 | Create list of mount points" - set_fact: + ansible.builtin.set_fact: mount_names: "{{ ansible_mounts | map(attribute='mount') | list }}" tags: - level1-server - level1-workstation - name: "PRELIM | Ensure python3-libselinux is installed" - package: + ansible.builtin.package: name: python3-libselinux state: present when: @@ -92,23 +103,31 @@ - name: "PRELIM | Set facts based on boot type" block: - name: "PRELIM | Check whether machine is UEFI-based" - stat: + ansible.builtin.stat: path: /sys/firmware/efi register: rhel_09_efi_boot - - name: "PRELIM | AUDIT | set legacy boot and grub path | Bios" - set_fact: + - name: "PRELIM | set legacy boot and grub path | Bios" + ansible.builtin.set_fact: rhel9cis_legacy_boot: true grub2_path: /etc/grub2.cfg when: not rhel_09_efi_boot.stat.exists - name: "PRELIM | set grub fact | UEFI" - set_fact: + ansible.builtin.set_fact: grub2_path: /etc/grub2-efi.cfg when: rhel_09_efi_boot.stat.exists +- name: "PRELIM | Update to latest gpg keys" + ansible.builtin.package: + name: "{{ ansible_distribution | lower }}-gpg-keys" + state: latest + when: + - rhel9cis_rule_1_2_4 + - ansible_distribution != 'RedHat' + - name: "PRELIM | Section 4.1 | Configure System Accounting (auditd)" - package: + ansible.builtin.package: name: audit state: present become: true @@ -122,19 +141,28 @@ - rule_4.1.1.1 - auditd -- name: "PRELIM | 4.1.12 | Ensure successful file system mounts are collected" - shell: for i in $(df | grep '^/dev' | awk '{ print $NF }'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done - changed_when: false - failed_when: false - check_mode: false - register: priv_procs +- name: "PRELIM | 4.1.4.5 | Audit conf and rules files | list files" + ansible.builtin.find: + path: /etc/audit + file_type: file + recurse: true + patterns: '*.conf,*.rules' + register: auditd_conf_files + when: + - rhel9cis_rule_4_1_4_5 or + rhel9cis_rule_4_1_4_6 or + rhel9cis_rule_4_1_4_7 tags: - - level1-server - - level1-workstation - - always + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.5 + - rule_4.1.4.6 + - rule_4.1.4.7 - name: "PRELIM | Section 5.1 | Configure cron" - package: + ansible.builtin.package: name: cronie state: present become: true @@ -148,7 +176,7 @@ - cron - name: "PRELIM | Install authconfig" - package: + ansible.builtin.package: name: authconfig state: present become: true @@ -169,7 +197,7 @@ - auditd - name: "PRELIM | 5.3.4 | Find all sudoers files." - command: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" + ansible.builtin.shell: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" changed_when: false failed_when: false check_mode: false @@ -181,45 +209,58 @@ - rule_5.3.4 - rule_5.3.5 -- name: "PRELIM | Check for rhnsd service" - shell: "systemctl show rhnsd | grep LoadState | cut -d = -f 2" - changed_when: false - check_mode: false - become: true - register: rhnsd_service_status - when: - - rhel9cis_rule_1_2_2 - - ansible_distribution == "RedHat" - tags: - - rule_1.2.2 - - skip_ansible_lint - -- name: "PRELIM | AUDIT | Discover Interactive UID MIN and MIN from logins.def" +- name: "PRELIM | Check sugroup exists if used" block: - - name: "PRELIM | AUDIT | Capture UID_MIN information from logins.def" - shell: grep -w "^UID_MIN" /etc/login.defs | awk '{print $NF}' + - name: "PRELIM | Check su group exists if defined" + ansible.builtin.shell: grep -w "{{ rhel9cis_sugroup }}" /etc/group + register: sugroup_exists + changed_when: false + failed_when: sugroup_exists.rc >= 2 + tags: + - skip_ansible_lint + + - name: "PRELIM | Check sugroup if defined exists before continuing" + ansible.builtin.assert: + that: sugroup_exists.rc == 0 + msg: "The variable rhel9cis_sugroup is defined but does not exist please rectify" + when: + - rhel9cis_sugroup is defined + - rhel9cis_rule_5_7 + tags: + - rule_5.7 + +- name: "PRELIM | Discover Interactive UID MIN and MIN from logins.def" + block: + - name: "PRELIM | Capture UID_MIN information from logins.def" + ansible.builtin.shell: grep -w "^UID_MIN" /etc/login.defs | awk '{print $NF}' changed_when: false register: uid_min_id - - name: "PRELIM | AUDIT | Capture UID_MAX information from logins.def" - shell: grep -w "^UID_MAX" /etc/login.defs | awk '{print $NF}' + - name: "PRELIM | Capture UID_MAX information from logins.def" + ansible.builtin.shell: grep -w "^UID_MAX" /etc/login.defs | awk '{print $NF}' changed_when: false register: uid_max_id - - name: "PRELIM | AUDIT | Capture GID_MIN information from logins.def" - shell: grep -w "^GID_MIN" /etc/login.defs | awk '{print $NF}' + - name: "PRELIM | Capture GID_MIN information from logins.def" + ansible.builtin.shell: grep -w "^GID_MIN" /etc/login.defs | awk '{print $NF}' changed_when: false register: gid_min_id - - name: "PRELIM | AUDIT | set_facts for interactive uid/gid" - set_fact: + - name: "PRELIM | set_facts for interactive uid/gid" + ansible.builtin.set_fact: min_int_uid: "{{ uid_min_id.stdout }}" max_int_uid: "{{ uid_max_id.stdout }}" min_int_gid: "{{ gid_min_id.stdout }}" -- name: Output of uid findings - debug: +- name: "PRELIM | Output of uid findings" + ansible.builtin.debug: msg: "{{ min_int_uid }} {{ max_int_uid }}" when: - not discover_int_uid + +- name: "PRELIM | Gather the package facts after prelim" + ansible.builtin.package_facts: + manager: auto + tags: + - always diff --git a/tasks/section_1/cis_1.1.1.x.yml b/tasks/section_1/cis_1.1.1.x.yml index 1c99b62..7a88f6f 100644 --- a/tasks/section_1/cis_1.1.1.x.yml +++ b/tasks/section_1/cis_1.1.1.x.yml @@ -1,76 +1,66 @@ --- -- name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled" +- name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled" block: - - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Edit modprobe config" - lineinfile: - path: /etc/modprobe.d/CIS.conf - regexp: "^(#)?install cramfs(\\s|$)" - line: "install cramfs /bin/true" - create: true - mode: 0600 - - - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Disable cramfs" - modprobe: - name: cramfs - state: absent - when: not system_is_container - when: - - rhel9cis_rule_1_1_1_1 - tags: - - level1-server - - level1-workstation - - automated - - patch - - rule_1.1.1.1 - - cramfs - -- name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled" - block: - - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled | Edit modprobe config" - lineinfile: + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Edit modprobe config" + ansible.builtin.lineinfile: path: /etc/modprobe.d/CIS.conf regexp: "^(#)?install squashfs(\\s|$)" line: "install squashfs /bin/true" create: true mode: 0600 - - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled | Disable squashfs" - modprobe: + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist squashfs(\\s|$)" + line: "blacklist squashfs" + create: true + mode: 0600 + + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Disable squashfs" + community.general.modprobe: name: squashfs state: absent when: not system_is_container + + when: + - rhel9cis_rule_1_1_1_1 + tags: + - level2-server + - level2-workstation + - patch + - rule_1.1.1.1 + - squashfs + +- name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disabled" + block: + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disable | Edit modprobe config" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install udf(\\s|$)" + line: "install udf /bin/true" + create: true + mode: 0600 + + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist udf(\\s|$)" + line: "blacklist udf" + create: true + mode: 0600 + + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disable | Disable udf" + community.general.modprobe: + name: udf + state: absent + when: not system_is_container when: - rhel9cis_rule_1_1_1_2 tags: - level2-server - level2-workstation - - automated - patch - rule_1.1.1.2 - - squashfs - -- name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disabled" - block: - - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disable | Edit modprobe config" - lineinfile: - path: /etc/modprobe.d/CIS.conf - regexp: "^(#)?install udf(\\s|$)" - line: "install udf /bin/true" - create: true - mode: 0600 - - - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disable | Disable udf" - modprobe: - name: udf - state: absent - when: not system_is_container - when: - - rhel9cis_rule_1_1_1_3 - tags: - - level2-server - - level2-workstation - - automated - - patch - - rule_1.1.1.3 - udf diff --git a/tasks/section_1/cis_1.1.2.x.yml b/tasks/section_1/cis_1.1.2.x.yml index d7db5a6..b4e1888 100644 --- a/tasks/section_1/cis_1.1.2.x.yml +++ b/tasks/section_1/cis_1.1.2.x.yml @@ -3,32 +3,20 @@ - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition" block: - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition | Absent" - debug: - msg: "Warning!! /tmp is not mounted on a separate partition" - when: - - required_mount not in mount_names + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.2.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - - - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: var_mount_present - when: - - required_mount in mount_names + - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition | Present" + ansible.builtin.import_tasks: warning_facts.yml vars: + warn_control_id: '1.1.2.1' required_mount: '/tmp' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_2_1 tags: - level1-server - level1-workstation - - automated - audit - mounts - rule_1.1.2.1 @@ -38,15 +26,14 @@ "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition" "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition" "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition" - mount: + ansible.builtin.mount: name: /tmp src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present opts: defaults,{% if rhel9cis_rule_1_1_2_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_2_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_2_4 %}nosuid{% endif %} - notify: remount tmp - with_items: - - "{{ ansible_mounts }}" + notify: Remount tmp + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" when: @@ -58,7 +45,6 @@ tags: - level1-server - level1-workstation - - automated - patch - mounts - rule_1.1.2.2 @@ -71,13 +57,13 @@ "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition" "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition" "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition" - template: + ansible.builtin.template: src: etc/systemd/system/tmp.mount.j2 dest: /etc/systemd/system/tmp.mount owner: root group: root mode: 0644 - notify: systemd restart tmp.mount + notify: Systemd restart tmp.mount when: - rhel9cis_tmp_svc - rhel9cis_rule_1_1_2_1 or diff --git a/tasks/section_1/cis_1.1.3.x.yml b/tasks/section_1/cis_1.1.3.x.yml index 9e4feb8..d873c51 100644 --- a/tasks/section_1/cis_1.1.3.x.yml +++ b/tasks/section_1/cis_1.1.3.x.yml @@ -3,34 +3,20 @@ - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var" block: - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Absent" - debug: + ansible.builtin.debug: msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - register: var_mount_absent - changed_when: var_mount_absent.skipped is undefined - when: - - required_mount not in mount_names - - - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.3.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: var_mount_present - when: - - required_mount in mount_names + ansible.builtin.import_tasks: warning_facts.yml vars: + warn_control_id: '1.1.3.1' required_mount: '/var' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_3_1 tags: - level2-server - level2-workstation - - automated - patch - mounts - rule_1.1.3.1 @@ -38,33 +24,26 @@ # skips if mount is absent - name: | "1.1.3.2 | PATCH | Ensure nodev option set on /var partition" - "1.1.3.3 | PATCH | Ensure noexec option set on /var partition" - "1.1.3.4 | PATCH | Ensure nosuid option set on /var partition" - mount: + "1.1.3.3 | PATCH | Ensure nosuid option set on /var partition" + ansible.builtin.mount: name: /var src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present - opts: defaults,{% if rhel9cis_rule_1_1_3_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_3_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_3_4 %}nosuid{% endif %} - with_items: - - "{{ ansible_mounts }}" + opts: defaults,{% if rhel9cis_rule_1_1_3_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_3_3 %}nosuid,{% endif %} + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" - notify: change_requires_reboot + notify: Change_requires_reboot when: - - var_mount_present is defined - item.mount == "/var" - - rhel9cis_rule_1_1_3_1 # This is required so the check takes place - rhel9cis_rule_1_1_3_2 or - rhel9cis_rule_1_1_3_3 or - rhel9cis_rule_1_1_3_4 + rhel9cis_rule_1_1_3_3 tags: - level1-server - level1-workstation - - automated - patch - mounts - skip_ansible_lint - rule_1.1.3.2 - rule_1.1.3.3 - - rule_1.1.3.4 diff --git a/tasks/section_1/cis_1.1.4.x.yml b/tasks/section_1/cis_1.1.4.x.yml index d05db6a..f063fbd 100644 --- a/tasks/section_1/cis_1.1.4.x.yml +++ b/tasks/section_1/cis_1.1.4.x.yml @@ -4,34 +4,20 @@ - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp" block: - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Absent" - debug: + ansible.builtin.debug: msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - register: var_tmp_mount_absent - changed_when: var_tmp_mount_absent.skipped is undefined - when: - - required_mount not in mount_names - - - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.4.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: var_tmp_mount_present - when: - - required_mount in mount_names + ansible.builtin.import_tasks: warning_facts.yml vars: + warn_control_id: '1.1.4.1' required_mount: '/var/tmp' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_4_1 tags: - level2-server - level2-workstation - - automated - audit - mounts - rule_1.1.4.1 @@ -41,28 +27,24 @@ "1.1.4.2 | PATCH | Ensure noexec option set on /var/tmp partition" "1.1.4.3 | PATCH | Ensure nosuid option set on /var/tmp partition" "1.1.4.4 | PATCH | Ensure nodev option set on /var/tmp partition" - mount: + ansible.builtin.mount: name: /var/tmp src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present - opts: defaults,{% if rhel9cis_rule_1_1_4_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_4_4 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_4_3 %}nosuid{% endif %} - with_items: - - "{{ ansible_mounts }}" + opts: defaults,{% if rhel9cis_rule_1_1_4_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_4_3 %}nosuid,{% endif %}{% if rhel9cis_rule_1_1_4_4 %}nodev{% endif %} + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" - notify: change_requires_reboot + notify: Change_requires_reboot when: - - var_tmp_mount_present is defined - item.mount == "/var/tmp" - - rhel9cis_rule_1_1_4_1 # This is required so the check takes place - rhel9cis_rule_1_1_4_2 or rhel9cis_rule_1_1_4_3 or rhel9cis_rule_1_1_4_4 tags: - level1-server - level1-workstation - - automated - patch - mounts - skip_ansible_lint diff --git a/tasks/section_1/cis_1.1.5.x.yml b/tasks/section_1/cis_1.1.5.x.yml index dd4ab9f..1707f30 100644 --- a/tasks/section_1/cis_1.1.5.x.yml +++ b/tasks/section_1/cis_1.1.5.x.yml @@ -3,34 +3,21 @@ - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log" block: - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Absent" - debug: + ansible.builtin.debug: msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - register: var_log_mount_absent - changed_when: var_log_mount_absent.skipped is undefined - when: - - required_mount not in mount_names - - - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.5.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: var_log_mount_present - when: - - required_mount in mount_names + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.5.1' required_mount: '/var/log' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_5_1 tags: - level2-server - level2-workstation - - automated - audit - mounts - rule_1.1.5.1 @@ -40,28 +27,24 @@ "1.1.5.2 | PATCH | Ensure nodev option set on /var/log partition" "1.1.5.3 | PATCH | Ensure noexec option set on /var/log partition" "1.1.5.4 | PATCH | Ensure nosuid option set on /var/log partition" - mount: + ansible.builtin.mount: name: /var/log src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present - opts: defaults,{% if rhel9cis_rule_1_1_5_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_5_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_5_4 %}nosuid{% endif %} - with_items: - - "{{ ansible_mounts }}" + opts: defaults,{% if rhel9cis_rule_1_1_5_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_5_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_5_4 %}nosuid{% endif %} + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" - notify: change_requires_reboot + notify: Change_requires_reboot when: - - var_log_mount_present is defined - item.mount == "/var/log" - - rhel9cis_rule_1_1_5_1 # This is required so the check takes place - rhel9cis_rule_1_1_5_2 or rhel9cis_rule_1_1_5_3 or rhel9cis_rule_1_1_5_4 tags: - level1-server - level1-workstation - - automated - patch - mounts - skip_ansible_lint diff --git a/tasks/section_1/cis_1.1.6.x.yml b/tasks/section_1/cis_1.1.6.x.yml index afbe41a..274f668 100644 --- a/tasks/section_1/cis_1.1.6.x.yml +++ b/tasks/section_1/cis_1.1.6.x.yml @@ -3,34 +3,21 @@ - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit" block: - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Absent" - debug: + ansible.builtin.debug: msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - register: var_log_audit_mount_absent - changed_when: var_log_audit_mount_absent.skipped is undefined - when: - - required_mount not in mount_names - - - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.6.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: var_log_audit_mount_present - when: - - required_mount in mount_names + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.6.1' required_mount: '/var/log/audit' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_6_1 tags: - level2-server - level2-workstation - - automated - audit - mounts - rule_1.1.6.1 @@ -39,28 +26,24 @@ "1.1.6.2 | PATCH | Ensure noexec option set on /var/log/audit partition" "1.1.6.3 | PATCH | Ensure nodev option set on /var/log/audit partition" "1.1.6.4 | PATCH | Ensure nosuid option set on /var/log/audit partition" - mount: + ansible.builtin.mount: name: /var/log/audit src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present opts: defaults,{% if rhel9cis_rule_1_1_6_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_6_3 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_6_4 %}nosuid{% endif %} - with_items: - - "{{ ansible_mounts }}" + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" - notify: change_requires_reboot + notify: Change_requires_reboot when: - - var_log_audit_mount_present is defined - item.mount == "/var/log/audit" - - rhel9cis_rule_1_1_6_1 # This is required so the check takes place - rhel9cis_rule_1_1_6_2 or rhel9cis_rule_1_1_6_3 or rhel9cis_rule_1_1_6_4 tags: - level1-server - level1-workstation - - automated - patch - mounts - skip_ansible_lint diff --git a/tasks/section_1/cis_1.1.7.x.yml b/tasks/section_1/cis_1.1.7.x.yml index 59f28ba..7f16610 100644 --- a/tasks/section_1/cis_1.1.7.x.yml +++ b/tasks/section_1/cis_1.1.7.x.yml @@ -3,34 +3,21 @@ - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home" block: - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Absent" - debug: + ansible.builtin.debug: msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - register: home_mount_absent - changed_when: home_mount_absent.skipped is undefined - when: - - required_mount not in mount_names - - - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.1.7.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - required_mount not in mount_names - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Present" - debug: - msg: "Congratulations: {{ required_mount }} exists." - register: home_mount_present - when: - - required_mount in mount_names + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.7.1' required_mount: '/home' when: + - required_mount not in mount_names - rhel9cis_rule_1_1_7_1 tags: - level2-server - level2-workstation - - automated - audit - mounts - rule_1.1.7.1 @@ -38,32 +25,25 @@ - name: | "1.1.7.2 | PATCH | Ensure nodev option set on /home partition - 1.1.7.3 | PATCH | Ensure nosuid option set on /home partition - 1.1.7.4 | PATCH | Ensure usrquota option set on /home partition - 1.1.7.5 | PATCH | Ensure grpquota option set on /home partition" - mount: + 1.1.7.3 | PATCH | Ensure nosuid option set on /home partition" + ansible.builtin.mount: name: /home src: "{{ item.device }}" fstype: "{{ item.fstype }}" state: present - opts: defaults,{% if rhel9cis_rule_1_1_7_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_7_3 %}nosuid,{% endif %}{% if rhel9cis_rule_1_1_7_4 %}usrquota,{% endif %}{% if rhel9cis_rule_1_1_7_5 %}grpquota{% endif %} - with_items: - - "{{ ansible_mounts }}" + opts: defaults,{% if rhel9cis_rule_1_1_7_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_7_3 %}nosuid,{% endif %} + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.device }}" - notify: change_requires_reboot + notify: Change_requires_reboot when: - - home_mount_present is defined - item.mount == "/home" - rhel9cis_rule_1_1_7_1 - rhel9cis_rule_1_1_7_2 or - rhel9cis_rule_1_1_7_3 or - rhel9cis_rule_1_1_7_4 or - rhel9cis_rule_1_1_7_5 + rhel9cis_rule_1_1_7_3 tags: - level1-server - level1-workstation - - automated - patch - mounts - rule_1.1.7.2 diff --git a/tasks/section_1/cis_1.1.8.x.yml b/tasks/section_1/cis_1.1.8.x.yml index 26ae877..3b85af3 100644 --- a/tasks/section_1/cis_1.1.8.x.yml +++ b/tasks/section_1/cis_1.1.8.x.yml @@ -1,43 +1,49 @@ --- # Skips if mount is absent -- name: | - "1.1.8.1 | PATCH | Ensure nodev option set on /dev/shm partition - 1.1.8.2 | PATCH | Ensure nosuid option set on /dev/shm partition - 1.1.8.3 | PATCH | Ensure noexec option set on /dev/shm partition" +- name: "1.1.8.1 | AUDIT | Ensure /dev/shm is a separate partition" block: - - name: | - "1.1.8.1 | AUDIT | Ensure nodev option set on /dev/shm partition | Check for /dev/shm existence - 1.1.8.2 | AUDIT | Ensure nosuid option set on /dev/shm partition | Check for /dev/shm existence - 1.1.8.3 | AUDIT | Ensure noexec option set on /dev/shm partition | Check for /dev/shm existence" - shell: mount -l | grep -E '\s/dev/shm\s' - changed_when: false - failed_when: false - check_mode: false - register: rhel9cis_1_1_8_x_dev_shm_status + - name: "1.1.8.1 | AUDIT | Ensure /dev/shm is a separate partition | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" - - name: | - "1.1.8.1 | PATCH | Ensure nodev option set on /dev/shm partition | Set nodev option - 1.1.8.2 | PATCH | Ensure noexec option set on /dev/shm partition | Set nosuid option - 1.1.8.3 | PATCH | Ensure nosuid option set on /dev/shm partition | Set noexec option" - mount: - name: /dev/shm - src: tmpfs - fstype: tmpfs - state: mounted - opts: defaults,{% if rhel9cis_rule_1_1_8_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_8_1 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_8_3 %}nosuid{% endif %} - when: "'dev/shm' in rhel9cis_1_1_8_x_dev_shm_status.stdout" - notify: change_requires_reboot + - name: "1.1.8.1 | AUDIT | Ensure separate partition exists for /home | Present" + ansible.builtin.import_tasks: warning_facts.yml + + vars: + warn_control_id: '1.1.8.1' + required_mount: '/dev/shm' when: - - rhel9cis_rule_1_1_8_1 or - rhel9cis_rule_1_1_8_2 or - rhel9cis_rule_1_1_8_3 + - required_mount not in mount_names + - rhel9cis_rule_1_1_8_1 tags: - level1-server - level1-workstation - - automated - - patch + - audit - mounts - rule_1.1.8.1 + - skip_ansible_lint + +- name: | + "1.1.8.2 | PATCH | Ensure nodev option set on /dev/shm partition | Set nodev option + 1.1.8.3 | PATCH | Ensure noexec option set on /dev/shm partition | Set nosuid option + 1.1.8.4 | PATCH | Ensure nosuid option set on /dev/shm partition | Set noexec option" + ansible.builtin.mount: + name: /dev/shm + src: tmpfs + fstype: tmpfs + state: mounted + opts: defaults,{% if rhel9cis_rule_1_1_8_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_8_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_8_4 %}nosuid{% endif %} + notify: Change_requires_reboot + when: + - rhel9cis_rule_1_1_8_2 or + rhel9cis_rule_1_1_8_3 or + rhel9cis_rule_1_1_8_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts - rule_1.1.8.2 - rule_1.1.8.3 + - rule_1.1.8.4 diff --git a/tasks/section_1/cis_1.1.x.yml b/tasks/section_1/cis_1.1.x.yml index ea5c862..0496300 100644 --- a/tasks/section_1/cis_1.1.x.yml +++ b/tasks/section_1/cis_1.1.x.yml @@ -1,26 +1,9 @@ --- -- name: "1.1.9 | PATCH | Disable Automounting" - service: - name: autofs - enabled: false - when: - - not rhel9cis_allow_autofs - - "'autofs' in ansible_facts.packages" - - rhel9cis_rule_1_1_9 - tags: - - level1-server - - level2-workstation - - automated - - patch - - mounts - - automounting - - rule_1.1.9 - -- name: "1.1.10 | PATCH | Disable USB Storage" +- name: "1.1.9 | PATCH | Disable USB Storage" block: - - name: "1.1.10 | PATCH | Disable USB Storage | Edit modprobe config" - lineinfile: + - name: "1.1.9 | PATCH | Disable USB Storage | Edit modprobe config" + ansible.builtin.lineinfile: path: /etc/modprobe.d/CIS.conf regexp: "^(#)?install usb-storage(\\s|$)" line: "install usb-storage /bin/true" @@ -29,17 +12,24 @@ group: root mode: 0600 - - name: "1.1.10 | PATCH | Disable USB Storage | Edit modprobe config" - modprobe: + - name: "1.1.9 | PATCH | Disable USB Storage | Edit modprobe config" + ansible.builtin.modprobe: name: usb-storage state: absent + + - name: "1.1.9 | PATCH | Disable USB Storage | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist usb-storage(\\s|$)" + line: "blacklist usb-storage" + create: true + mode: 0600 when: - - rhel9cis_rule_1_1_10 + - rhel9cis_rule_1_1_9 tags: - level1-server - level2-workstation - - automated - patch - mounts - removable_storage - - rule_1.1.10 + - rule_1.1.9 diff --git a/tasks/section_1/cis_1.10.yml b/tasks/section_1/cis_1.10.yml index 19ddc3f..c43e445 100644 --- a/tasks/section_1/cis_1.10.yml +++ b/tasks/section_1/cis_1.10.yml @@ -1,17 +1,16 @@ --- - name: "1.10 | PATCH | Ensure system-wide crypto policy is not legacy" - shell: | + ansible.builtin.shell: | update-crypto-policies --set "{{ rhel9cis_crypto_policy }}" update-crypto-policies - notify: change_requires_reboot + notify: Change_requires_reboot when: - rhel9cis_rule_1_10 - system_wide_crypto_policy['stdout'] == 'LEGACY' tags: - level1-server - level1-workstation - - automated - no system_is_ec2 - patch - rule_1.10 diff --git a/tasks/section_1/cis_1.2.x.yml b/tasks/section_1/cis_1.2.x.yml index 9445d15..452c009 100644 --- a/tasks/section_1/cis_1.2.x.yml +++ b/tasks/section_1/cis_1.2.x.yml @@ -1,54 +1,28 @@ --- -- name: "1.2.1 | PATCH | Ensure Red Hat Subscription Manager connection is configured" - redhat_subscription: - state: present - username: "{{ rhel9cis_rh_sub_user }}" - password: "{{ rhel9cis_rh_sub_password }}" - auto_attach: true - no_log: true - when: - - ansible_distribution == "RedHat" - - rhel9cis_rhnsd_required - - rhel9cis_rule_1_2_1 - tags: - - level1-server - - level1-workstation - - manual - - patch - - rule_1.2.1 - - skip_ansible_lint # Added as no_log still errors on ansuible-lint - -- name: "1.2.2 | AUDIT | Ensure GPG keys are configured" +- name: "1.2.1 | AUDIT | Ensure GPG keys are configured" block: - - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | list installed pubkey keys" - shell: "rpm -qa | grep {{ os_gpg_key_pubkey_name }}" + - name: "1.2.1 | AUDIT | Ensure GPG keys are configured | list installed pubkey keys" + ansible.builtin.shell: "rpm -qa | grep {{ os_gpg_key_pubkey_name }}" changed_when: false failed_when: false register: os_installed_pub_keys - - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | Query found keys" - shell: "rpm -q --queryformat \"%{PACKAGER} %{VERSION}\\n\" {{ os_gpg_key_pubkey_name }} | grep \"{{ os_gpg_key_pubkey_content }}\"" + - name: "1.2.1 | AUDIT | Ensure GPG keys are configured | Query found keys" + ansible.builtin.shell: 'rpm -q --queryformat "%{PACKAGER} %{VERSION}\\n" {{ os_gpg_key_pubkey_name }} | grep "{{ os_gpg_key_pubkey_content }}"' changed_when: false failed_when: false register: os_gpg_key_check when: os_installed_pub_keys.rc == 0 - - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | expected keys pass" - debug: - msg: "Congratulations !! - The installed gpg keys match expected values" - when: - - os_installed_pub_keys.rc == 0 - - os_gpg_key_check.rc == 0 - - - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | expected keys fail" - fail: + - name: "1.2.1 | AUDIT | Ensure GPG keys are configured | expected keys fail" + ansible.builtin.fail: msg: Installed GPG Keys do not meet expected values or keys installed that are not expected when: - os_installed_pub_keys.rc == 1 or os_gpg_key_check.rc == 1 when: - - rhel9cis_rule_1_2_2 + - rhel9cis_rule_1_2_1 - ansible_distribution == "RedHat" or ansible_distribution == "Rocky" or ansible_distribution == "AlmaLinux" @@ -57,62 +31,90 @@ - level1-workstation - manual - patch - - rule_1.2.2 + - rule_1.2.1 -- name: "1.2.3| PATCH | Ensure gpgcheck is globally activated" +- name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated" block: - - name: "1.2.3 | AUDIT | Ensure gpgcheck is globally activated | Find repos" - find: + - name: "1.2.2 | AUDIT | Ensure gpgcheck is globally activated | Find repos" + ansible.builtin.find: paths: /etc/yum.repos.d patterns: "*.repo" register: yum_repos - changed_when: false - - name: "1.2.3 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" - replace: + - name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" + ansible.builtin.replace: name: "{{ item.path }}" regexp: "^gpgcheck=0" replace: "gpgcheck=1" - with_items: - - "{{ yum_repos.files }}" + loop: "{{ yum_repos.files }}" loop_control: label: "{{ item.path }}" + when: + - rhel9cis_rule_1_2_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.2.2 + +- name: "1.2.3 | AUDIT | Ensure package manager repositories are configured" + block: + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Get repo list" + ansible.builtin.shell: dnf repolist + changed_when: false + failed_when: false + register: dnf_configured + check_mode: false + + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Display repo list" + ansible.builtin.debug: + msg: + - "Warning!! Below are the configured repos. Please review and make sure all align with site policy" + - "{{ dnf_configured.stdout_lines }}" + + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Warn Count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.2.3' when: - rhel9cis_rule_1_2_3 tags: - level1-server - level1-workstation - - automated - - patch + - manual + - audit - rule_1.2.3 + - skip_ansible_lint -- name: "1.2.4 | AUDIT | Ensure package manager repositories are configured" +- name: "1.2.4 | AUDIT | Ensure repo_gpgcheck is globally activated" block: - - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Get repo list" - command: dnf repolist - changed_when: false - failed_when: false - register: dnf_configured - check_mode: false - args: - warn: false + - name: "1.2.4 | PATCH | Ensure repo_gpgcheck is globally activated | dnf.conf" + ansible.builtin.lineinfile: + path: /etc/dnf/dnf.conf + regexp: '^repo_gpgcheck' + line: repo_gpgcheck=1 - - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Display repo list" - debug: - msg: - - "Warning!! Below are the configured repos. Please review and make sure all align with site policy" - - "{{ dnf_configured.stdout_lines }}" + - name: "1.2.4 | AUDIT| Ensure repo_gpgcheck is globally activated | get repo files" + ansible.builtin.find: + paths: /etc/yum.repos.d + patterns: "*.repo" + register: repo_files + + - name: "1.2.4 | PATCH | Ensure repo_gpgcheck is globally activated | amend repo files" + ansible.builtin.replace: + path: "{{ item.path }}" + regexp: '^repo_gpgcheck( |)=( |)0' + replace: repo_gpgcheck=1 + loop: "{{ repo_files.files }}" + loop_control: + label: "{{ item.path }}" - - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Warn Count" - set_fact: - control_number: "{{ control_number }} + ['rule_1.2.4']" - warn_count: "{{ warn_count | int + 1 }}" when: - rhel9cis_rule_1_2_4 + - not rhel9cis_rhel_default_repo or ansible_distribution != 'RedHat' tags: - level1-server - level1-workstation - manual - audit - rule_1.2.4 - - skip_ansible_lint diff --git a/tasks/section_1/cis_1.3.x.yml b/tasks/section_1/cis_1.3.x.yml index 4dd7bcd..2c61fc8 100644 --- a/tasks/section_1/cis_1.3.x.yml +++ b/tasks/section_1/cis_1.3.x.yml @@ -3,32 +3,37 @@ - name: "1.3.1 | PATCH | Ensure AIDE is installed" block: - name: "1.3.1 | PATCH | Ensure AIDE is installed | Install AIDE" - package: + ansible.builtin.package: name: aide state: present - - name: "1.3.1 | PATCH | Ensure AIDE is installed | Configure AIDE" - command: /usr/sbin/aide --init -B 'database_out=file:/var/lib/aide/aide.db.gz' + - name: "1.3.1 | PATCH | Ensure AIDE is installed | Build AIDE DB" + ansible.builtin.shell: /usr/sbin/aide --init changed_when: false failed_when: false async: 45 poll: 0 args: - creates: /var/lib/aide/aide.db.gz + creates: /var/lib/aide/aide.db.new.gz when: not ansible_check_mode + + - name: "1.3.1 | PATCH | Ensure AIDE is installed | copy AIDE DB" + ansible.builtin.copy: + src: /var/lib/aide/aide.db.new.gz + dest: /var/lib/aide/aide.db.gz + remote_src: true when: - rhel9cis_config_aide - rhel9cis_rule_1_3_1 tags: - level1-server - level1-workstation - - automated - aide - patch - rule_1.3.1 - name: "1.3.2 | PATCH | Ensure filesystem integrity is regularly checked" - cron: + ansible.builtin.cron: name: Run AIDE integrity check cron_file: "{{ rhel9cis_aide_cron['cron_file'] }}" user: "{{ rhel9cis_aide_cron['cron_user'] }}" @@ -44,8 +49,30 @@ tags: - level1-server - level1-workstation - - automated - aide - file_integrity - patch - rule_1.3.2 + +- name: "1.3.3 | Ensure cryptographic mechanisms are used to protect the integrity of audit tools" + ansible.builtin.blockinfile: + path: /etc/aide.conf + marker: "# {mark} Audit tools (CIS - Ansible)" + block: | + /sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + validate: aide -D --config %s + when: + - rhel9cis_rule_1_3_2 + - not system_is_ec2 + tags: + - level1-server + - level1-workstation + - aide + - file_integrity + - patch + - rule_1.3.3 diff --git a/tasks/section_1/cis_1.4.x.yml b/tasks/section_1/cis_1.4.x.yml index cdad67f..f2dcaee 100644 --- a/tasks/section_1/cis_1.4.x.yml +++ b/tasks/section_1/cis_1.4.x.yml @@ -1,20 +1,19 @@ --- - name: "1.4.1 | PATCH | Ensure bootloader password is set" - copy: + ansible.builtin.copy: dest: /boot/grub2/user.cfg content: "GRUB2_PASSWORD={{ rhel9cis_bootloader_password_hash }}" # noqa template-instead-of-copy owner: root group: root mode: 0600 - notify: grub2cfg + notify: Grub2cfg when: - rhel9cis_set_boot_pass - rhel9cis_rule_1_4_1 tags: - level1-server - level1-workstation - - automated - grub - patch - rule_1.4.1 @@ -22,51 +21,21 @@ - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured" block: - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured" - file: - path: /boot/grub2/grub.cfg + ansible.builtin.file: + path: "/boot/grub2/{{ item.path }}" owner: root group: root - mode: 0600 + mode: "{{ item.mode }}" + loop: + - { path: 'grub.cfg', mode: '0700' } + - { path: 'grubenv', mode: '0600' } + - { path: 'user.cfg', mode: '0600' } - - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured | UEFI" - mount: - name: /boot/efi - src: "UUID={{ item.uuid }}" - fstype: vfat - state: present - opts: defaults,umask=0027,fmask=0077,uid=0,gid=0 - passno: '0' - with_items: - - "{{ ansible_mounts }}" - loop_control: - label: "{{ item.mount }}" - when: - - not rhel9cis_legacy_boot - - item.mount == "/boot/efi" when: - rhel9cis_rule_1_4_2 tags: - level1-server - level1-workstation - - automated - grub - patch - rule_1.4.2 - -- name: "1.4.3 | PATCH | Ensure authentication is required when booting into rescue mode" - lineinfile: - path: /etc/systemd/system/rescue.service.d/00-require-auth.conf - regexp: '^ExecStart=' - line: "ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue" - create: true - owner: root - group: root - mode: 0644 - when: - - rhel9cis_rule_1_4_3 - tags: - - level1-server - - level1-workstation - - automated - - patch - - rule_1.4.3 diff --git a/tasks/section_1/cis_1.5.x.yml b/tasks/section_1/cis_1.5.x.yml index 031ba5c..443bfc1 100644 --- a/tasks/section_1/cis_1.5.x.yml +++ b/tasks/section_1/cis_1.5.x.yml @@ -1,23 +1,22 @@ --- - name: "1.5.1 | PATCH | Ensure core dump storage is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/coredump.conf regexp: '^Storage\s*=\s*(?!none).*' line: 'Storage=none' - notify: systemd_daemon_reload + notify: Systemd daemon reload when: - rhel9cis_rule_1_5_1 - systemd_coredump.stat.exists tags: - level1-server - level1-workstation - - automated - patch - rule_1.5.1 - name: "1.5.2 | PATCH | Ensure core dump backtraces are disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/coredump.conf regexp: '^ProcessSizeMax\s*=\s*.*[1-9]$' line: 'ProcessSizeMax=0' @@ -26,7 +25,6 @@ tags: - level1-server - level1-workstation - - automated - patch - sysctl - rule_1.5.2 @@ -34,17 +32,17 @@ - name: "1.5.3 | PATCH | Ensure address space layout randomization (ASLR) is enabled" block: - name: "1.5.3 | PATCH | Ensure address space layout randomization (ASLR) is enabled" - set_fact: + ansible.builtin.set_fact: sysctl_update: true + - name: "1.5.3 | PATCH | Ensure address space layout randomization (ASLR) is enabled" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-kernel_sysctl.conf" when: - rhel9cis_rule_1_5_3 tags: - level1-server - level1-workstation - - automated - patch - sysctl - rule_1.5.3 diff --git a/tasks/section_1/cis_1.6.1.x.yml b/tasks/section_1/cis_1.6.1.x.yml index 9a8d134..f05143c 100644 --- a/tasks/section_1/cis_1.6.1.x.yml +++ b/tasks/section_1/cis_1.6.1.x.yml @@ -1,7 +1,7 @@ --- - name: "1.6.1.1 | PATCH | Ensure SELinux is installed" - package: + ansible.builtin.package: name: libselinux state: present when: @@ -9,18 +9,20 @@ tags: - level1-server - level1-workstation - - automated - patch - rule_1.6.1.1 - name: "1.6.1.2 | PATCH | Ensure SELinux is not disabled in bootloader configuration" - replace: - dest: /etc/default/grub - regexp: 'selinux=0' + ansible.builtin.replace: + path: /etc/default/grub + regexp: '{{ item }}' replace: '' + loop: + - selinux=0 + - enforcing=0 register: selinux_grub_patch ignore_errors: true # noqa ignore-errors - notify: grub2cfg + notify: Grub2cfg when: - rhel9cis_rule_1_6_1_2 tags: @@ -32,93 +34,99 @@ # State set to enforcing because control 1.6.1.5 requires enforcing to be set - name: "1.6.1.3 | PATCH | Ensure SELinux policy is configured" - selinux: + ansible.posix.selinux: conf: /etc/selinux/config policy: "{{ rhel9cis_selinux_pol }}" - state: enforcing + state: "{{ rhel9cis_selinux_enforce }}" when: - not rhel9cis_selinux_disable - rhel9cis_rule_1_6_1_3 tags: - level1-server - level1-workstation - - automated - selinux - patch - rule_1.6.1.3 -- name: "1.6.1.4 | PATCH | Ensure the SELinux state is enforcing" - selinux: +- name: "1.6.1.4 | PATCH | Ensure the SELinux state is not disabled" + ansible.posix.selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: "{{ rhel9cis_selinux_enforce }}" + when: + - not rhel9cis_selinux_disable + - rhel9cis_rule_1_6_1_4 + tags: + - level1-server + - level1-workstation + - selinux + - patch + - rule_1.6.1.4 + +- name: "1.6.1.5 | PATCH | Ensure the SELinux state is enforcing" + ansible.posix.selinux: conf: /etc/selinux/config policy: "{{ rhel9cis_selinux_pol }}" state: enforcing when: - not rhel9cis_selinux_disable - - rhel9cis_rule_1_6_1_4 + - rhel9cis_selinux_enforce == 'enforcing' + - rhel9cis_rule_1_6_1_5 tags: - level2-server - level2-workstation - - automated - selinux - patch - - rule_1.6.1.4 + - rule_1.6.1.5 -- name: "1.6.1.5 | AUDIT | Ensure no unconfined services exist" +- name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist" block: - - name: "1.6.1.5 | AUDIT | Ensure no unconfined services exist | Find the unconfined services" - shell: ps -eZ | grep unconfined_service_t | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }' - register: rhelcis_1_6_1_5_unconf_services + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | Find the unconfined services" + ansible.builtin.shell: ps -eZ | grep unconfined_service_t | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }' + register: rhelcis_1_6_1_6_unconf_services failed_when: false changed_when: false - - name: "1.6.1.5 | AUDIT | Ensure no unconfined services exist | Message on no unconfined services" - debug: - msg: "Good News! There are no services found on your system" - when: rhelcis_1_6_1_5_unconf_services.stdout | length == 0 + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | Message on unconfined services" + ansible.builtin.debug: + msg: "Warning!! You have unconfined services: {{ rhelcis_1_6_1_6_unconf_services.stdout_lines }}" + when: rhelcis_1_6_1_6_unconf_services.stdout | length > 0 - - name: "1.6.1.5 | AUDIT | Ensure no unconfined services exist | Message on unconfined services" - debug: - msg: "Warning!! You have unconfined services: {{ rhelcis_1_6_1_5_unconf_services.stdout_lines }}" - when: rhelcis_1_6_1_5_unconf_services.stdout | length > 0 - - - name: "1.6.1.5 | AUDIT | Ensure no unconfined services exist | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_1.6.1.5' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhelcis_1_6_1_5_unconf_services.stdout | length > 0 + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: rhelcis_1_6_1_6_unconf_services.stdout | length > 0 + vars: + warn_control_id: '1.6.1.6' when: - - rhel9cis_rule_1_6_1_5 + - rhel9cis_rule_1_6_1_6 tags: - level1-server - level1-workstation - - automated - audit - services - - rule_1.6.1.5 + - rule_1.6.1.6 -- name: "1.6.1.6 | PATCH | Ensure SETroubleshoot is not installed" - package: +- name: "1.6.1.7 | PATCH | Ensure SETroubleshoot is not installed" + ansible.builtin.package: name: setroubleshoot state: absent when: - - rhel9cis_rule_1_6_1_6 + - rhel9cis_rule_1_6_1_7 - "'setroubleshoot' in ansible_facts.packages" tags: - level1-server - - automated - selinux - patch - - rule_1.6.1.6 + - rule_1.6.1.7 -- name: "1.6.1.7 | PATCH | Ensure the MCS Translation Service (mcstrans) is not installed" - package: +- name: "1.6.1.8 | PATCH | Ensure the MCS Translation Service (mcstrans) is not installed" + ansible.builtin.package: name: mcstrans state: absent when: - - rhel9cis_rule_1_6_1_7 + - rhel9cis_rule_1_6_1_8 tags: - level1-server - level1-workstation - - automated - patch - - rule_1.6.1.7 + - rule_1.6.1.8 diff --git a/tasks/section_1/cis_1.7.x.yml b/tasks/section_1/cis_1.7.x.yml index 1ee5579..1c20dca 100644 --- a/tasks/section_1/cis_1.7.x.yml +++ b/tasks/section_1/cis_1.7.x.yml @@ -1,7 +1,7 @@ --- - name: "1.7.1 | PATCH | Ensure message of the day is configured properly" - template: + ansible.builtin.template: src: etc/motd.j2 dest: /etc/motd owner: root @@ -12,13 +12,12 @@ tags: - level1-server - level1-workstation - - automated - banner - patch - rule_1.7.1 - name: "1.7.2 | PATCH | Ensure local login warning banner is configured properly" - template: + ansible.builtin.template: src: etc/issue.j2 dest: /etc/issue owner: root @@ -29,12 +28,11 @@ tags: - level1-server - level1-workstation - - automated - patch - rule_1.7.2 - name: "1.7.3 | PATCH | Ensure remote login warning banner is configured properly" - template: + ansible.builtin.template: src: etc/issue.net.j2 dest: /etc/issue.net owner: root @@ -45,15 +43,13 @@ tags: - level1-server - level1-workstation - - automated - banner - patch - rule_1.7.3 - name: "1.7.4 | PATCH | Ensure permissions on /etc/motd are configured" - file: - dest: /etc/motd - state: file + ansible.builtin.file: + path: /etc/motd owner: root group: root mode: 0644 @@ -62,15 +58,13 @@ tags: - level1-server - level1-workstation - - automated - perms - patch - rule_1.7.4 - name: "1.7.5 | PATCH | Ensure permissions on /etc/issue are configured" - file: - dest: /etc/issue - state: file + ansible.builtin.file: + path: /etc/issue owner: root group: root mode: 0644 @@ -79,15 +73,13 @@ tags: - level1-server - level1-workstation - - automated - perms - patch - rule_1.7.5 - name: "1.7.6 | PATCH | Ensure permissions on /etc/issue.net are configured" - file: - dest: /etc/issue.net - state: file + ansible.builtin.file: + path: /etc/issue.net owner: root group: root mode: 0644 @@ -96,7 +88,6 @@ tags: - level1-server - level1-workstation - - automated - perms - patch - rule_1.7.6 diff --git a/tasks/section_1/cis_1.8.x.yml b/tasks/section_1/cis_1.8.x.yml index f47d2a1..4f6922f 100644 --- a/tasks/section_1/cis_1.8.x.yml +++ b/tasks/section_1/cis_1.8.x.yml @@ -1,52 +1,58 @@ --- - name: "1.8.1 | PATCH | Ensure GNOME Display Manager is removed" - package: + ansible.builtin.package: name: gdm state: absent when: - rhel9cis_rule_1_8_1 - "'gdm' in ansible_facts.packages" + - not rhel9cis_gui tags: - level2-server - - automated - patch - gui - gdm - rule_1.8.1 - name: "1.8.2 | PATCH | Ensure GDM login banner is configured" - lineinfile: - path: "{{ item.file }}" - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" - state: present - create: true - owner: root - group: root - mode: 0644 - notify: reload dconf - with_items: - - { file: '/etc/dconf/profile/gdm', regexp: 'user-db', line: 'user-db:user' } - - { file: '/etc/dconf/profile/gdm', regexp: 'system-db', line: 'system-db:gdm' } - - { file: '/etc/dconf/profile/gdm', regexp: 'file-db', line: 'file-db:/usr/share/gdm/greeter-dconf-defaults' } - - { file: '/etc/dconf/db/gdm.d/01-banner-message', regexp: '\[org\/gnome\/login-screen\]', line: '[org/gnome/login-screen]' } - - { file: '/etc/dconf/db/gdm.d/01-banner-message', regexp: 'banner-message-enable', line: 'banner-message-enable=true' } - - { file: '/etc/dconf/db/gdm.d/01-banner-message', regexp: 'banner-message-text', line: "banner-message-text='{{ rhel9cis_warning_banner }}' " } + block: + - name: "1.8.2 | PATCH | Ensure GDM login banner is configured | gdm profile" + ansible.builtin.lineinfile: + path: /etc/dconf/profile/gdm + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + create: true + owner: root + group: root + mode: 0644 + notify: Reload dconf + loop: + - { regexp: 'user-db', line: 'user-db:user' } + - { regexp: 'system-db', line: 'system-db:gdm' } + - { regexp: 'file-db', line: 'file-db:/usr/share/gdm/greeter-dconf-defaults' } + + - name: "1.8.2 | PATCH | Ensure GDM login banner is configured | gdm profile" + ansible.builtin.template: + src: etc/dconf/db/gdm.d/01-banner-message.j2 + dest: /etc/dconf/db/gdm.d/01-banner-message + owner: root + group: root + mode: 0644 + notify: Reload dconf when: - rhel9cis_rule_1_8_2 - rhel9cis_gui tags: - level1-server - level1-workstation - - automated - patch - gui - gdm - rule_1.8.2 -- name: "1.8.3 | PATCH | Ensure last logged in user display is disabled" - lineinfile: +- name: "1.8.3 | PATCH | Ensure GDM disable-user-list option is enabled" + ansible.builtin.lineinfile: path: "{{ item.file }}" regexp: "{{ item.regexp }}" line: "{{ item.line }}" @@ -54,8 +60,8 @@ owner: root group: root mode: 0644 - notify: reload dconf - with_items: + notify: Reload dconf + loop: - { file: '/etc/dconf/profile/gdm', regexp: 'user-db', line: 'user-db:user' } - { file: '/etc/dconf/profile/gdm', regexp: 'system-db', line: 'system-db:gdm' } - { file: '/etc/dconf/profile/gdm', regexp: 'file-db', line: 'file-db:/usr/share/gdm/greeter-dconf-defaults'} @@ -67,48 +73,192 @@ tags: - level1-server - level1-workstation - - automated - patch - gui - rule_1.8.3 -- name: "1.8.4 | PATCH | Ensure XDMCP is not enabled" - lineinfile: - path: /etc/gdm/custom.conf - regexp: 'Enable=true' - state: absent +- name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle" + block: + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | User profile" + ansible.builtin.lineinfile: + path: /etc/dconf/profile/user + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + create: true + owner: root + group: root + mode: 0644 + loop: + - { regexp: '^user-db', line: 'user-db: user' } + - { regexp: '^system-db', line: 'system-db: local' } + + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | Make db directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | Make conf file" + ansible.builtin.template: + src: etc/dconf/db/00-screensaver.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-screensaver" + owner: root + group: root + mode: '0644' + notify: Reload dconf when: - rhel9cis_rule_1_8_4 - rhel9cis_gui tags: - level1-server - level1-workstation - - automated - patch - gui - rule_1.8.4 -- name: "1.8.5 | PATCH | Ensure automatic mounting of removable media is disabled" - lineinfile: - path: /etc/dconf/db/local.d/00-media-automount - regexp: "{{ item.regex }}" - line: "{{ item.line }}" - create: true - owner: root - group: root - mode: 0644 - notify: reload dconf - with_items: - - { regex: '\[org\/gnome\/desktop\/media-handling\]', line: '[org/gnome/desktop/media-handling]' } - - { regex: 'automount=', line: 'automount=false' } - - { regex: 'automount-open=', line: 'automount-open=false'} +- name: "1.8.5 PATCH | Ensure GDM screen locks cannot be overridden" + block: + - name: "1.8.5 | PATCH | Ensure GDM screen locks cannot be overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.5 | PATCH | Ensure GDM screen locks cannot be overridden | Make lock file" + ansible.builtin.template: + src: etc/dconf/db/00-screensaver_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-screensaver" + owner: root + group: root + mode: 0644 + notify: Reload dconf when: - rhel9cis_rule_1_8_5 - rhel9cis_gui tags: - level1-server - - level2-workstation - - automated + - level1-workstation - patch - gui - rule_1.8.5 + +- name: "1.8.6 | PATCH | Ensure GDM automatic mounting of removable media is disabled" + ansible.builtin.template: + src: etc/dconf/db/00-media-automount.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-media-automount" + owner: root + group: root + mode: '0644' + notify: Reload dconf + when: + - rhel9cis_rule_1_8_6 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.6 + +- name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden" + block: + - name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden | Make lock file" + ansible.builtin.template: + src: etc/dconf/db/00-automount_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-automount_lock" + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_7 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.7 + +- name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled" + block: + - name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled | Make directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled | Make conf file" + ansible.builtin.template: + src: etc/dconf/db/00-media-autorun.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-media-autorun" + owner: root + group: root + mode: '0644' + notify: Reload dconf + when: + - rhel9cis_rule_1_8_8 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.8 + +- name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden" + block: + - name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden | Make lockfile" + ansible.builtin.template: + src: etc/dconf/db/00-autorun_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-autorun_lock" + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_9 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.9 + +- name: "1.8.10 | PATCH | Ensure XDMCP is not enabled" + ansible.builtin.lineinfile: + path: /etc/gdm/custom.conf + regexp: 'Enable=true' + state: absent + when: + - rhel9cis_rule_1_8_10 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - rule_1.8.4 diff --git a/tasks/section_1/cis_1.9.yml b/tasks/section_1/cis_1.9.yml index 42c27b1..e226948 100644 --- a/tasks/section_1/cis_1.9.yml +++ b/tasks/section_1/cis_1.9.yml @@ -1,17 +1,16 @@ --- - name: "1.9 | PATCH | Ensure updates, patches, and additional security software are installed" - package: + ansible.builtin.package: name: "*" state: latest - notify: change_requires_reboot + notify: Change_requires_reboot when: - rhel9cis_rule_1_9 - not system_is_ec2 tags: - level1-server - level1-workstation - - automated - patch - rule_1.9 - skip_ansible_lint diff --git a/tasks/section_1/main.yml b/tasks/section_1/main.yml index 1d6ab55..d9bc3b5 100644 --- a/tasks/section_1/main.yml +++ b/tasks/section_1/main.yml @@ -1,57 +1,57 @@ --- - name: "SECTION | 1.1.1.x | Disable unused filesystems" - import_tasks: cis_1.1.1.x.yml + ansible.builtin.import_tasks: cis_1.1.1.x.yml - name: "SECTION | 1.1.2.x | Configure /tmp" - import_tasks: cis_1.1.2.x.yml + ansible.builtin.import_tasks: cis_1.1.2.x.yml - name: "SECTION | 1.1.3.x | Configure /var" - import_tasks: cis_1.1.3.x.yml + ansible.builtin.import_tasks: cis_1.1.3.x.yml - name: "SECTION | 1.1.4.x | Configure /var/tmp" - import_tasks: cis_1.1.4.x.yml + ansible.builtin.import_tasks: cis_1.1.4.x.yml - name: "SECTION | 1.1.5.x | Configure /var/log" - import_tasks: cis_1.1.5.x.yml + ansible.builtin.import_tasks: cis_1.1.5.x.yml - name: "SECTION | 1.1.6.x | Configure /var/log/audit" - import_tasks: cis_1.1.6.x.yml + ansible.builtin.import_tasks: cis_1.1.6.x.yml - name: "SECTION | 1.1.7.x | Configure /home" - import_tasks: cis_1.1.7.x.yml + ansible.builtin.import_tasks: cis_1.1.7.x.yml - name: "SECTION | 1.1.8.x | Configure /dev/shm" - import_tasks: cis_1.1.8.x.yml + ansible.builtin.import_tasks: cis_1.1.8.x.yml - name: "SECTION | 1.1.x | Disable various mounting" - import_tasks: cis_1.1.x.yml + ansible.builtin.import_tasks: cis_1.1.x.yml - name: "SECTION | 1.2 | Configure Software Updates" - import_tasks: cis_1.2.x.yml + ansible.builtin.import_tasks: cis_1.2.x.yml - name: "SECTION | 1.3 | Filesystem Integrity Checking" - import_tasks: cis_1.3.x.yml + ansible.builtin.import_tasks: cis_1.3.x.yml when: rhel9cis_config_aide - name: "SECTION | 1.4 | Secure Boot Settings" - import_tasks: cis_1.4.x.yml + ansible.builtin.import_tasks: cis_1.4.x.yml - name: "SECTION | 1.5 | Additional Process Hardening" - import_tasks: cis_1.5.x.yml + ansible.builtin.import_tasks: cis_1.5.x.yml - name: "SECTION | 1.6 | Mandatory Access Control" include_tasks: cis_1.6.1.x.yml when: not rhel9cis_selinux_disable - name: "SECTION | 1.7 | Command Line Warning Banners" - import_tasks: cis_1.7.x.yml + ansible.builtin.import_tasks: cis_1.7.x.yml - name: "SECTION | 1.8 | Gnome Display Manager" - import_tasks: cis_1.8.x.yml + ansible.builtin.import_tasks: cis_1.8.x.yml - name: "SECTION | 1.9 | Updates and Patches" - import_tasks: cis_1.9.yml + ansible.builtin.import_tasks: cis_1.9.yml - name: "SECTION | 1.10 | Crypto policies" include_tasks: cis_1.10.yml diff --git a/tasks/section_2/cis_2.1.x.yml b/tasks/section_2/cis_2.1.x.yml index 1db8179..43cc226 100644 --- a/tasks/section_2/cis_2.1.x.yml +++ b/tasks/section_2/cis_2.1.x.yml @@ -1,7 +1,7 @@ --- - name: "2.1.1 | PATCH | Ensure time synchronization is in use" - package: + ansible.builtin.package: name: chrony state: present when: @@ -10,14 +10,13 @@ tags: - level1-server - level1-workstation - - automated - patch - rule_2.1.1 - name: "2.1.2 | PATCH | Ensure chrony is configured" block: - name: "2.1.2 | PATCH | Ensure chrony is configured | Set configuration" - template: + ansible.builtin.template: src: etc/chrony.conf.j2 dest: /etc/chrony.conf owner: root @@ -25,7 +24,7 @@ mode: 0644 - name: "2.1.2 | PATCH | Ensure chrony is configured | modify /etc/sysconfig/chronyd | 1" - lineinfile: + ansible.builtin.lineinfile: path: /etc/sysconfig/chronyd regexp: "^(#)?OPTIONS" line: "OPTIONS=\"-u chrony\"" diff --git a/tasks/section_2/cis_2.2.x.yml b/tasks/section_2/cis_2.2.x.yml index 6a195ca..496a92f 100644 --- a/tasks/section_2/cis_2.2.x.yml +++ b/tasks/section_2/cis_2.2.x.yml @@ -1,54 +1,39 @@ --- -- name: "2.2.1 | PATCH | Ensure xinetd is not installed" - package: - name: xinetd - state: absent - when: - - rhel9cis_rule_2_2_1 - - not rhel9cis_xinetd_server - - "'xinetd' in ansible_facts.packages" - tags: - - level1-server - - level1-workstation - - automated - - patch - - rule_2.2.1 -- name: "2.2.2 | PATCH | Ensure xorg-x11-server-common is not installed" - package: +- name: "2.2.1 | PATCH | Ensure xorg-x11-server-common is not installed" + ansible.builtin.package: name: xorg-x11-server-common state: absent when: - - rhel9cis_rule_2_2_2 + - rhel9cis_rule_2_2_1 - "'xorg-x11-server-common' in ansible_facts.packages" + - not rhel9cis_gui tags: - level1-server - - automated - patch - x11 - - rule_2.2.2 + - rule_2.2.1 -- name: "2.2.3 | PATCH | Ensure Avahi Server is not installed" - package: +- name: "2.2.2 | PATCH | Ensure Avahi Server is not installed" + ansible.builtin.package: name: - avahi-autoipd - avahi state: absent when: - - rhel9cis_rule_2_2_3 + - rhel9cis_rule_2_2_2 - not rhel9cis_avahi_server - "'avahi' in ansible_facts.packages or 'avahi-autopd' in ansible_facts.packages" tags: - level1-server - level2-workstation - - automated - patch - avahi - - rule_2.2.3 + - rule_2.2.2 -- name: "2.2.4 | PATCH | Ensure CUPS is not installed" - package: +- name: "2.2.3 | PATCH | Ensure CUPS is not installed" + ansible.builtin.package: name: cups state: absent when: @@ -57,124 +42,102 @@ - rhel9cis_rule_2_2_3 tags: - level1-server - - automated - patch - cups - rule_2.2.3 -- name: "2.2.5 | PATCH | Ensure DHCP Server is not installed" - package: +- name: "2.2.4 | PATCH | Ensure DHCP Server is not installed" + ansible.builtin.package: name: dhcp-server state: absent when: - not rhel9cis_dhcp_server - "'dhcp-server' in ansible_facts.packages" - - rhel9cis_rule_2_2_5 + - rhel9cis_rule_2_2_4 tags: - level1-server - level1-workstation - - automated - patch - dhcp - - rule_2.2.5 + - rule_2.2.4 -- name: "2.2.6 | PATCH | Ensure DNS Server is not installed" - package: +- name: "2.2.5 | PATCH | Ensure DNS Server is not installed" + ansible.builtin.package: name: bind state: absent when: - not rhel9cis_dns_server - "'bind' in ansible_facts.packages" - - rhel9cis_rule_2_2_6 + - rhel9cis_rule_2_2_5 tags: - level1-server - level1-workstation - - automated - patch - dns - - rule_2.2.6 + - rule_2.2.5 -- name: "2.2.7 | PATCH | Ensure FTP Server is not installed" - package: - name: ftp - state: absent - when: - - not rhel9cis_ftp_server - - "'ftp' in ansible_facts.packages" - - rhel9cis_rule_2_2_7 - tags: - - level1-server - - level1-workstation - - automated - - patch - - ftp - - rule_2.2.7 - -- name: "2.2.8 | PATCH | Ensure VSFTP Server is not installed" - package: +- name: "2.2.6 | PATCH | Ensure VSFTP Server is not installed" + ansible.builtin.package: name: vsftpd state: absent when: - not rhel9cis_vsftpd_server - "'vsftpd' in ansible_facts.packages" - - rhel9cis_rule_2_2_8 + - rhel9cis_rule_2_2_6 tags: - level1-server - level1-workstation - - automated - patch - vsftpd - - rule_2.2.8 + - rule_2.2.6 -- name: "2.2.9 | PACH | Ensure TFTP Server is not installed" - package: +- name: "2.2.7 | PACH | Ensure TFTP Server is not installed" + ansible.builtin.package: name: tftp-server state: absent when: - not rhel9cis_tftp_server - "'tftp-server' in ansible_facts.packages" - - rhel9cis_rule_2_2_9 + - rhel9cis_rule_2_2_7 tags: - level1-server - level1-workstation - - automated - patch - tftp - - rule_2.2.9 + - rule_2.2.7 -- name: "2.2.10 | PATCH | Ensure a web server is not installed" +- name: "2.2.8 | PATCH | Ensure a web server is not installed" block: - - name: "2.2.10 | PATCH | Ensure a web server is not installed | Remove httpd server" - package: + - name: "2.2.8 | PATCH | Ensure a web server is not installed | Remove httpd server" + ansible.builtin.package: name: httpd state: absent when: - not rhel9cis_httpd_server - "'httpd' in ansible_facts.packages" - - name: "2.2.10 | PATCH | Ensure a web server is not installed | Remove nginx server" - package: + - name: "2.2.8 | PATCH | Ensure a web server is not installed | Remove nginx server" + ansible.builtin.package: name: nginx state: absent when: - not rhel9cis_nginx_server - "'nginx' in ansible_facts.packages" when: - - rhel9cis_rule_2_2_10 + - rhel9cis_rule_2_2_8 tags: - level1-server - level1-workstation - - automated - patch - httpd - nginx - webserver - - rule_2.2.9 + - rule_2.2.8 -- name: "2.2.11 | PATCH | Ensure IMAP and POP3 server is not installed" +- name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" block: - - name: "2.2.11 | PATCH | Ensure IMAP and POP3 server is not installed" - package: + - name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" + ansible.builtin.package: name: - dovecot state: absent @@ -182,8 +145,8 @@ - not rhel9cis_dovecot_server - "'dovecot' in ansible_facts.packages" - - name: "2.2.11 | PATCH | Ensure IMAP and POP3 server is not installed" - package: + - name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" + ansible.builtin.package: name: - cyrus-imapd state: absent @@ -192,129 +155,123 @@ - "'cyrus-imapd' in ansible_facts.packages" when: - - rhel9cis_rule_2_2_11 + - rhel9cis_rule_2_2_9 tags: - level1-server - level1-workstation - - automated - patch - dovecot - imap - pop3 - - rule_2.2.11 + - rule_2.2.9 -- name: "2.2.12 | PATCH | Ensure Samba is not enabled" - package: +- name: "2.2.10 | PATCH | Ensure Samba is not enabled" + ansible.builtin.package: name: samba state: absent when: - not rhel9cis_samba_server - "'samba' in ansible_facts.packages" - - rhel9cis_rule_2_2_12 + - rhel9cis_rule_2_2_10 tags: - level1-server - level1-workstation - - automated - patch - samba - - rule_2.2.12 + - rule_2.2.10 -- name: "2.2.13 | PATCH | Ensure HTTP Proxy Server is not installed" - package: +- name: "2.2.11 | PATCH | Ensure HTTP Proxy Server is not installed" + ansible.builtin.package: name: squid state: absent when: - not rhel9cis_squid_server - "'squid' in ansible_facts.packages" - - rhel9cis_rule_2_2_6 + - rhel9cis_rule_2_2_11 tags: - level1-server - level1-workstation - - automated - patch - squid - - rule_2.2.13 + - rule_2.2.11 -- name: "2.2.14 | PATCH | Ensure net-snmp is not installed" - package: +- name: "2.2.12 | PATCH | Ensure net-snmp is not installed" + ansible.builtin.package: name: net-snmp state: absent when: - not rhel9cis_snmp_server - "'net-snmp' in ansible_facts.packages" - - rhel9cis_rule_2_2_14 + - rhel9cis_rule_2_2_12 tags: - level1-server - level1-workstation - - automated - patch - snmp - - rule_2.2.14 + - rule_2.2.12 -- name: "2.2.15 | PATCH | Ensure NIS Server is not installed" - package: - name: ypserv - state: absent - when: - - not rhel9cis_nis_server - - "'ypserv' in ansible_facts.packages" - - rhel9cis_rule_2_2_17 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nis - - rule_2.2.17 - -- name: "2.2.16 | PATCH | Ensure telnet-server is not installed" - package: +- name: "2.2.13 | PATCH | Ensure telnet-server is not installed" + ansible.builtin.package: name: telnet-server state: absent when: - not rhel9cis_telnet_server - "'telnet-server' in ansible_facts.packages" - - rhel9cis_rule_2_2_16 + - rhel9cis_rule_2_2_13 tags: - level1-server - level1-workstation - - automated - patch - telnet - - rule_2.2.16 + - rule_2.2.13 -- name: "2.2.17 | PATCH | Ensure mail transfer agent is configured for local-only mode" - lineinfile: +- name: "2.2.14 | PATCH | Ensure dnsmasq is not installed" + ansible.builtin.package: + name: dnsmasq + state: absent + notify: Restart postfix + when: + - not rhel9cis_is_mail_server + - "'dnsmasq' in ansible_facts.packages" + - rhel9cis_rule_2_2_14 + tags: + - level1-server + - level1-workstation + - patch + - dnsmasq + - rule_2.2.14 + +- name: "2.2.15 | PATCH | Ensure mail transfer agent is configured for local-only mode" + ansible.builtin.lineinfile: path: /etc/postfix/main.cf regexp: "^(#)?inet_interfaces" line: "inet_interfaces = loopback-only" - notify: restart postfix + notify: Restart postfix when: - not rhel9cis_is_mail_server - "'postfix' in ansible_facts.packages" - - rhel9cis_rule_2_2_17 + - rhel9cis_rule_2_2_15 tags: - level1-server - level1-workstation - - automated - patch - postfix - - rule_2.2.17 + - rule_2.2.15 # The name title of the service says mask the service, but the fix allows for both options # Options available in default/main if to remove the package default is false just mask the server service -- name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked" +- name: "2.2.16 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked" block: - - name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | remove package" - package: + - name: "2.2.16 | 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 rhel9cis_use_nfs_server - not rhel9cis_use_nfs_service - - name: "2.2.18 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | mask service" - systemd: + - name: "2.2.16 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | mask service" + ansible.builtin.systemd: name: nfs-server masked: true state: stopped @@ -323,30 +280,29 @@ - rhel9cis_use_nfs_service when: - "'nfs-utils' in ansible_facts.packages" - - rhel9cis_rule_2_2_18 + - rhel9cis_rule_2_2_16 tags: - level1-server - level1-workstation - - automated - patch - nfs - services - - rule_2.2.18 + - rule_2.2.16 # The name title of the service says mask the service, but the fix allows for both options # Options available in default/main if to remove the package default is false just mask the server service -- name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked" +- name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked" block: - - name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | remove package" - package: + - name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | remove package" + ansible.builtin.package: name: rpcbind state: absent when: - not rhel9cis_use_rpc_server - not rhel9cis_use_rpc_service - - name: "2.2.19 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | mask service" - systemd: + - name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | mask service" + ansible.builtin.systemd: name: rpcbind.socket masked: true state: stopped @@ -355,29 +311,28 @@ - not rhel9cis_use_rpc_service when: - "'rpcbind' in ansible_facts.packages" - - rhel9cis_rule_2_2_19 + - rhel9cis_rule_2_2_17 tags: - level1-server - level1-workstation - - automated - patch - rpc - - rule_2.2.19 + - rule_2.2.17 # The name title of the service says mask the service, but the fix allows for both options # Options available in default/main if to remove the package default is false just mask the server service -- name: "2.2.20 | PATCH | Ensure rsync service is not enabled " +- name: "2.2.18 | PATCH | Ensure rsync service is not enabled " block: - - name: "2.2.20 | PATCH | Ensure rsync service is not enabled | remove package" - package: - name: rsync + - name: "2.2.18 | PATCH | Ensure rsync-daemon is not installed or the rsync service is masked | remove package" + ansible.builtin.package: + name: rsync-daemon state: absent when: - not rhel9cis_use_rsync_server - not rhel9cis_use_rsync_service - - name: "2.2.20 | PATCH | Ensure rsync service is not enabled | mask service" - systemd: + - name: "2.2.18 | PATCH | Ensure rsync service is not enabled | mask service" + ansible.builtin.systemd: name: rsyncd masked: true state: stopped @@ -386,11 +341,10 @@ - not rhel9cis_use_rsync_service when: - "'rsync' in ansible_facts.packages" - - rhel9cis_rule_2_2_20 + - rhel9cis_rule_2_2_18 tags: - level1-server - level1-workstation - - automated - patch - rsync - - rule_2.2.20 + - rule_2.2.18 diff --git a/tasks/section_2/cis_2.3.x.yml b/tasks/section_2/cis_2.3.x.yml index a1941da..10a0662 100644 --- a/tasks/section_2/cis_2.3.x.yml +++ b/tasks/section_2/cis_2.3.x.yml @@ -1,97 +1,61 @@ --- -- name: "2.3.1 | PATCH | Ensure NIS Client is not installed" - package: - name: ypbind - state: absent - when: - - not rhel9cis_ypbind_required - - "'ypbind' in ansible_facts.packages" - - rhel9cis_rule_2_3_1 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nis - - rule_2.3.1 - -- name: "2.3.2 | PATCH | Ensure rsh client is not installed" - package: - name: rsh - state: absent - when: - - not rhel9cis_rsh_required - - "'rsh' in ansible_facts.packages" - - rhel9cis_rule_2_3_2 - tags: - - level1-server - - level2-server - - automated - - patch - - rsh - - rule_2.3.2 - -- name: "2.3.3 | PATCH | Ensure talk client is not installed" - package: - name: talk - state: absent - when: - - not rhel9cis_talk_required - - "'talk' in ansible_facts.packages" - - rhel9cis_rule_2_3_3 - tags: - - level1-server - - level1-workstation - - automated - - patch - - talk - - rule_2.3.3 - -- name: "2.3.4 | PATCH | Ensure telnet client is not installed" - package: +- name: "2.3.1 | PATCH | Ensure telnet client is not installed" + ansible.builtin.package: name: telnet state: absent when: - not rhel9cis_telnet_required - "'telnet' in ansible_facts.packages" - - rhel9cis_rule_2_3_4 + - rhel9cis_rule_2_3_1 tags: - level1-server - level1-workstation - - automated - patch - telnet - - rule_2.3.4 + - rule_2.3.1 -- name: "2.3.5 | PATCH | Ensure LDAP client is not installed" - package: +- name: "2.3.2 | PATCH | Ensure LDAP client is not installed" + ansible.builtin.package: name: openldap-clients state: absent when: - not rhel9cis_openldap_clients_required - "'openldap-clients' in ansible_facts.packages" - - rhel9cis_rule_2_3_5 + - rhel9cis_rule_2_3_2 tags: - level1-server - level1-workstation - - automated - patch - ldap - - rule_2.3.5 + - rule_2.3.2 -- name: "2.3.6 | PATCH | Ensure TFTP client is not installed" - package: +- name: "2.3.3 | PATCH | Ensure TFTP client is not installed" + ansible.builtin.package: name: tftp state: absent when: - not rhel9cis_tftp_client - "'tftp' in ansible_facts.packages" - - rhel9cis_rule_2_3_6 + - rhel9cis_rule_2_3_3 tags: - level1-server - level1-workstation - - automated - patch - tftp - - rule_2.3.6 + - rule_2.3.3 + +- name: "2.3.4 | PATCH | Ensure FTP client is not installed" + ansible.builtin.package: + name: ftp + state: absent + when: + - not rhel9cis_tftp_client + - "'ftp' in ansible_facts.packages" + - rhel9cis_rule_2_3_4 + tags: + - level1-server + - level1-workstation + - patch + - ftp + - rule_2.3.4 diff --git a/tasks/section_2/cis_2.4.yml b/tasks/section_2/cis_2.4.yml index 3373e54..ce02b40 100644 --- a/tasks/section_2/cis_2.4.yml +++ b/tasks/section_2/cis_2.4.yml @@ -1,25 +1,33 @@ --- -- name: "2.4 | AUDIT | Ensure nonessential services are removed or masked" +- name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked" block: - - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Get list of services" - shell: systemctl list-units --type=service + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Get list of services" + ansible.builtin.shell: systemctl list-units --type=service changed_when: false failed_when: false check_mode: false register: rhel9cis_2_4_services - - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Display list of services" - debug: + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Get list of sockets" + ansible.builtin.shell: systemctl list-units --type=sockets + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_2_4_sockets + + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Display list of services" + ansible.builtin.debug: msg: - - "Warning!! Below are the list of services, both active and inactive" + - "Warning!! Below are the list of services and sockets, both active and inactive" - "Please review to make sure all are essential" - "{{ rhel9cis_2_4_services.stdout_lines }}" + - "{{ rhel9cis_2_4_sockets.stdout_lines }}" - - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Warn Count" - set_fact: - control_number: "{{ control_number }} + ['rule_2.4']" - warn_count: "{{ warn_count | int + 1 }}" + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Warn Count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '2.4' when: - rhel9cis_rule_2_4 tags: diff --git a/tasks/section_2/main.yml b/tasks/section_2/main.yml index 8f79854..39b912d 100644 --- a/tasks/section_2/main.yml +++ b/tasks/section_2/main.yml @@ -1,13 +1,13 @@ --- - name: "SECTION | 2.1 | Time Synchronization" - import_tasks: cis_2.1.x.yml + ansible.builtin.import_tasks: cis_2.1.x.yml - name: "SECTION | 2.2 | Special Purpose Services" - import_tasks: cis_2.2.x.yml + ansible.builtin.import_tasks: cis_2.2.x.yml - name: "SECTION | 2.3 | Service Clients" - import_tasks: cis_2.3.x.yml + ansible.builtin.import_tasks: cis_2.3.x.yml - name: "SECTION | 2.4 | Nonessential services removed" - import_tasks: cis_2.4.yml + ansible.builtin.import_tasks: cis_2.4.yml diff --git a/tasks/section_3/cis_3.1.x.yml b/tasks/section_3/cis_3.1.x.yml index ebe4325..e972ae2 100644 --- a/tasks/section_3/cis_3.1.x.yml +++ b/tasks/section_3/cis_3.1.x.yml @@ -2,15 +2,15 @@ # The CIS Control wants IPv6 disabled if not in use. # We are using the rhel9cis_ipv6_required to specify if you have IPv6 in use -- name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system" +- name: "3.1.1 | PATCH | Ensure IPv6 status is identified" block: - - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system" - set_fact: + - name: "3.1.1 | PATCH | Ensure IPv6 status is identified | refresh" + ansible.builtin.set_fact: sysctl_update: true flush_ipv6_route: true - - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system" - debug: + - name: "3.1.1 | PATCH | Ensure IPv6 status is identified | disable" + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-disable_ipv6.conf" when: - not rhel9cis_ipv6_required @@ -24,72 +24,60 @@ - networking - rule_3.1.1 -- name: "3.1.2 | PATCH | Ensure SCTP is disabled" - template: - src: "etc/modprobe.d/modprobe.conf.j2" - dest: "/etc/modprobe.d/{{ item }}.conf" - mode: "0600" - owner: root - group: root - with_items: - - sctp +- name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled" + block: + - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if nmcli command is available" + ansible.builtin.shell: rpm -q NetworkManager + changed_when: false + failed_when: false + check_mode: false + register: rhel_09_nmcli_available + + - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if wifi is enabled" + ansible.builtin.shell: nmcli radio wifi + register: rhel_09_wifi_enabled + changed_when: rhel_09_wifi_enabled.stdout != "disabled" + failed_when: false + when: rhel_09_nmcli_available.rc == 0 + + - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Disable wifi if enabled" + ansible.builtin.shell: nmcli radio all off + changed_when: false + failed_when: false + when: rhel_09_wifi_enabled is changed when: - rhel9cis_rule_3_1_2 tags: - - level2-server - - level2-workstation - - automated + - level1-server - patch - - sctp + - wireless - rule_3.1.2 -- name: "3.1.3 | PATCH | Ensure DCCP is disabled" - template: - src: "etc/modprobe.d/modprobe.conf.j2" - dest: "/etc/modprobe.d/{{ item }}.conf" - mode: "0600" - owner: root - group: root - with_items: - - dccp +- name: "3.1.3 | PATCH | Ensure TIPC is disabled" + block: + - name: "3.1.3 | PATCH | Ensure TIPC is disabled" + ansible.builtin.template: + src: "etc/modprobe.d/modprobe.conf.j2" + dest: "/etc/modprobe.d/{{ item }}.conf" + mode: "0600" + owner: root + group: root + loop: + - tipc + # note the item used in the template + + - name: "3.1.3 | PATCH | Ensure TIPC is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist tipc(\\s|$)" + line: "blacklist tipc" + create: true + mode: 0600 when: - rhel9cis_rule_3_1_3 tags: - level2-server - level2-workstation - - automated - - dccp - patch + - tipc - rule_3.1.3 - -- name: "3.1.4 | PATCH | Ensure wireless interfaces are disabled" - block: - - name: "3.1.4 | AUDIT | Ensure wireless interfaces are disabled | Check if nmcli command is available" - command: rpm -q NetworkManager - changed_when: false - failed_when: false - check_mode: false - args: - warn: false - register: rhel_08_nmcli_available - - - name: "3.1.4 | AUDIT | Ensure wireless interfaces are disabled | Check if wifi is enabled" - command: nmcli radio wifi - register: rhel_08_wifi_enabled - changed_when: rhel_08_wifi_enabled.stdout != "disabled" - failed_when: false - when: rhel_08_nmcli_available.rc == 0 - - - name: "3.1.4 | PATCH | Ensure wireless interfaces are disabled | Disable wifi if enabled" - command: nmcli radio all off - changed_when: false - failed_when: false - when: rhel_08_wifi_enabled is changed - when: - - rhel9cis_rule_3_1_4 - tags: - - level1-server - - automated - - patch - - wireless - - rule_3.1.4 diff --git a/tasks/section_3/cis_3.2.x.yml b/tasks/section_3/cis_3.2.x.yml index 6e07c55..56e47f7 100644 --- a/tasks/section_3/cis_3.2.x.yml +++ b/tasks/section_3/cis_3.2.x.yml @@ -3,22 +3,22 @@ - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled" block: - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | IPv6" block: - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding | Set Fact" - set_fact: + ansible.builtin.set_fact: flush_ipv6_route: true - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" when: rhel9cis_ipv6_required @@ -28,7 +28,6 @@ tags: - level1-server - level1-workstation - - automated - sysctl - patch - rule_3.2.1 @@ -36,11 +35,11 @@ - name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled" block: - name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - not rhel9cis_is_router @@ -48,7 +47,6 @@ tags: - level1-server - level1-workstation - - automated - patch - sysctl - rule_3.2.2 diff --git a/tasks/section_3/cis_3.3.x.yml b/tasks/section_3/cis_3.3.x.yml index b78593e..84363e7 100644 --- a/tasks/section_3/cis_3.3.x.yml +++ b/tasks/section_3/cis_3.3.x.yml @@ -3,21 +3,21 @@ - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted" block: - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv4 | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv4" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6" block: - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6 | Set Fact" - set_fact: + ansible.builtin.set_fact: flush_ipv6_route: true - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" when: rhel9cis_ipv6_required when: @@ -32,22 +32,22 @@ - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted" block: - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv4 | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv4" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6" block: - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6 | Set Fact" - set_fact: + ansible.builtin.set_fact: flush_ipv6_route: true - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" when: rhel9cis_ipv6_required when: @@ -62,12 +62,12 @@ - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted" block: - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_3 @@ -81,12 +81,12 @@ - name: "3.3.4 | PATCH | Ensure suspicious packets are logged" block: - name: "3.3.4 | PATCH | Ensure suspicious packets are logged | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.4 | PATCH | Ensure suspicious packets are logged" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_4 @@ -100,12 +100,12 @@ - name: "3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored" block: - name: "3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: 3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_5 @@ -119,12 +119,12 @@ - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored" block: - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_6 @@ -138,12 +138,12 @@ - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled" block: - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_7 @@ -157,12 +157,12 @@ - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled" block: - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv4_route: true - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_8 @@ -176,12 +176,12 @@ - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted" block: - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted | IPv6 | Set Fact" - set_fact: + ansible.builtin.set_fact: sysctl_update: true flush_ipv6_route: true - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted | IPv6" - debug: + ansible.builtin.debug: msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl" when: - rhel9cis_ipv6_required diff --git a/tasks/section_3/cis_3.4.1.x.yml b/tasks/section_3/cis_3.4.1.x.yml index d43dfe6..8a7e721 100644 --- a/tasks/section_3/cis_3.4.1.x.yml +++ b/tasks/section_3/cis_3.4.1.x.yml @@ -1,153 +1,59 @@ --- -- name: "3.4.1.1 | PATCH | Ensure firewalld is installed" - package: +- name: "3.4.1.1 | PATCH | Ensure nftables is installed" + ansible.builtin.package: name: - - firewalld - - iptables + - nftables state: present when: - rhel9cis_rule_3_4_1_1 + - rhel9cis_firewall == 'nftables' tags: - level1-server - level1-workstation - - automated - patch - - firewalld + - nftables - rule_3.4.1.1 -- name: "3.4.1.2 | PATCH | Ensure iptables-services not installed with firewalld" +- name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use" block: - - name: "3.4.1.2 | PATCH | Ensure iptables-services not installed with firewalld | Stop running services" - systemd: + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | nftables" + ansible.builtin.systemd: name: "{{ item }}" masked: true - with_items: - - iptables - - ip6tables - when: item in ansible_facts.packages - - - name: "3.4.1.2 | PATCH | Ensure iptables-services not installed with firewalld | remove iptables-services pkg " - package: - name: iptables-services - state: absent + loop: + - firewalld when: + - item in ansible_facts.packages + - rhel9cis_firewall == 'nftables' + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | firewalld" + ansible.builtin.systemd: + name: "{{ item }}" + masked: true + loop: + - nftables + when: + - item in ansible_facts.packages + - rhel9cis_firewall == 'firewalld' + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | package installed" + ansible.builtin.package: + name: "{{ rhel9cis_firewall }}" + state: installed + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | {{ rhel9cis_firewall }} started and enabled" + ansible.builtin.systemd: + name: "{{ rhel9cis_firewall }}" + enabled: true + state: started + when: - rhel9cis_rule_3_4_1_2 - - "'iptables-services' in ansible_facts.packages" tags: - level1-server - level1-workstation - - automated - patch - firewalld + - nftables - rule_3.4.1.2 - -- name: "3.4.1.3 | PATCH | Ensure nftables either not installed or masked with firewalld" - block: - - name: "3.4.1.3 | PATCH | Ensure nftables either not installed or masked with firewalld | mask service" - systemd: - name: nftables - state: stopped - masked: true - when: - - rhel9cis_firewalld_nftables_state == "masked" - - - name: "3.4.1.3 | PATCH | Ensure nftables either not installed or masked with firewalld | pkg removed" - package: - name: nftables - state: absent - when: - - rhel9cis_firewalld_nftables_state == "absent" - when: - - rhel9cis_rule_3_4_1_3 - tags: - - level1-server - - level1-workstation - - automated - - patch - - firewalld - - rule_3_4_1_3 - -- name: "3.4.1.4 | PATCH | Ensure firewalld service is enabled and running" - systemd: - name: firewalld - state: started - enabled: true - when: - - rhel9cis_rule_3_4_1_4 - tags: - - level1-server - - level1-workstation - - automated - - patch - - firewalld - - rule_3_4_1_4 -- name: "3.4.1.5 | PATCH | Ensure firewalld default zone is set" - block: - - name: "3.4.1.5 | AUDIT | Ensure firewalld default zone is set" - shell: "firewall-cmd --get-default-zone | grep {{ rhel9cis_default_zone }}" - changed_when: false - failed_when: ( firewalld_zone_set.rc not in [ 0, 1 ] ) - register: firewalld_zone_set - - - name: "3.4.1.5 | AUDIT | Ensure firewalld default zone is set" - command: firewall-cmd --set-default-zone="{{ rhel9cis_default_zone }}" - when: - - firewalld_zone_set.rc != 0 - when: - - rhel9cis_firewall == "firewalld" - - rhel9cis_rule_3_4_1_5 - tags: - - level1-server - - level1-workstation - - automated - - patch - - firewalld - - rule_3.4.1.5 - -- name: "3.4.1.6 | AUDIT | Ensure network interfaces are assigned to appropriate zone" - block: - - name: "3.4.1.6 | AUDIT | Ensure network interfaces are assigned to appropriate zone | Get list of interfaces and polocies" - shell: "nmcli -t connection show | awk -F: '{ if($4){print $4} }' | while read INT; do firewall-cmd --get-active-zones | grep -B1 $INT; done" - changed_when: false - failed_when: false - check_mode: false - register: rhel9cis_3_4_1_6_interfacepolicy - - - name: "3.4.1.6 | AUDIT | Ensure network interfaces are assigned to appropriate zone | Get list of interfaces and polocies | Show the interface to policy" - debug: - msg: - - "The items below are the policies tied to the interfaces, please correct as needed" - - "{{ rhel9cis_3_4_1_6_interfacepolicy.stdout_lines }}" - when: - - rhel9cis_rule_3_4_1_6 - tags: - - level1-server - - level1-workstation - - manual - - audit - - rule_3.4.1.6 - -- name: "3.4.1.7 | AUDIT | Ensure firewalld drops unnecessary services and ports" - block: - - name: "3.4.1.7 | AUDIT | Ensure firewalld drops unnecessary services and ports | Get list of services and ports" - shell: "firewall-cmd --get-active-zones | awk '!/:/ {print $1}' | while read ZN; do firewall-cmd --list-all --zone=$ZN; done" - changed_when: false - failed_when: false - check_mode: false - register: rhel9cis_3_4_1_7_servicesport - - - name: "3.4.1.7 | AUDIT | Ensure firewalld drops unnecessary services and ports | Show services and ports" - debug: - msg: - - "The items below are the services and ports that are accepted, please correct as needed" - - "{{ rhel9cis_3_4_1_7_servicesport.stdout_lines }}" - when: - - rhel9cis_rule_3_4_1_7 - tags: - - level1-server - - level1-workstation - - manual - - audit - - rule_3.4.1.7 diff --git a/tasks/section_3/cis_3.4.2.x.yml b/tasks/section_3/cis_3.4.2.x.yml index ebb3631..540bda0 100644 --- a/tasks/section_3/cis_3.4.2.x.yml +++ b/tasks/section_3/cis_3.4.2.x.yml @@ -1,352 +1,300 @@ --- -- name: "3.4.2.1 | PATCH | Ensure nftables is installed" - package: - name: nftables - state: present +- name: "3.4.2.1 | PATCH | Ensure firewalld default zone is set" + block: + - name: "3.4.2.1 | AUDIT | Ensure firewalld default zone is set" + ansible.builtin.shell: "firewall-cmd --get-default-zone | grep {{ rhel9cis_default_zone }}" + changed_when: false + failed_when: ( firewalld_zone_set.rc not in [ 0, 1 ] ) + register: firewalld_zone_set + + - name: "3.4.2.1 | AUDIT | Ensure firewalld default zone is set" + ansible.builtin.command: firewall-cmd --set-default-zone="{{ rhel9cis_default_zone }}" + when: firewalld_zone_set.rc != 0 when: - - rhel9cis_firewall == "nftables" + - rhel9cis_firewall == "firewalld" - rhel9cis_rule_3_4_2_1 tags: - level1-server - level1-workstation - - automated - patch - - nftables + - firewalld - rule_3.4.2.1 -# The control allows the service it be masked or not installed -# We have chosen not installed -- name: "3.4.2.2 | PATCH | Ensure firewalld is either not installed or masked with nftables" - package: - name: firewalld - state: absent +- name: "3.4.2.2 | AUDIT | Ensure at least one nftables table exists" + block: + - name: "3.4.2.2 | AUDIT | Ensure a table exists | Check for tables" + ansible.builtin.command: nft list tables + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_2_nft_tables + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Show existing tables" + ansible.builtin.debug: + msg: + - "Below are the current nft tables, please review" + - "{{ rhel9cis_3_4_2_2_nft_tables.stdout_lines }}" + when: rhel9cis_3_4_2_2_nft_tables.stdout | length > 0 + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Alert on no tables" + ansible.builtin.debug: + msg: + - "Warning!! You currently have no nft tables, please review your setup" + - 'Use the command "nft create table inet " to create a new table' + when: + - rhel9cis_3_4_2_2_nft_tables.stdout | length == 0 + - not rhel9cis_nft_tables_autonewtable + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Alert on no tables | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: + - rhel9cis_3_4_2_2_nft_tables.stdout | length == 0 + - not rhel9cis_nft_tables_autonewtable + + - name: "3.4.2.2 | PATCH | Ensure a table exists | Create table if needed" + ansible.builtin.command: nft create table inet "{{ rhel9cis_nft_tables_tablename }}" + failed_when: false + when: rhel9cis_nft_tables_autonewtable + vars: + warn_control_id: '3.4.2.2' when: - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_2 tags: - level1-server - level1-workstation - - automated - patch - nftables - rule_3.4.2.2 -- name: "3.4.2.3 | PATCH | Ensure iptables-services not installed with nftables" +- name: "3.4.2.3 | PATCH | Ensure nftables base chains exist" block: - - name: "3.4.2.3 | PATCH | Ensure iptables-services not installed with nftables | Stop services" - systemd: - name: "{{ item }}" - enabled: false - masked: true - ignore_errors: true # noqa ignore-errors - with_items: - - iptables - - ip6tables - - - name: "3.4.2.3 | PATCH | Ensure iptables-services not installed with nftables | Remove IPTables" - package: - name: iptables-service - state: absent - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_3 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.2.3 - -- name: "3.4.2.4 | PATCH | Ensure iptables are flushed with nftables" - block: - - name: "3.4.2.4 | PATCH | Ensure iptables are flushed with nftables | IPv4" - command: iptables -F - - - name: "3.4.2.4 | PATCH | Ensure iptables are flushed with nftables | IPv6" - command: ip6tables -F - when: rhel9cis_ipv6_required - when: - - rhel9cis_rule_3_4_2_4 - - rhel9cis_firewall != "firewalld" - tags: - - level1-server - - level1-workstation - - manual - - patch - - nftables - - rule_3.4.2.4 - -- name: "3.4.2.5 | AUDIT | Ensure an nftables table exists" - block: - - name: "3.4.2.5 | AUDIT | Ensure a table exists | Check for tables" - command: nft list tables + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for INPUT" + ansible.builtin.shell: nft list ruleset | grep 'hook input' changed_when: false failed_when: false - register: rhel9cis_3_4_2_5_nft_tables + register: rhel9cis_3_4_2_3_input_chains - - name: "3.4.2.5 | AUDIT | Ensure an nftables table exists | Show existing tables" - debug: - msg: - - "Below are the current nft tables, please review" - - "{{ rhel9cis_3_4_2_5_nft_tables.stdout_lines }}" - when: rhel9cis_3_4_2_5_nft_tables.stdout | length > 0 - - - name: "3.4.2.5 | AUDIT | Ensure an nftables table exists | Alert on no tables" - debug: - msg: - - "Warning!! You currently have no nft tables, please review your setup" - - 'Use the command "nft create table inet
" to create a new table' - when: - - rhel9cis_3_4_2_5_nft_tables.stdout | length == 0 - - not rhel9cis_nft_tables_autonewtable - - - name: "3.4.2.5 | AUDIT | Ensure an nftables table exists | Alert on no tables | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_3.4.2.5' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: - - rhel9cis_3_4_2_5_nft_tables.stdout | length == 0 - - not rhel9cis_nft_tables_autonewtable - - - name: "3.4.2.5 | PATCH | Ensure a table exists | Create table if needed" - command: nft create table inet "{{ rhel9cis_nft_tables_tablename }}" - failed_when: false - when: rhel9cis_nft_tables_autonewtable - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_5 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.2.5 - -- name: "3.4.2.6 | PATCH | Ensure nftables base chains exist" - block: - - name: "3.4.2.6 | AUDIT | Ensure nftables base chains exist | Get current chains for INPUT" - shell: nft list ruleset | grep 'hook input' + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for FORWARD" + ansible.builtin.shell: nft list ruleset | grep 'hook forward' changed_when: false failed_when: false - register: rhel9cis_3_4_2_6_input_chains + register: rhel9cis_3_4_2_3_forward_chains - - name: "3.4.2.6 | AUDIT | Ensure nftables base chains exist | Get current chains for FORWARD" - shell: nft list ruleset | grep 'hook forward' + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for OUTPUT" + ansible.builtin.shell: nft list ruleset | grep 'hook output' changed_when: false failed_when: false - register: rhel9cis_3_4_2_6_forward_chains + register: rhel9cis_3_4_2_3_output_chains - - name: "3.4.2.6 | AUDIT | Ensure nftables base chains exist | Get current chains for OUTPUT" - shell: nft list ruleset | grep 'hook output' - changed_when: false - failed_when: false - register: rhel9cis_3_4_2_6_output_chains - - - name: "3.4.2.6 | AUDIT | Ensure nftables base chains exist | Display chains for review" - debug: + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Display chains for review" + ansible.builtin.debug: msg: - "Below are the current INPUT chains" - - "{{ rhel9cis_3_4_2_6_input_chains.stdout_lines }}" + - "{{ rhel9cis_3_4_2_3_input_chains.stdout_lines }}" - "Below are the current FORWARD chains" - - "{{ rhel9cis_3_4_2_6_forward_chains.stdout_lines }}" + - "{{ rhel9cis_3_4_2_3_forward_chains.stdout_lines }}" - "Below are teh current OUTPUT chains" - - "{{ rhel9cis_3_4_2_6_output_chains.stdout_lines }}" + - "{{ rhel9cis_3_4_2_3_output_chains.stdout_lines }}" when: not rhel9cis_nft_tables_autochaincreate - - name: "3.4.2.6 | PATCH | Ensure nftables base chains exist | Create chains if needed" - shell: "{{ item }}" - args: - warn: false + - name: "3.4.2.3 | PATCH | Ensure nftables base chains exist | Create chains if needed" + ansible.builtin.shell: "{{ item }}" failed_when: false - with_items: + loop: - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" input { type filter hook input priority 0 \; } - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" forward { type filter hook forward priority 0 \; } - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" output { type filter hook output priority 0 \; } when: rhel9cis_nft_tables_autochaincreate + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_2_3 + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.2.3 + +- name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured" + block: + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather iif lo accept existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'iif "lo" accept' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_iiflo + + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather ip saddr existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip saddr' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_ipsaddr + + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather ip6 saddr existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip6 saddr' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_ip6saddr + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set iif lo accept rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input iif lo accept + when: '"iif \"lo\" accept" not in rhel9cis_3_4_2_4_iiflo.stdout' + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set ip sddr rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip saddr 127.0.0.0/8 counter drop + when: '"ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_4_ipsaddr.stdout' + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set ip6 saddr rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip6 saddr ::1 counter drop + when: '"ip6 saddr ::1 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_4_ip6saddr.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_2_4 + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.2.4 + + +- name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | firewalld" + ansible.posix.firewalld: + rich_rule: "{{ item }}" + zone: "{{ rhel9cis_default_zone }}" + permanent: true + immediate: true + state: enabled + loop: + - rule family="ipv4" source address="127.0.0.1" destination not address="127.0.0.1" drop + - rule family="ipv6" source address="::1" destination not address="::1" drop + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_4 + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.2.4 + +- name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports" + block: + - name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports | Get list of services and ports" + ansible.builtin.shell: "firewall-cmd --get-active-zones | awk '!/:/ {print $1}' | while read ZN; do firewall-cmd --list-all --zone=$ZN; done" + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_3_4_2_5_servicesport + + - name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports | Show services and ports" + ansible.builtin.debug: + msg: + - "The items below are the services and ports that are accepted, please correct as needed" + - "{{ rhel9cis_3_4_2_5_servicesport.stdout_lines }}" + when: + - rhel9cis_rule_3_4_2_5 + tags: + - level1-server + - level1-workstation + - manual + - audit + - rule_3.4.2.5 + +- name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured" + block: + - name: "3.4.2.6 | AUDIT | EEnsure nftables established connections are configured | Gather incoming connection rules" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_6_inconnectionrule + + - name: "3.4.2.6| AUDIT | Ensure nftables established connections are configured | Gather outbound connection rules" + ansible.builtin.shell: nft list ruleset | awk '/hook output/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_6_outconnectionrule + + - name: "3.4.2.6| PATCH | Ensure nftables established connections are configured | Add input tcp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol tcp ct state established accept + when: '"ip protocol tcp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add input udp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol udp ct state established accept + when: '"ip protocol udp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add input icmp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol icmp ct state established accept + when: '"ip protocol icmp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output tcp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol tcp ct state new,related,established accept + when: '"ip protocol tcp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output udp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol udp ct state new,related,established accept + when: '"ip protocol udp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output icmp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol icmp ct state new,related,established accept + when: '"ip protocol icmp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' when: - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_6 tags: - level1-server - level1-workstation - - automated - patch - nftables - rule_3.4.2.6 -- name: "3.4.2.7 | PATCH | Ensure nftables loopback traffic is configured" +- name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy" block: - - name: "3.4.2.7 | AUDIT | Ensure nftables loopback traffic is configured | Gather iif lo accept existence" - shell: nft list ruleset | awk '/hook input/,/}/' | grep 'iif "lo" accept' - changed_when: false + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook input deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook input' failed_when: false - register: rhel9cis_3_4_2_7_iiflo - - - name: "3.4.2.7 | AUDIT | Ensure nftables loopback traffic is configured | Gather ip saddr existence" - shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip saddr' changed_when: false - failed_when: false - register: rhel9cis_3_4_2_7_ipsaddr + register: rhel9cis_3_4_2_7_inputpolicy - - name: "3.4.2.7 | AUDIT | Ensure nftables loopback traffic is configured | Gather ip6 saddr existence" - shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip6 saddr' + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook forward deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook forward' + failed_when: false changed_when: false + register: rhel9cis_3_4_2_7_forwardpolicy + + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook output deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook output' failed_when: false - register: rhel9cis_3_4_2_7_ip6saddr + changed_when: false + register: rhel9cis_3_4_2_7_outputpolicy - - name: "3.4.2.7 | PATCH | Ensure nftables loopback traffic is configured | Set iif lo accept rule" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input iif lo accept - when: '"iif \"lo\" accept" not in rhel9cis_3_4_2_7_iiflo.stdout' + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for SSH allow" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'ssh' + failed_when: false + changed_when: false + register: rhel9cis_3_4_2_7_sshallowcheck - - name: "3.4.2.7 | PATCH | Ensure nftables loopback traffic is configured | Set ip sddr rule" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip saddr 127.0.0.0/8 counter drop - when: '"ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_7_ipsaddr.stdout' + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Enable SSH traffic" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input tcp dport ssh accept + when: '"tcp dport ssh accept" not in rhel9cis_3_4_2_7_sshallowcheck.stdout' - - name: "3.4.2.7 | PATCH | Ensure nftables loopback traffic is configured | Set ip6 saddr rule" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip6 saddr ::1 counter drop - when: '"ip6 saddr ::1 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_7_ip6saddr.stdout' + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Set hook input deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" input { policy drop \; } + when: '"type filter hook input priority 0; policy drop;" not in rhel9cis_3_4_2_7_inputpolicy.stdout' + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Create hook forward deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" forward { policy drop \; } + when: '"type filter hook forward priority 0; policy drop;" not in rhel9cis_3_4_2_7_forwardpolicy.stdout' + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Create hook output deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" output { policy drop \; } + when: '"type filter hook output priority 0; policy drop;" not in rhel9cis_3_4_2_7_outputpolicy.stdout' when: - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_7 tags: - level1-server - level1-workstation - - automated - patch - nftables - rule_3.4.2.7 - -- name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured" - block: - - name: "3.4.2.8 | AUDIT | Ensure nftables outbound and established connections are configured | Gather incoming connection rules" - shell: nft list ruleset | awk '/hook input/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' - changed_when: false - failed_when: false - register: rhel9cis_3_4_2_8_inconnectionrule - - - name: "3.4.2.8| AUDIT | Ensure nftables outbound and established connections are configured | Gather outbound connection rules" - shell: nft list ruleset | awk '/hook output/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' - changed_when: false - failed_when: false - register: rhel9cis_3_4_2_8_outconnectionrule - - - name: "3.4.2.8| PATCH | Ensure nftables outbound and established connections are configured | Add input tcp established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol tcp ct state established accept - when: '"ip protocol tcp ct state established accept" not in rhel9cis_3_4_2_8_inconnectionrule.stdout' - - - name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured | Add input udp established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol udp ct state established accept - when: '"ip protocol udp ct state established accept" not in rhel9cis_3_4_2_8_inconnectionrule.stdout' - - - name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured | Add input icmp established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol icmp ct state established accept - when: '"ip protocol icmp ct state established accept" not in rhel9cis_3_4_2_8_inconnectionrule.stdout' - - - name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured | Add output tcp new, related, established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol tcp ct state new,related,established accept - when: '"ip protocol tcp ct state established,related,new accept" not in rhel9cis_3_4_2_8_outconnectionrule.stdout' - - - name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured | Add output udp new, related, established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol udp ct state new,related,established accept - when: '"ip protocol udp ct state established,related,new accept" not in rhel9cis_3_4_2_8_outconnectionrule.stdout' - - - name: "3.4.2.8 | PATCH | Ensure nftables outbound and established connections are configured | Add output icmp new, related, established accept policy" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol icmp ct state new,related,established accept - when: '"ip protocol icmp ct state established,related,new accept" not in rhel9cis_3_4_2_8_outconnectionrule.stdout' - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_8 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.3.5 - -- name: "3.4.2.9 | PATCH | Ensure nftables default deny firewall policy" - block: - - name: "3.4.2.9 | AUDIT | Ensure nftables default deny firewall policy | Check for hook input deny policy" - shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook input' - failed_when: false - changed_when: false - register: rhel9cis_3_4_2_9_inputpolicy - - - name: "3.4.2.9 | AUDIT | Ensure nftables default deny firewall policy | Check for hook forward deny policy" - shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook forward' - failed_when: false - changed_when: false - register: rhel9cis_3_4_2_9_forwardpolicy - - - name: "3.4.2.9 | AUDIT | Ensure nftables default deny firewall policy | Check for hook output deny policy" - shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook output' - failed_when: false - changed_when: false - register: rhel9cis_3_4_2_9_outputpolicy - - - name: "3.4.2.9 | AUDIT | Ensure nftables default deny firewall policy | Check for SSH allow" - shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'ssh' - failed_when: false - changed_when: false - register: rhel9cis_3_4_2_9_sshallowcheck - - - name: "3.4.2.9 | PATCH | Ensure nftables default deny firewall policy | Enable SSH traffic" - command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input tcp dport ssh accept - when: '"tcp dport ssh accept" not in rhel9cis_3_4_2_9_sshallowcheck.stdout' - - - name: "3.4.2.9 | PATCH | Ensure nftables default deny firewall policy | Set hook input deny policy" - command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" input { policy drop \; } - when: '"type filter hook input priority 0; policy drop;" not in rhel9cis_3_4_2_9_inputpolicy.stdout' - - - name: "3.4.2.9 | PATCH | Ensure nftables default deny firewall policy | Create hook forward deny policy" - command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" forward { policy drop \; } - when: '"type filter hook forward priority 0; policy drop;" not in rhel9cis_3_4_2_9_forwardpolicy.stdout' - - - name: "3.4.2.9 | PATCH | Ensure nftables default deny firewall policy | Create hook output deny policy" - command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" output { policy drop \; } - when: '"type filter hook output priority 0; policy drop;" not in rhel9cis_3_4_2_9_outputpolicy.stdout' - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_9 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.2.9 - -- name: "3.4.2.10 | PATCH | Ensure nftables service is enabled" - service: - name: nftables - enabled: true - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_10 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.3.7 - -- name: "3.4.2.11 | PATCH | Ensure nftables rules are permanent" - lineinfile: - path: /etc/sysconfig/nftables.conf - state: present - insertafter: EOF - line: include "/etc/nftables/inet-{{ rhel9cis_nft_tables_tablename }}" - when: - - rhel9cis_firewall == "nftables" - - rhel9cis_rule_3_4_2_11 - tags: - - level1-server - - level1-workstation - - automated - - patch - - nftables - - rule_3.4.2.11 diff --git a/tasks/section_3/main.yml b/tasks/section_3/main.yml index 6795a67..535aba9 100644 --- a/tasks/section_3/main.yml +++ b/tasks/section_3/main.yml @@ -1,20 +1,16 @@ --- - name: "SECTION | 3.1.x | Disable unused network protocols and devices" - import_tasks: cis_3.1.x.yml + ansible.builtin.import_tasks: cis_3.1.x.yml - name: "SECTION | 3.2.x | Network Parameters (Host Only)" - import_tasks: cis_3.2.x.yml + ansible.builtin.import_tasks: cis_3.2.x.yml - name: "SECTION | 3.3.x | Network Parameters (host and Router)" - import_tasks: cis_3.3.x.yml + ansible.builtin.import_tasks: cis_3.3.x.yml -- name: "SECTION | 3.4.1.x | Configure firewalld" - import_tasks: cis_3.4.1.x.yml - when: - - rhel9cis_firewall == "firewalld" +- name: "SECTION | 3.4.1.x | Firewall configuration" + ansible.builtin.import_tasks: cis_3.4.1.x.yml -- name: "SECTION | 3.4.2.x | Configure nftables" - include_tasks: cis_3.4.2.x.yml - when: - - rhel9cis_firewall == "nftables" +- name: "SECTION | 3.4.2.x | Configure firewall" + ansible.builtin.import_tasks: cis_3.4.2.x.yml diff --git a/tasks/section_4/cis_4.1.1.x.yml b/tasks/section_4/cis_4.1.1.x.yml index 258b64f..a8be25f 100644 --- a/tasks/section_4/cis_4.1.1.x.yml +++ b/tasks/section_4/cis_4.1.1.x.yml @@ -3,13 +3,13 @@ - name: "4.1.1.1 | PATCH | Ensure auditd is installed" block: - name: "4.1.1.1 | PATCH | Ensure auditd is installed | Install auditd packages" - package: + ansible.builtin.package: name: audit state: present when: '"auditd" not in ansible_facts.packages' - name: "4.1.1.1 | PATCH | Ensure auditd is installed | Install auditd-lib packages" - package: + ansible.builtin.package: name: audit-libs state: present when: '"auditd-lib" not in ansible_facts.packages' @@ -18,92 +18,88 @@ tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.1.1 -- name: "4.1.1.2 | PATCH | Ensure auditd service is enabled" - service: - name: auditd - state: started - enabled: true +- name: "4.1.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled" + block: + - name: "4.1.1.2 | AUDIT | Ensure auditing for processes that start prior to auditd is enabled | Get GRUB_CMDLINE_LINUX" + ansible.builtin.shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_4_1_1_2_grub_cmdline_linux + + - name: "4.1.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Replace existing setting" + ansible.builtin.replace: + path: /etc/default/grub + regexp: 'audit=.' + replace: 'audit=1' + notify: Grub2cfg + when: "'audit=' in rhel9cis_4_1_1_2_grub_cmdline_linux.stdout" + + - name: "4.1.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Add audit setting if missing" + ansible.builtin.lineinfile: + path: /etc/default/grub + regexp: '^GRUB_CMDLINE_LINUX=' + line: '{{ rhel9cis_4_1_1_2_grub_cmdline_linux.stdout }} audit=1"' + notify: Grub2cfg + when: "'audit=' not in rhel9cis_4_1_1_2_grub_cmdline_linux.stdout" when: - rhel9cis_rule_4_1_1_2 tags: - level2-server - level2-workstation - - automated - patch - auditd + - grub - rule_4.1.1.2 -- name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled" +- name: "4.1.1.3 | PATCH | Ensure audit_backlog_limit is sufficient" block: - - name: "4.1.1.3 | AUDIT | Ensure auditing for processes that start prior to auditd is enabled | Get GRUB_CMDLINE_LINUX" - shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' + - name: "4.1.1.3 | AUDIT | Ensure audit_backlog_limit is sufficient | Get GRUB_CMDLINE_LINUX" + ansible.builtin.shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' changed_when: false failed_when: false check_mode: false register: rhel9cis_4_1_1_3_grub_cmdline_linux - - name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Replace existing setting" - replace: - dest: /etc/default/grub - regexp: 'audit=.' - replace: 'audit=1' - notify: grub2cfg - when: "'audit=' in rhel9cis_4_1_1_3_grub_cmdline_linux.stdout" + - name: "4.1.1.3 | PATCH | Ensure audit_backlog_limit is sufficient | Replace existing setting" + ansible.builtin.replace: + path: /etc/default/grub + regexp: 'audit_backlog_limit=\d+' + replace: 'audit_backlog_limit={{ rhel9cis_audit_back_log_limit }}' + notify: Grub2cfg + when: "'audit_backlog_limit=' in rhel9cis_4_1_1_3_grub_cmdline_linux.stdout" - - name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Add audit setting if missing" - lineinfile: + - name: "4.1.1.3 | PATCH | Ensure audit_backlog_limit is sufficient | Add audit_backlog_limit setting if missing" + ansible.builtin.lineinfile: path: /etc/default/grub regexp: '^GRUB_CMDLINE_LINUX=' - line: '{{ rhel9cis_4_1_1_3_grub_cmdline_linux.stdout }} audit=1"' - notify: grub2cfg - when: "'audit=' not in rhel9cis_4_1_1_3_grub_cmdline_linux.stdout" + line: '{{ rhel9cis_4_1_1_3_grub_cmdline_linux.stdout }} audit_backlog_limit={{ rhel9cis_audit_back_log_limit }}"' + notify: Grub2cfg + when: "'audit_backlog_limit=' not in rhel9cis_4_1_1_3_grub_cmdline_linux.stdout" when: - rhel9cis_rule_4_1_1_3 tags: - level2-server - level2-workstation - - automated - patch - auditd - grub - rule_4.1.1.3 -- name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient" - block: - - name: "4.1.1.4 | AUDIT | Ensure audit_backlog_limit is sufficient | Get GRUB_CMDLINE_LINUX" - shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' - changed_when: false - failed_when: false - check_mode: false - register: rhel9cis_4_1_1_4_grub_cmdline_linux - - - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient | Replace existing setting" - replace: - dest: /etc/default/grub - regexp: 'audit_backlog_limit=\d+' - replace: 'audit_backlog_limit={{ rhel9cis_audit_back_log_limit }}' - notify: grub2cfg - when: "'audit_backlog_limit=' in rhel9cis_4_1_1_4_grub_cmdline_linux.stdout" - - - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient | Add audit_backlog_limit setting if missing" - lineinfile: - path: /etc/default/grub - regexp: '^GRUB_CMDLINE_LINUX=' - line: '{{ rhel9cis_4_1_1_4_grub_cmdline_linux.stdout }} audit_backlog_limit={{ rhel9cis_audit_back_log_limit }}"' - notify: grub2cfg - when: "'audit_backlog_limit=' not in rhel9cis_4_1_1_4_grub_cmdline_linux.stdout" +- name: "4.1.1.4 | PATCH | Ensure auditd service is enabled" + ansible.builtin.systemd: + name: auditd + state: started + enabled: true when: - rhel9cis_rule_4_1_1_4 tags: - level2-server - level2-workstation - - automated - patch - auditd - - grub - rule_4.1.1.4 diff --git a/tasks/section_4/cis_4.1.2.x.yml b/tasks/section_4/cis_4.1.2.x.yml index a3ab990..b830b1f 100644 --- a/tasks/section_4/cis_4.1.2.x.yml +++ b/tasks/section_4/cis_4.1.2.x.yml @@ -1,44 +1,42 @@ --- - name: "4.1.2.1 | PATCH | Ensure audit log storage size is configured" - lineinfile: + ansible.builtin.lineinfile: path: /etc/audit/auditd.conf regexp: "^max_log_file( |=)" line: "max_log_file = {{ rhel9cis_max_log_file_size }}" - notify: restart auditd + notify: Restart auditd when: - rhel9cis_rule_4_1_2_1 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.2.1 - name: "4.1.2.2 | PATCH | Ensure audit logs are not automatically deleted" - lineinfile: + ansible.builtin.lineinfile: path: /etc/audit/auditd.conf regexp: "^max_log_file_action" line: "max_log_file_action = {{ rhel9cis_auditd['max_log_file_action'] }}" - notify: restart auditd + notify: Restart auditd when: - rhel9cis_rule_4_1_2_2 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.2.2 - name: "4.1.2.3 | PATCH | Ensure system is disabled when audit logs are full" - lineinfile: + ansible.builtin.lineinfile: path: /etc/audit/auditd.conf regexp: "{{ item.regexp }}" line: "{{ item.line }}" - notify: restart auditd - with_items: + notify: Restart auditd + loop: - { regexp: '^admin_space_left_action', line: 'admin_space_left_action = {{ rhel9cis_auditd.admin_space_left_action }}' } - { regexp: '^action_mail_acct', line: 'action_mail_acct = {{ rhel9cis_auditd.action_mail_acct }}' } - { regexp: '^space_left_action', line: 'space_left_action = {{ rhel9cis_auditd.space_left_action }}' } @@ -47,23 +45,21 @@ tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.2.3 - name: PATCH | Configure other keys for auditd.conf - lineinfile: + ansible.builtin.lineinfile: path: /etc/audit/auditd.conf regexp: "^{{ item }}( |=)" line: "{{ item }} = {{ rhel9cis_auditd_extra_conf[item] }}" loop: "{{ rhel9cis_auditd_extra_conf.keys() }}" - notify: restart auditd + notify: Restart auditd when: - - rhel9cis_auditd_extra_conf.keys() | length > 0 + - rhel9cis_auditd_extra_conf.keys() | length > 0 tags: - level2-server - level2-workstation - - automated - patch - auditd diff --git a/tasks/section_4/cis_4.1.3.x.yml b/tasks/section_4/cis_4.1.3.x.yml index 8272b7e..922ea61 100644 --- a/tasks/section_4/cis_4.1.3.x.yml +++ b/tasks/section_4/cis_4.1.3.x.yml @@ -2,70 +2,65 @@ # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.1 | PATCH | Ensure changes to system administration scope (sudoers) is collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_1 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.1 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.2 | PATCH | Ensure actions as another user are always logged" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_2 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.2 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.3 | PATCH | Ensure events that modify the sudo log file are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_3 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.3 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.4 | PATCH | Ensure events that modify date and time information are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_4 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.4 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.5 | PATCH | Ensure events that modify the system's network environment are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_5 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.5 @@ -74,14 +69,14 @@ - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" block: - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" - shell: for i in $(df | grep '^/dev' | awk '{ print $NF }'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done + ansible.builtin.shell: for i in $(df | grep '^/dev' | awk '{ print $NF }'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done changed_when: false failed_when: false check_mode: false register: priv_procs - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true notify: update auditd when: @@ -89,98 +84,91 @@ tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.6 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.7 | PATCH | Ensure unsuccessful unauthorized file access attempts are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_7 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3_7 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.8 | PATCH | Ensure events that modify user/group information are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_8 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.8 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.9 | PATCH | Ensure discretionary access control permission modification events are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_9 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.9 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.10 | PATCH | Ensure successful file system mounts are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_10 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.10 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.11 | PATCH | Ensure session initiation information is collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_11 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.11 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.12 | PATCH | Ensure login and logout events are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_12 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.12 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.13 | PATCH | Ensure file deletion events by users are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_13 @@ -193,104 +181,97 @@ # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.14 | PATCH | Ensure events that modify the system's Mandatory Access Controls are collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_14 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.14 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.15 | PATCH | Ensure successful and unsuccessful attempts to use the chcon command are recorded" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_15 tags: - level2-server - level2- workstation - - automated - patch - auditd - rule_4.1.3.15 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.16 | PATCH | Ensure successful and unsuccessful attempts to use the setfacl command are recorded" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_16 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.16 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.17 | PATCH | Ensure successful and unsuccessful attempts to use the chacl command are recorded" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_17 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.17 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.18 | PATCH | Ensure successful and unsuccessful attempts to use the usermod command are recorded" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_18 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.18 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.19 | PATCH | Ensure kernel module loading and unloading is collected" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_19 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.3.19 # All changes selected are managed by the POST audit and handlers to update - name: "4.1.3.20 | PATCH | Ensure the audit configuration is immutable" - set_fact: + ansible.builtin.set_fact: update_audit_template: true when: - rhel9cis_rule_4_1_3_20 tags: - level2-server - level2-workstation - - automated - patch - auditd - rule_4.1.20 - name: "4.1.3.21 | AUDIT | Ensure the running and on disk configuration is the same" - debug: + ansible.builtin.debug: msg: - "Please run augenrules --load if you suspect there is a configuration that is not active" when: @@ -304,7 +285,7 @@ - rule_4.1.3.21 - name: Auditd | 4.1.3 | Auditd controls updated - debug: + ansible.builtin.debug: msg: "Auditd Controls handled in POST using template - updating /etc/auditd/rules.d/99_auditd.rules" changed_when: false when: diff --git a/tasks/section_4/cis_4.1.4.x.yml b/tasks/section_4/cis_4.1.4.x.yml new file mode 100644 index 0000000..9eb2bd5 --- /dev/null +++ b/tasks/section_4/cis_4.1.4.x.yml @@ -0,0 +1,178 @@ +--- + +- name: | + "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" + "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" + "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" + + block: + - name: "4.1.4.1 | AUDIT | Ensure audit log files are mode 0640 or less permissive | discover file" + ansible.builtin.shell: grep ^log_file /etc/audit/auditd.conf | awk '{ print $NF }' + register: audit_logfile + changed_when: false + + - name: | + "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" + "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" + "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" + ansible.builtin.file: + path: "{{ audit_logfile.stdout }}" + mode: 0640 + owner: root + group: root + when: + - rhel9cis_rule_4_1_4_1 or + rhel9cis_rule_4_1_4_2 or + rhel9cis_rule_4_1_4_3 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.1 + - rule_4.1.4.2 + - rule_4.1.4.3 + +- name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive" + block: + - name: "4.1.4.4 | AUDIT | Ensure the audit log directory is 0750 or more restrictive | get current permissions" + ansible.builtin.stat: + path: "{{ audit_logfile.stdout | dirname }}" + register: auditlog_dir + + - name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive | set" + ansible.builtin.file: + path: "{{ audit_logfile.stdout | dirname }}" + state: directory + mode: 0750 + when: not auditlog_dir.stat.mode is match('07(0|5)0') + when: + - rhel9cis_rule_4_1_4_4 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.4 + +- name: "4.1.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 }}" + loop_control: + label: "{{ item.path }}" + when: + - item.mode != '06(0|4)0' + - rhel9cis_rule_4_1_4_5 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.5 + +- name: "4.1.4.6 | PATCH | Ensure audit configuration files are owned by root" + ansible.builtin.file: + path: "{{ item.path }}" + owner: root + loop: "{{ auditd_conf_files.files }}" + loop_control: + label: "{{ item.path }}" + when: + - rhel9cis_rule_4_1_4_6 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.6 + +- name: "4.1.4.7 | PATCH | Ensure audit configuration files belong to group root" + ansible.builtin.file: + path: "{{ item.path }}" + group: root + loop: "{{ auditd_conf_files.files }}" + loop_control: + label: "{{ item.path }}" + when: + - rhel9cis_rule_4_1_4_7 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.7 + +- name: "4.1.4.8 | PATCH | Ensure audit tools are 755 or more restrictive" + block: + - name: "4.1.4.8 | AUDIT | Get audit binary file stat | get current mode" + ansible.builtin.stat: + path: "{{ item }}" + register: "audit_bins" + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + + - name: "4.1.4.8 | PATCH | Ensure audit tools are 755 or more restrictive | set if required" + ansible.builtin.file: + path: "{{ item.item }}" + mode: 0750 + + loop: "{{ audit_bins.results }}" + loop_control: + label: "{{ item.item }}" + when: not item.stat.mode is match('07(0|5)0') + when: + - rhel9cis_rule_4_1_4_8 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.8 + +- name: "4.1.4.9 | PATCH | Ensure audit tools are owned by root" + ansible.builtin.file: + path: "{{ item }}" + owner: root + group: root + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + when: + - rhel9cis_rule_4_1_4_9 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.9 + +- name: "4.1.4.10 | PATCH | Ensure audit tools belong to group root" + ansible.builtin.file: + path: "{{ item }}" + group: root + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + when: + - rhel9cis_rule_4_1_4_10 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.10 diff --git a/tasks/section_4/cis_4.2.1.x.yml b/tasks/section_4/cis_4.2.1.x.yml index 12afac1..4eeb61d 100644 --- a/tasks/section_4/cis_4.2.1.x.yml +++ b/tasks/section_4/cis_4.2.1.x.yml @@ -1,7 +1,7 @@ --- - name: "4.2.1.1 | PATCH | Ensure rsyslog installed" - package: + ansible.builtin.package: name: rsyslog state: present when: @@ -10,13 +10,12 @@ tags: - level1-server - level1-workstation - - automated - patch - rsyslog - rule_4.2.1.1 - name: "4.2.1.2 | PATCH | Ensure rsyslog Service is enabled" - service: + ansible.builtin.systemd: name: rsyslog enabled: true when: @@ -24,39 +23,36 @@ tags: - level1-server - level1-workstation - - automated - patch - rsyslog - rule_4.2.1.2 -# This is counter to control 4.2.2.5?? - name: "4.2.1.3 | PATCH | Ensure journald is configured to send logs to rsyslog" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journald.conf regexp: "^#ForwardToSyslog=|^ForwardToSyslog=" line: ForwardToSyslog=yes + notify: Restart rsyslog when: - rhel9cis_rule_4_2_1_3 - - rhel9cis_preferred_log_capture == "rsyslog" + - rhel9cis_syslog == "rsyslog" tags: - level1-server - level1-workstation - - manual - patch - rule_4.2.1.3 - name: "4.2.1.4 | PATCH | Ensure rsyslog default file permissions configured" - lineinfile: + ansible.builtin.lineinfile: path: /etc/rsyslog.conf regexp: '^\$FileCreateMode' line: '$FileCreateMode 0640' - notify: restart rsyslog + notify: Restart rsyslog when: - rhel9cis_rule_4_2_1_4 tags: - level1-server - level1-workstation - - automated - patch - rsyslog - rule_4.2.1.4 @@ -64,22 +60,21 @@ - name: "4.2.1.5 | PATCH | Ensure logging is configured" block: - name: "4.2.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" - command: cat /etc/rsyslog.conf + ansible.builtin.shell: cat /etc/rsyslog.conf changed_when: false failed_when: false check_mode: false - register: rhel_08_4_2_1_5_audit + register: rhel_09_4_2_1_5_audit - name: "4.2.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" - debug: + ansible.builtin.debug: msg: - "These are the current logging configurations for rsyslog, please review:" - - "{{ rhel_08_4_2_1_5_audit.stdout_lines }}" + - "{{ rhel_09_4_2_1_5_audit.stdout_lines }}" - name: "4.2.1.5 | PATCH | Ensure logging is configured | mail.* log setting" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf - state: present marker: "# {mark} MAIL LOG SETTINGS (ANSIBLE MANAGED)" block: | # mail logging additions to meet CIS standards @@ -88,11 +83,11 @@ mail.warning -/var/log/mail.warning mail.err /var/log/mail.err insertafter: '# Log all the mail messages in one place.' - notify: restart rsyslog + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - name: "4.2.1.5 | PATCH | Ensure logging is configured | news.crit log setting" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present marker: "# {mark} NEWS LOG SETTINGS (ANSIBLE MANAGED)" @@ -101,11 +96,11 @@ news.crit -/var/log/news/news.crit news.notice -/var/log/news/news.crit insertafter: '# Save news errors of level crit and higher in a special file.' - notify: restart rsyslog + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - name: "4.2.1.5 | PATCH | Ensure logging is configured | Misc. log setting" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present marker: "# {mark} MISC. LOG SETTINGS (ANSIBLE MANAGED)" @@ -115,11 +110,11 @@ *.crit /var/log/warn *.*;mail.none;news.none /var/log/messages insertafter: '#### RULES ####' - notify: restart rsyslog + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - name: "4.2.1.5 | PATCH | Ensure logging is configured | Local log settings" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present marker: "#{mark} LOCAL LOG SETTINGS (ANSIBLE MANAGED)" @@ -131,10 +126,10 @@ local6,local7.* -/var/log/localmessages *.emrg :omusrmsg:* insertafter: '#### RULES ####' - notify: restart rsyslog + notify: Restart rsyslog - name: "4.2.1.5 | PATCH | Ensure logging is configured | Auth Settings" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present marker: "#{mark} Auth SETTINGS (ANSIBLE MANAGED)" @@ -142,10 +137,10 @@ # Private settings to meet CIS standards auth,authpriv.* /var/log/secure insertafter: '#### RULES ####' - notify: restart rsyslog + notify: Restart rsyslog - name: "4.2.1.5 | PATCH | Ensure logging is configured | Cron Settings" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present marker: "#{mark} Cron SETTINGS (ANSIBLE MANAGED)" @@ -153,37 +148,35 @@ # Cron settings to meet CIS standards cron.* /var/log/cron insertafter: '#### RULES ####' - notify: restart rsyslog + notify: Restart rsyslog when: - rhel9cis_rule_4_2_1_5 tags: - level1-server - level1-workstation - - manual - patch - rsyslog - rule_4.2.1.5 - name: "4.2.1.6 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" - blockinfile: + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present block: | - # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional - *.* @@{{ rhel9cis_remote_log_server }} + # target can be IP or FQDN + *.* action(type="omfwd" target="{{ rhel9cis_remote_log_host }}" port="{{ rhel9cis_remote_log_port }}" protocol="{{ rhel9cis_remote_log_protocol }}" action.resumeRetryCount="{{ rhel9cis_remote_log_retrycount }}" queue.type="LinkedList" queue.size="{{ rhel9cis_remote_log_queuesize }}") insertafter: EOF register: result failed_when: - result is failed - result.rc != 257 - notify: restart rsyslog + notify: Restart rsyslog when: - rhel9cis_rule_4_2_1_6 - - rhel9cis_remote_log_server is defined + - rhel9cis_remote_log_server tags: - level1-server - level1-workstation - - manual - patch - rsyslog - rule_4.2.1.6 @@ -191,12 +184,12 @@ - name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote client" block: - name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote client. | When not log host" - replace: + ansible.builtin.replace: path: /etc/rsyslog.conf - regexp: '({{ item }})' + regexp: '{{ item }}' replace: '#\1' - notify: restart rsyslog - with_items: + notify: Restart rsyslog + loop: - '^(\$ModLoad imtcp)' - '^(\$InputTCPServerRun)' - '^(module\(load="imtcp"\))' @@ -204,23 +197,20 @@ when: not rhel9cis_system_is_log_server - name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote clients. | When log host" - replace: + ansible.builtin.replace: path: /etc/rsyslog.conf regexp: '^#(.*{{ item }}.*)' replace: '\1' - notify: restart rsyslog - with_items: + notify: Restart rsyslog + loop: - 'ModLoad imtcp' - 'InputTCPServerRun' - - 'module\(load="imtcp"\)' - - 'input\(type="imtcp"' when: rhel9cis_system_is_log_server when: - rhel9cis_rule_4_2_1_7 tags: - level1-server - level1-workstation - - automated - patch - rsyslog - rule_4.2.1.7 diff --git a/tasks/section_4/cis_4.2.2.x.yml b/tasks/section_4/cis_4.2.2.x.yml index f172f96..72767a4 100644 --- a/tasks/section_4/cis_4.2.2.x.yml +++ b/tasks/section_4/cis_4.2.2.x.yml @@ -1,7 +1,7 @@ --- - name: "4.2.2.1.1 | PATCH | Ensure systemd-journal-remote is installed" - package: + ansible.builtin.package: name: systemd-journal-remote state: present when: @@ -15,12 +15,12 @@ - rule_4.2.2.1.1 - name: "4.2.2.1.2 | PATCH | Ensure systemd-journal-remote is configured" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journal-upload.conf regexp: "{{ item.regexp }}" line: "{{ item.line }}" - notify: restart systemd_journal_upload - with_items: + notify: Restart systemd_journal_upload + loop: - { regexp: 'URL=', line: 'URL={{ rhel9cis_journal_upload_url }}'} - { regexp: 'ServerKeyFile=', line: 'ServerKeyFile={{ rhel9cis_journal_upload_serverkeyfile }}'} - { regexp: 'ServerCertificateFile=', line: 'ServerCertificateFile={{ rhel9cis_journal_servercertificatefile }}'} @@ -36,7 +36,7 @@ - rule_4.2.2.1.2 - name: "4.2.2.1.3 | PATCH | Ensure systemd-journal-remote is enabled" - systemd: + ansible.builtin.systemd: name: systemd-journal-upload state: started enabled: true @@ -52,7 +52,7 @@ - rule_4.2.2.1.3 - name: "4.2.2.1.4 | PATCH | Ensure journald is not configured to recieve logs from a remote client" - systemd: + ansible.builtin.systemd: name: systemd-journal-remote.socket state: stopped enabled: false @@ -63,7 +63,6 @@ tags: - level1-server - level1-workstation - - automated - patch - journald - rule_4.2.2.1.4 @@ -71,75 +70,74 @@ - name: "4.2.2.2 | PATCH | Ensure journald service is enabled" block: - name: "4.2.2.2 | PATCH | Ensure journald service is enabled | Enable service" - systemd: + ansible.builtin.systemd: name: systemd-journald state: started enabled: true - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Capture status" - shell: systemctl is-enabled systemd-journald.service + ansible.builtin.shell: systemctl is-enabled systemd-journald.service changed_when: false failed_when: false register: rhel9cis_4_2_2_2_status - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Alert on bad status" - debug: + ansible.builtin.debug: msg: - "Warning!! The status of systemd-journald should be static and it is not. Please investigate" when: "'static' not in rhel9cis_4_2_2_2_status.stdout" - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_4.2.2.2' ]" - warn_count: "{{ warn_count | int + 1 }}" + ansible.builtin.import_tasks: warning_facts.yml when: "'static' not in rhel9cis_4_2_2_2_status.stdout" + vars: + warn_control_id: '4.2.2.2' when: - rhel9cis_rule_4_2_2_2 tags: - level1-server - level1-workstation - - automated - audit - journald - rule_4.2.2.2 - name: "4.2.2.3 | PATCH | Ensure journald is configured to compress large log files" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journald.conf regexp: "^#Compress=|^Compress=" line: Compress=yes + notify: Restart systemd_journal_upload when: - rhel9cis_rule_4_2_2_3 tags: - level1-server - level1-workstation - - automated - patch - journald - rule_4.2.2.3 - name: "4.2.2.4 | PATCH | Ensure journald is configured to write logfiles to persistent disk" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journald.conf regexp: "^#Storage=|^Storage=" line: Storage=persistent + notify: Restart systemd_journal_upload when: - rhel9cis_rule_4_2_2_4 tags: - level1-server - level1-workstation - - automated - patch - journald - rule_4.2.2.4 # This is counter to control 4.2.1.3?? - name: "4.2.2.5 | PATCH | Ensure journald is not configured to send logs to rsyslog" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journald.conf regexp: "^ForwardToSyslog=" line: "#ForwardToSyslog=yes" - notify: restart systemd_journal_upload + notify: Restart systemd_journal_upload when: - rhel9cis_rule_4_2_2_5 tags: @@ -151,12 +149,12 @@ - rule_4.2.2.5 - name: "4.2.2.6 | PATCH | Ensure journald log rotation is configured per site policy" - lineinfile: + ansible.builtin.lineinfile: path: /etc/systemd/journald.conf regexp: "{{ item.regexp }}" line: "{{ item.line }}" - notify: restart journald - with_items: + notify: Restart systemd_journal_upload + loop: - { regexp: '^#SystemMaxUse=|^SystemMaxUse=', line: 'SystemMaxUse={{ rhel9cis_journald_systemmaxuse }}'} - { regexp: '^#SystemKeepFree=|^SystemKeepFree=', line: 'SystemKeepFree={{ rhel9cis_journald_systemkeepfree }}' } - { regexp: '^#RuntimeMaxUse=|^RuntimeMaxUse=', line: 'RuntimeMaxUse={{ rhel9cis_journald_runtimemaxuse }}'} @@ -175,35 +173,21 @@ - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured" block: - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Check for override file" - find: - paths: /etc/tmpfiles.d - patterns: systemd.conf - register: rhel9cis_4_2_2_7_override_status + ansible.builtin.stat: + path: /etc/tmpfiles.d/systemd.conf + register: rhel9cis_4_2_2_7_override - - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Get override file settings" - shell: cat /etc/tmpfiles.d/systemd.conf - changed_when: false - failed_when: false - register: rhel9cis_4_2_2_7_override_settings - when: rhel9cis_4_2_2_7_override_status.matched >= 1 + - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Set live file" + ansible.builtin.set_fact: + systemd_conf_file: /etc/tmpfiles.d/systemd.conf + when: rhel9cis_4_2_2_7_override.stat.exists - - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Get non-override file settings" - shell: cat /usr/lib/tmpfiles.d/systemd.conf - changed_when: false - failed_when: false - register: rhel9cis_4_2_2_7_notoverride_settings - when: rhel9cis_4_2_2_7_override_status.matched == 0 + - name: "4.2.2.7 | PATCH | Ensure journald default file permissions configured | Set permission" + ansible.builtin.lineinfile: + path: "{{ systemd_conf_file | default('/usr/lib/tmpfiles.d/systemd.conf') }}" + regexp: "^z \/var\/log\/journal\/%m\/system.journal (!?06(0|4)0) root" + line: 'z /var/log/journal/%m/system.journal 0640 root systemd-journal - -' - - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Display file settings" - debug: - msg: - - "Warning!! Below are the current default settings for journald, please confirm they align with your site policies" - - "{{ (rhel9cis_4_2_2_7_override_status.matched >= 1) | ternary(rhel9cis_4_2_2_7_override_settings.stdout_lines, rhel9cis_4_2_2_7_notoverride_settings.stdout_lines) }}" - - - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Warn Count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_4.2.2.7' ]" - warn_count: "{{ warn_count | int + 1 }}" when: - rhel9cis_rule_4_2_2_7 tags: diff --git a/tasks/section_4/cis_4.2.3.yml b/tasks/section_4/cis_4.2.3.yml index a1b3bb7..a391254 100644 --- a/tasks/section_4/cis_4.2.3.yml +++ b/tasks/section_4/cis_4.2.3.yml @@ -1,15 +1,30 @@ --- - name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured" - command: find /var/log -type f -exec chmod g-wx,o-rwx "{}" + - changed_when: false - failed_when: false + block: + - name: "4.2.3 | AUDIT | Ensure permissions on all logfiles are configured | find files" + ansible.builtin.find: + paths: "/var/log" + file_type: file + recurse: true + register: logfiles + + - name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured | change permissions" + ansible.builtin.file: + path: "{{ item.path }}" + mode: 0640 + loop: "{{ logfiles.files }}" + loop_control: + label: "{{ item.path }}" + when: + - item.path != "/var/log/btmp" + - item.path != "/var/log/utmp" + - item.path != "/var/log/wtmp" when: - rhel9cis_rule_4_2_3 tags: - level1-server - level1-workstation - - automated - patch - logfiles - rule_4.2.3 diff --git a/tasks/section_4/cis_4.3.yml b/tasks/section_4/cis_4.3.yml index 2283d6a..be17c70 100644 --- a/tasks/section_4/cis_4.3.yml +++ b/tasks/section_4/cis_4.3.yml @@ -1,57 +1,55 @@ --- -- name: "4.3.1 | PATCH | Ensure logrotate is installed" - package: - name: rsyslog-logrotate - state: present - when: - - rhel9cis_rule_4_3_1 - tags: - - level1-server - - level1-workstation - - manual - - patch - - logrotate - - rule_4.3.1 - -- name: "4.3.2 | PATCH | Ensure logrotate is running and enabled" - systemd: - name: logrotate.timer - state: started - enabled: true - when: - - rhel9cis_rule_4_3_2 - tags: - - level1-server - - level1-workstation - - manual - - patch - - logrotate - - rule_4.3.2 - -- name: "4.3.3 | PATCH | Ensure logrotate is configured" +- name: "4.3 | PATCH | Ensure logrotate is configured" block: - - name: "4.3.3 | AUDIT | Ensure logrotate is configured | Get logrotate settings" - find: - paths: /etc/logrotate.d/ - register: log_rotates + - name: "4.3 | PATCH | Ensure logrotate is configured | installed" + ansible.builtin.package: + name: rsyslog-logrotate + state: present - - name: "4.3.3 | PATCH | Ensure logrotate is configured" - replace: - path: "{{ item.path }}" + - name: "4.3 | PATCH | Ensure logrotate is configured | scheduled" + ansible.builtin.systemd: + name: logrotate.timer + state: started + enabled: true + + - name: "4.3 | PATCH | Ensure logrotate is configured | set default conf" + ansible.builtin.replace: + path: "/etc/logrotate.conf" regexp: '^(\s*)(daily|weekly|monthly|yearly)$' replace: "\\1{{ rhel9cis_logrotate }}" - with_items: - - "{{ log_rotates.files }}" - - { path: "/etc/logrotate.conf" } - loop_control: - label: "{{ item.path }}" + + - name: "4.3 | AUDIT | Ensure logrotate is configured | Get non default logrotate settings" + ansible.builtin.find: + paths: /etc/logrotate.d/ + contains: '^(\s*)(?!{{ rhel9cis_logrotate }})(daily|weekly|monthly|yearly)$' + register: log_rotates + + - name: "4.3 | AUDIT | Ensure logrotate is configured" + block: + - name: "4.3 | AUDIT | Ensure logrotate is configured | generate file list" + ansible.builtin.set_fact: + logrotate_non_def_conf: "{{ log_rotates.files | map(attribute='path') | join (', ') }}" + + - name: "4.3 | AUDIT | Ensure logrotate is configured | List configured files" + ansible.builtin.debug: + msg: | + "Warning!! The following files are not covered by default logrotate settings ensure they match site policy" + "{{ logrotate_non_def_conf }}" + loop: "{{ log_rotates.files }}" + + - name: "4.3 | AUDIT | Ensure logrotate is configured | Warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '4.3' + when: log_rotates.matched > 0 + when: - - rhel9cis_rule_4_3_3 + - rhel9cis_rule_4_3 tags: - level1-server - level1-workstation - manual - patch - logrotate - - rule_4.3.3 + - rule_4.3 diff --git a/tasks/section_4/main.yml b/tasks/section_4/main.yml index a4f05d2..285a2f3 100644 --- a/tasks/section_4/main.yml +++ b/tasks/section_4/main.yml @@ -1,26 +1,29 @@ --- - name: "SECTION | 4.1 | Configure System Accounting (auditd)" - include_tasks: cis_4.1.1.x.yml + ansible.builtin.import_tasks: cis_4.1.1.x.yml when: - not system_is_container - name: "SECTION | 4.1.2 | Configure Data Retention" - import_tasks: cis_4.1.2.x.yml + ansible.builtin.import_tasks: cis_4.1.2.x.yml - name: "SECTION | 4.1.3 | Configure Auditd rules" - import_tasks: cis_4.1.3.x.yml + ansible.builtin.import_tasks: cis_4.1.3.x.yml + +- name: "SECTION | 4.1.4 | Configure Audit files" + ansible.builtin.import_tasks: cis_4.1.4.x.yml - name: "SECTION | 4.2 | Configure Logging" - import_tasks: cis_4.2.1.x.yml + ansible.builtin.import_tasks: cis_4.2.1.x.yml when: rhel9cis_syslog == 'rsyslog' - name: "SECTION | 4.2.2 | Configure journald" - import_tasks: cis_4.2.2.x.yml + ansible.builtin.import_tasks: cis_4.2.2.x.yml when: rhel9cis_syslog == 'journald' - name: "SECTION | 4.2.3 | Configure logile perms" - import_tasks: cis_4.2.3.yml + ansible.builtin.import_tasks: cis_4.2.3.yml - name: "SECTION | 4.3 | Configure logrotate" - import_tasks: cis_4.3.yml + ansible.builtin.import_tasks: cis_4.3.yml diff --git a/tasks/section_5/cis_5.1.x.yml b/tasks/section_5/cis_5.1.x.yml index 6af5981..f897c6c 100644 --- a/tasks/section_5/cis_5.1.x.yml +++ b/tasks/section_5/cis_5.1.x.yml @@ -1,7 +1,7 @@ --- - name: "5.1.1 | PATCH | Ensure cron daemon is enabled" - service: + ansible.builtin.service: name: crond enabled: true when: @@ -9,14 +9,13 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.1 - name: "5.1.2 | PATCH | Ensure permissions on /etc/crontab are configured" - file: - dest: /etc/crontab + ansible.builtin.file: + path: /etc/crontab owner: root group: root mode: 0600 @@ -25,14 +24,13 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.2 - name: "5.1.3 | PATCH | Ensure permissions on /etc/cron.hourly are configured" - file: - dest: /etc/cron.hourly + ansible.builtin.file: + path: /etc/cron.hourly state: directory owner: root group: root @@ -42,14 +40,13 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.3 - name: "5.1.4 | PATCH | Ensure permissions on /etc/cron.daily are configured" - file: - dest: /etc/cron.daily + ansible.builtin.file: + path: /etc/cron.daily state: directory owner: root group: root @@ -59,14 +56,13 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.4 - name: "5.1.5 | PATCH | Ensure permissions on /etc/cron.weekly are configured" - file: - dest: /etc/cron.weekly + ansible.builtin.file: + path: /etc/cron.weekly state: directory owner: root group: root @@ -80,8 +76,8 @@ - rule_5.1.5 - name: "5.1.6 | PATCH | Ensure permissions on /etc/cron.monthly are configured" - file: - dest: /etc/cron.monthly + ansible.builtin.file: + path: /etc/cron.monthly state: directory owner: root group: root @@ -91,13 +87,12 @@ tags: - level1-server - level1-workstation - - automated - patch - rule_5.1.6 - name: "5.1.7 | PATCH | Ensure permissions on /etc/cron.d are configured" - file: - dest: /etc/cron.d + ansible.builtin.file: + path: /etc/cron.d state: directory owner: root group: root @@ -107,7 +102,6 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.7 @@ -115,18 +109,18 @@ - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users" block: - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Remove cron.deny" - file: - dest: /etc/cron.deny + ansible.builtin.file: + path: /etc/cron.deny state: absent - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Check if cron.allow exists" - stat: + ansible.builtin.stat: path: "/etc/cron.allow" register: rhel9cis_5_1_8_cron_allow_state - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Ensure cron.allow is restricted to authorized users" - file: - dest: /etc/cron.allow + ansible.builtin.file: + path: /etc/cron.allow state: '{{ "file" if rhel9cis_5_1_8_cron_allow_state.stat.exists else "touch" }}' owner: root group: root @@ -136,7 +130,6 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.8 @@ -144,18 +137,18 @@ - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users" block: - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Remove at.deny" - file: - dest: /etc/at.deny + ansible.builtin.file: + path: /etc/at.deny state: absent - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Check if at.allow exists" - stat: + ansible.builtin.stat: path: "/etc/at.allow" register: rhel9cis_5_1_9_at_allow_state - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Ensure at.allow is restricted to authorized users" - file: - dest: /etc/at.allow + ansible.builtin.file: + path: /etc/at.allow state: '{{ "file" if rhel9cis_5_1_9_at_allow_state.stat.exists else "touch" }}' owner: root group: root @@ -165,7 +158,6 @@ tags: - level1-server - level1-workstation - - automated - patch - cron - rule_5.1.9 diff --git a/tasks/section_5/cis_5.2.x.yml b/tasks/section_5/cis_5.2.x.yml index 14484b6..8d0c050 100644 --- a/tasks/section_5/cis_5.2.x.yml +++ b/tasks/section_5/cis_5.2.x.yml @@ -1,9 +1,8 @@ --- - name: "5.2.1 | Ensure permissions on /etc/ssh/sshd_config are configured" - file: - dest: /etc/ssh/sshd_config - state: file + ansible.builtin.file: + path: /etc/ssh/sshd_config owner: root group: root mode: 0600 @@ -12,7 +11,6 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - permissions @@ -21,7 +19,7 @@ - name: "5.2.2 | PATCH | Ensure permissions on SSH private host key files are configured" block: - name: "5.2.2 | AUDIT | Ensure permissions on SSH private host key files are configured | Find the SSH private host keys" - find: + ansible.builtin.find: paths: /etc/ssh patterns: 'ssh_host_*_key' recurse: true @@ -29,13 +27,12 @@ register: rhel9cis_5_2_2_ssh_private_host_key - name: "5.2.2 | PATCH | Ensure permissions on SSH private host key files are configured | Set permissions on SSH private host keys" - file: + ansible.builtin.file: path: "{{ item.path }}" owner: root group: root mode: 0600 - with_items: - - "{{ rhel9cis_5_2_2_ssh_private_host_key.files }}" + loop: "{{ rhel9cis_5_2_2_ssh_private_host_key.files }}" loop_control: label: "{{ item.path }}" when: @@ -43,7 +40,6 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - permissions @@ -52,7 +48,7 @@ - name: "5.2.3 | PATCH | Ensure permissions on SSH public host key files are configured" block: - name: "5.2.3 | AUDIT | Ensure permissions on SSH public host key files are configured | Find the SSH public host keys" - find: + ansible.builtin.find: paths: /etc/ssh patterns: 'ssh_host_*_key.pub' recurse: true @@ -60,13 +56,12 @@ register: rhel9cis_5_2_3_ssh_public_host_key - name: "5.2.3 | PATCH | Ensure permissions on SSH public host key files are configured | Set permissions on SSH public host keys" - file: + ansible.builtin.file: path: "{{ item.path }}" owner: root group: root mode: 0644 - with_items: - - "{{ rhel9cis_5_2_3_ssh_public_host_key.files }}" + loop: "{{ rhel9cis_5_2_3_ssh_public_host_key.files }}" loop_control: label: "{{ item.path }}" when: @@ -74,7 +69,6 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.3 @@ -82,52 +76,51 @@ - name: "5.2.4 | PATCH | Ensure SSH access is limited" block: - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowusers" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^AllowUsers" - line: AllowUsers {{ rhel9cis_sshd['allowusers'] }} + line: "AllowUsers {{ rhel9cis_sshd['allowusers'] }}" validate: sshd -t -f %s - notify: restart sshd + notify: Restart sshd when: "rhel9cis_sshd['allowusers']|default('') | length > 0" - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowgroups" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^AllowGroups" - line: AllowGroups {{ rhel9cis_sshd['allowgroups'] }} + line: "AllowGroups {{ rhel9cis_sshd['allowgroups'] }}" validate: sshd -t -f %s - notify: restart sshd + notify: Restart sshd when: "rhel9cis_sshd['allowgroups']|default('') | length > 0" - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denyusers" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^DenyUsers" - line: DenyUsers {{ rhel9cis_sshd['denyusers'] }} + line: "DenyUsers {{ rhel9cis_sshd['denyusers'] }}" validate: sshd -t -f %s - notify: restart sshd + notify: Restart sshd when: "rhel9cis_sshd['denyusers']|default('') | length > 0" - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denygroups" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^DenyGroups" - line: DenyGroups {{ rhel9cis_sshd['denygroups'] }} + line: "DenyGroups {{ rhel9cis_sshd['denygroups'] }}" validate: sshd -t -f %s - notify: restart sshd + notify: Restart sshd when: "rhel9cis_sshd['denygroups']|default('') | length > 0" when: - rhel9cis_rule_5_2_4 tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.4 - name: "5.2.5 | PATCH | Ensure SSH LogLevel is appropriate" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#LogLevel|^LogLevel" line: 'LogLevel {{ rhel9cis_ssh_loglevel }}' @@ -137,13 +130,12 @@ tags: - level1-server - level1-workstation - - automated - patch - sshs - rule_5.2.5 - name: "5.2.6 | PATCH | Ensure SSH PAM is enabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#UsePAM|^UsePAM" line: 'UsePAM yes' @@ -153,13 +145,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.6 - name: "5.2.7 | PATCH | Ensure SSH root login is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#PermitRootLogin|^PermitRootLogin" line: 'PermitRootLogin no' @@ -169,13 +160,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.7 - name: "5.2.8 | PATCH | Ensure SSH HostbasedAuthentication is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#HostbasedAuthentication|^HostbasedAuthentication" line: 'HostbasedAuthentication no' @@ -185,13 +175,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.8 - name: "5.2.9 | PATCH | Ensure SSH PermitEmptyPasswords is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#PermitEmptyPasswords|^PermitEmptyPasswords" line: 'PermitEmptyPasswords no' @@ -201,13 +190,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.9 - name: "5.2.10 | PATCH | Ensure SSH PermitUserEnvironment is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#PermitUserEnvironment|^PermitUserEnvironment" line: 'PermitUserEnvironment no' @@ -217,13 +205,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.10 - name: "5.2.11 | PATCH | Ensure SSH IgnoreRhosts is enabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#IgnoreRhosts|^IgnoreRhosts" line: 'IgnoreRhosts yes' @@ -233,13 +220,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.11 - name: "5.2.12 | PATCH | Ensure SSH X11 forwarding is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#X11Forwarding|^X11Forwarding" line: 'X11Forwarding no' @@ -249,13 +235,12 @@ tags: - level2-server - level1-workstation - - automated - patch - ssh - rule_5.2.12 - name: "5.2.13 | PATCH | Ensure SSH AllowTcpForwarding is disabled" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#AllowTcpForwarding|^AllowTcpForwarding" line: 'AllowTcpForwarding no' @@ -265,7 +250,6 @@ tags: - level2-server - level2-workstation - - automated - patch - ssh - rule_5.2.13 @@ -273,31 +257,26 @@ - name: "5.2.14 | PATCH | Ensure system-wide crypto policy is not over-ridden" block: - name: "5.2.14 | AUDIT | Ensure system-wide crypto policy is not over-ridden" - shell: grep -i '^\s*CRYPTO_POLICY=' /etc/sysconfig/sshd - args: - warn: false + ansible.builtin.shell: grep -i '^\s*CRYPTO_POLICY=' /etc/sysconfig/sshd changed_when: false failed_when: ( ssh_crypto_discovery.rc not in [ 0, 1 ] ) register: ssh_crypto_discovery - name: "5.2.14 | PATCH | Ensure system-wide crypto policy is not over-ridden" - shell: sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd - args: - warn: false - notify: restart sshd + ansible.builtin.shell: sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd + notify: Restart sshd when: ssh_crypto_discovery.stdout | length > 0 when: - rhel9cis_rule_5_2_14 tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.14 - name: "5.2.15 | PATCH | Ensure SSH warning banner is configured" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^Banner' line: 'Banner /etc/issue.net' @@ -306,13 +285,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.15 - name: "5.2.16 | PATCH | Ensure SSH MaxAuthTries is set to 4 or less" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^(#)?MaxAuthTries \d' line: 'MaxAuthTries 4' @@ -322,13 +300,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.16 - name: "5.2.17 | PATCH | Ensure SSH MaxStartups is configured" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#MaxStartups|^MaxStartups" line: 'MaxStartups 10:30:60' @@ -338,13 +315,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.17 - name: "5.2.18 | PATCH | Ensure SSH MaxSessions is set to 10 or less" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#MaxSessions|^MaxSessions" line: 'MaxSessions {{ rhel9cis_ssh_maxsessions }}' @@ -354,13 +330,12 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.18 - name: "5.2.19 | PATCH | Ensure SSH LoginGraceTime is set to one minute or less" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "^#LoginGraceTime|^LoginGraceTime" line: "LoginGraceTime {{ rhel9cis_sshd['logingracetime'] }}" @@ -370,7 +345,6 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.19 @@ -378,14 +352,14 @@ - name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured" block: - name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured | Add line in sshd_config for ClientAliveInterval" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^ClientAliveInterval' line: "ClientAliveInterval {{ rhel9cis_sshd['clientaliveinterval'] }}" validate: sshd -t -f %s - name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured | Ensure SSH ClientAliveCountMax set to <= 3" - lineinfile: + ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^ClientAliveCountMax' line: "ClientAliveCountMax {{ rhel9cis_sshd['clientalivecountmax'] }}" @@ -395,7 +369,6 @@ tags: - level1-server - level1-workstation - - automated - patch - ssh - rule_5.2.20 diff --git a/tasks/section_5/cis_5.3.x.yml b/tasks/section_5/cis_5.3.x.yml index f9dad14..0443781 100644 --- a/tasks/section_5/cis_5.3.x.yml +++ b/tasks/section_5/cis_5.3.x.yml @@ -1,7 +1,7 @@ --- - name: "5.3.1 | PATCH | Ensure sudo is installed" - package: + ansible.builtin.package: name: sudo state: present when: @@ -9,13 +9,12 @@ tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.1 - name: "5.3.2 | PATCH | Ensure sudo commands use pty" - lineinfile: + ansible.builtin.lineinfile: path: /etc/sudoers line: "Defaults use_pty" validate: '/usr/sbin/visudo -cf %s' @@ -24,13 +23,12 @@ tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.2 - name: "5.3.3 | PATCH | Ensure sudo log file exists" - lineinfile: + ansible.builtin.lineinfile: path: /etc/sudoers regexp: '^Defaults logfile=' line: 'Defaults logfile="{{ rhel9cis_sudolog_location }}"' @@ -40,43 +38,38 @@ tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.3 - name: "5.3.4 | PATCH | Ensure users must provide password for escalation" - replace: + ansible.builtin.replace: path: "{{ item }}" regexp: '^([^#|{% if system_is_ec2 %}ec2-user{% endif %}].*)NOPASSWD(.*)' replace: '\1PASSWD\2' validate: '/usr/sbin/visudo -cf %s' - with_items: - - "{{ rhel9cis_sudoers_files.stdout_lines }}" + loop: "{{ rhel9cis_sudoers_files.stdout_lines }}" when: - rhel9cis_rule_5_3_4 tags: - level2-server - level2-workstation - - automated - patch - sudo - rule_5.3.4 - name: "5.3.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" - replace: + ansible.builtin.replace: path: "{{ item }}" regexp: '^([^#].*)!authenticate(.*)' replace: '\1authenticate\2' validate: '/usr/sbin/visudo -cf %s' - with_items: - - "{{ rhel9cis_sudoers_files.stdout_lines }}" + loop: "{{ rhel9cis_sudoers_files.stdout_lines }}" when: - rhel9cis_rule_5_3_5 tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.5 @@ -84,13 +77,13 @@ - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly" block: - name: "5.3.6 | AUDIT | Ensure sudo authentication timeout is configured correctly | Get files with timeout set" - shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort + ansible.builtin.shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort changed_when: false failed_when: false register: rhel9cis_5_3_6_timeout_files - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" - lineinfile: + ansible.builtin.lineinfile: path: /etc/sudoers regexp: 'Defaults timestamp_timeout=' line: "Defaults timestamp_timeout={{ rhel9cis_sudo_timestamp_timeout }}" @@ -98,20 +91,18 @@ when: rhel9cis_5_3_6_timeout_files.stdout | length == 0 - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" - replace: + ansible.builtin.replace: path: "{{ item }}" regexp: 'timestamp_timeout=(\d+)' replace: "timestamp_timeout={{ rhel9cis_sudo_timestamp_timeout }}" validate: '/usr/sbin/visudo -cf %s' - with_items: - - "{{ rhel9cis_5_3_6_timeout_files.stdout_lines }}" + loop: "{{ rhel9cis_5_3_6_timeout_files.stdout_lines }}" when: rhel9cis_5_3_6_timeout_files.stdout | length > 0 when: - rhel9cis_rule_5_3_6 tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.6 @@ -119,21 +110,21 @@ - name: "5.3.7 | PATCH | Ensure access to the su command is restricted" block: - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" - lineinfile: + ansible.builtin.lineinfile: path: /etc/pam.d/su regexp: '^(#)?auth\s+required\s+pam_wheel\.so' line: 'auth required pam_wheel.so use_uid {% if rhel9cis_sugroup is defined %}group={{ rhel9cis_sugroup }}{% endif %}' - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | wheel group contains root" - user: - name: "{{ rhel9cis_sugroup_users }}" + ansible.builtin.user: + name: "{{ item }}" groups: "{{ rhel9cis_sugroup | default('wheel') }}" + loop: "{{ rhel9cis_sugroup_users }}" when: - rhel9cis_rule_5_3_7 tags: - level1-server - level1-workstation - - automated - patch - sudo - rule_5.3.7 diff --git a/tasks/section_5/cis_5.4.x.yml b/tasks/section_5/cis_5.4.x.yml index 11ddbbd..cb37024 100644 --- a/tasks/section_5/cis_5.4.x.yml +++ b/tasks/section_5/cis_5.4.x.yml @@ -3,22 +3,20 @@ - 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" - shell: 'authselect current | grep "Profile ID: custom/"' + ansible.builtin.shell: 'authselect current | grep "Profile ID: custom/"' failed_when: false changed_when: false check_mode: false register: rhel9cis_5_4_1_profiles - name: "5.4.1 | AUDIT | Ensure custom authselect profile is used | Show profiles" - debug: + ansible.builtin.debug: msg: - "Below are the current custom profiles" - "{{ rhel9cis_5_4_1_profiles.stdout_lines }}" - name: "5.4.1 | PATCH | Ensure custom authselect profile is used | Create custom profiles" - shell: authselect create-profile {{ rhel9cis_authselect['custom_profile_name'] }} -b {{ rhel9cis_authselect['default_file_to_copy'] }} - args: - warn: false + ansible.builtin.shell: authselect create-profile {{ rhel9cis_authselect['custom_profile_name'] }} -b {{ rhel9cis_authselect['default_file_to_copy'] }} when: rhel9cis_authselect_custom_profile_create when: - rhel9cis_rule_5_4_1 @@ -30,32 +28,58 @@ - authselect - rule_5.4.1 -- name: "5.4.2 | PATCH | Ensure authselect includes with-faillock" +- name: "5.4.2 | PATCH | Ensure authselect includes with-faillock | with auth select profile" block: - name: "5.4.2 | AUDIT | Ensure authselect includes with-faillock | Gather profiles and enabled features" - shell: "authselect current | grep with-faillock" + ansible.builtin.shell: "authselect current | grep with-faillock" failed_when: false changed_when: false check_mode: false register: rhel9cis_5_4_2_profiles_faillock - - name: "5.4.2 | AUDIT | Ensure authselect includes with-faillock| Show profiles" - debug: + - name: "5.4.2 | AUDIT | Ensure authselect includes with-faillock | Show profiles" + ansible.builtin.debug: msg: - "Below are the current custom profiles" - "{{ rhel9cis_5_4_2_profiles_faillock.stdout_lines }}" - name: "5.4.2 | PATCH | Ensure authselect includes with-faillock | Create custom profiles" - shell: "authselect select custom/{{ rhel9cis_authselect['custom_profile_name'] }} with-faillock" - args: - warn: false + ansible.builtin.shell: "authselect select custom/{{ rhel9cis_authselect['custom_profile_name'] }} with-faillock" when: rhel9cis_authselect_custom_profile_select + + - name: 5.4.2 | PATCH | Ensure authselect includes with-faillock | not auth select profile" + ansible.builtin.lineinfile: + path: "/etc/pam.d/password-auth" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before }}" + loop: + - { 'regexp': '^auth\s+required\s+pam_faillock.so preauth silent deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so preauth silent deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+sufficient\s+pam_unix.so try_first_pass'} + - { 'regexp': '^auth\s+required\s+pam_faillock.so authfail deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so authfail deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+required\s+pam_deny.so'} + - { 'regexp': '^account\s+required\s+pam_faillock.so', 'line': 'account required pam_faillock.so', 'before':'^account required pam_unix.so'} + when: + - rhel9cis_add_faillock_without_authselect + - rhel9cis_5_4_2_risks == 'ACCEPT' + + - name: 5.4.2 | PATCH | Ensure authselect includes with-faillock | not auth select profile" + ansible.builtin.lineinfile: + path: "/etc/pam.d/system-auth" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before | default(omit)}}" + insertafter: "{{ item.after | default(omit)}}" + loop: + - { 'regexp': '^auth\s+required\s+pam_faillock.so preauth silent deny=.*unlock_time=.*', 'line':'auth required pam_faillock.so preauth silent deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+sufficient\s+pam_unix.so try_first_pass'} + - { 'regexp': '^auth\s+required\s+pam_faillock.so authfail deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so authfail deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+required\s+pam_deny.so'} + - { 'regexp': '^account\s+required\s+pam_faillock.so', 'line': 'account required pam_faillock.so', 'before':'^account required pam_unix.so'} + when: + - rhel9cis_add_faillock_without_authselect + - rhel9cis_5_4_2_risks == 'ACCEPT' when: - rhel9cis_rule_5_4_2 tags: - level1-server - level1-workstation - - automated - patch - authselect - rule_5.4.2 diff --git a/tasks/section_5/cis_5.5.x.yml b/tasks/section_5/cis_5.5.x.yml index d16d91f..13ac418 100644 --- a/tasks/section_5/cis_5.5.x.yml +++ b/tasks/section_5/cis_5.5.x.yml @@ -1,25 +1,25 @@ --- -- name: "5.5.1 | PATCH | " +- name: "5.5.1 | PATCH | Ensure password creation requirements are configured" block: - name: "5.5.1 | PATCH | Ensure password creation requirements are configured | Set pwquality config settings" - lineinfile: + ansible.builtin.lineinfile: path: /etc/security/pwquality.conf regexp: ^{{ item.name }} line: "{{ item.name }} = {{ item.value }}" - with_items: + loop: - { name: minlen, value: "{{ rhel9cis_pam_password.minlen }}" } - { name: minclass, value: "{{ rhel9cis_pam_password.minclass }}" } - name: "5.5.1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings" - lineinfile: + ansible.builtin.lineinfile: path: /etc/pam.d/system-auth regexp: '^password\s*requisite\s*pam_pwquality.so' line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce_for_root retry=3" insertbefore: '^#?password ?' - name: "5.5.1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings" - lineinfile: + ansible.builtin.lineinfile: path: /etc/pam.d/password-auth regexp: '^password\s*requisite\s*pam_pwquality.so' line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce_for_root retry=3" @@ -32,12 +32,12 @@ - patch - rule_5.5.1 -- name: "5.5.2 | PATCH | Ensure system accounts are secured" - lineinfile: +- name: "5.5.2 | PATCH | Ensure lockout for failed password attempts is configured" + ansible.builtin.lineinfile: path: /etc/security/faillock.conf regexp: "{{ item.regexp }}" line: "{{ item.line }}" - with_items: + loop: - { regexp: '^\s*deny\s*=\s*[1-5]\b', line: 'deny = 5' } - { regexp: '^\s*unlock_time\s*=\s*(0|9[0-9][0-9]|[1-9][0-9][0-9][0-9]+)\b', line: 'unlock_time = 900' } when: @@ -45,17 +45,17 @@ - name: "5.5.3 | PATCH | Ensure password reuse is limited" block: - - name: "5.5.3 | PATCH | Ensure password reuse is limited | pwhistory" - lineinfile: + - name: "5.5.3 | PATCH | Ensure password reuse is limited | pwquality" + ansible.builtin.lineinfile: path: /etc/pam.d/system-auth line: "password requisite pam_pwhistory.so try_first_pass local_users_only enforce_for_root retry=3 remember={{ rhel9cis_pam_faillock.remember }}" insertafter: '^password\s*requisite\s*pam_pwquality.so' - name: "5.5.3 | PATCH | Ensure password reuse is limited | pam_unix" - replace: + ansible.builtin.replace: path: /etc/pam.d/system-auth - regexp: '^password\s*sufficient\s*pam_unix.so.*$' - replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' + regexp: '^password\s*(sufficient|requisite|sufficient)\s*pam_unix.so.*$' + replace: 'password requisite pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' when: - rhel9cis_rule_5_5_3 tags: @@ -64,31 +64,31 @@ - patch - rule_5.5.3 -- name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512" +- name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 or yescrypt" block: - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | libuser.conf" - replace: + ansible.builtin.replace: path: /etc/libuser.conf regexp: '^crypt_style\s*=\s*.*$' replace: 'crypt_style = sha512' - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | login.defs" - replace: + ansible.builtin.replace: path: /etc/login.defs regexp: '^ENCRYPT_METHOD.*' replace: 'ENCRYPT_METHOD SHA512' - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | password-auth" - replace: + ansible.builtin.replace: path: /etc/pam.d/password-auth regexp: '^password\s*sufficient\s*pam_unix.so.*$' replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | system-auth" - replace: + ansible.builtin.replace: path: /etc/pam.d/system-auth regexp: '^password\s*sufficient\s*pam_unix.so.*$' - replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' + replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' when: - rhel9cis_rule_5_5_4 tags: diff --git a/tasks/section_5/cis_5.6.1.x.yml b/tasks/section_5/cis_5.6.1.x.yml index 358d075..141c013 100644 --- a/tasks/section_5/cis_5.6.1.x.yml +++ b/tasks/section_5/cis_5.6.1.x.yml @@ -1,7 +1,7 @@ --- - name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less" - lineinfile: + ansible.builtin.lineinfile: path: /etc/login.defs regexp: '^PASS_MAX_DAYS' line: "PASS_MAX_DAYS {{ rhel9cis_pass['max_days'] }}" @@ -10,13 +10,12 @@ tags: - level1-server - level1-workstation - - automated - patch - password - rule_5.5.1.1 - name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more" - lineinfile: + ansible.builtin.lineinfile: path: /etc/login.defs regexp: '^PASS_MIN_DAYS' line: "PASS_MIN_DAYS {{ rhel9cis_pass['min_days'] }}" @@ -25,13 +24,12 @@ tags: - level1-server - level1-workstation - - automated - patch - password - rule_5.6.1.2 - name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more" - lineinfile: + ansible.builtin.lineinfile: path: /etc/login.defs regexp: '^PASS_WARN_AGE' line: "PASS_WARN_AGE {{ rhel9cis_pass['warn_age'] }}" @@ -40,7 +38,6 @@ tags: - level1-server - level1-workstation - - automated - patch - password - rule_5.5.1.3 @@ -48,32 +45,30 @@ - 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 | Check current settings" - shell: useradd -D | grep INACTIVE={{ rhel9cis_inactivelock.lock_days }} | cut -f2 -d= + ansible.builtin.shell: useradd -D | grep INACTIVE={{ rhel9cis_inactivelock.lock_days }} | cut -f2 -d= changed_when: false failed_when: false check_mode: false register: rhel9cis_5_6_1_4_inactive_settings - name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less | Set default inactive setting" - command: useradd -D -f {{ rhel9cis_inactivelock.lock_days }} + ansible.builtin.shell: useradd -D -f {{ rhel9cis_inactivelock.lock_days }} when: rhel9cis_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" - shell: "awk -F: '/^[^#:]+:[^\\!\\*:]*:[^:]*:[^:]*:[^:]*:[^:]*:(\\s*|-1|3[1-9]|[4-9][0-9]|[1-9][0-9][0-9]+):[^:]*:[^:]*\\s*$/ {print $1}' /etc/shadow" + 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 + register: rhel9cis_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" - command: chage --inactive {{ rhel9cis_inactivelock.lock_days }} "{{ item }}" - with_items: - - "{{ rhel_8_5_6_1_4_user_list.stdout_lines }}" + ansible.builtin.shell: chage --inactive {{ rhel9cis_inactivelock.lock_days }} "{{ item }}" + loop: "{{ rhel9cis_5_6_1_4_user_list.stdout_lines }}" when: - rhel9cis_rule_5_6_1_4 tags: - level1-server - level1-workstation - - automated - patch - password - rule_5.6.1.4 @@ -81,46 +76,40 @@ - name: "5.6.1.5 | PATCH | Ensure all users last password change date is in the past" block: - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Get current date in Unix Time" - shell: echo $(($(date --utc --date "$1" +%s)/86400)) + ansible.builtin.shell: echo $(($(date --utc --date "$1" +%s)/86400)) changed_when: false failed_when: false check_mode: false register: rhel9cis_5_6_1_5_currentut - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Get list of users with last changed pw date in the future" - shell: "cat /etc/shadow | awk -F: '{if($3>{{ rhel9cis_5_6_1_5_currentut.stdout }})print$1}'" + ansible.builtin.shell: "cat /etc/shadow | awk -F: '{if($3>{{ rhel9cis_5_6_1_5_currentut.stdout }})print$1}'" changed_when: false failed_when: false check_mode: false register: rhel9cis_5_6_1_5_user_list - - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Alert no pw change in the future exist" - debug: - msg: "Good News! All accounts have PW change dates that are in the past" - when: rhel9cis_5_6_1_5_user_list.stdout | length == 0 - - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Alert on accounts with pw change in the future" - debug: + ansible.builtin.debug: msg: "Warning!! The following accounts have the last PW change date in the future: {{ rhel9cis_5_6_1_5_user_list.stdout_lines }}" when: - rhel9cis_5_6_1_5_user_list.stdout | length > 0 - not rhel9cis_futurepwchgdate_autofix - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_5.6.1.5' ]" - warn_count: "{{ warn_count | int + 1 }}" + ansible.builtin.import_tasks: warning_facts.yml when: - rhel9cis_5_6_1_5_user_list.stdout | length > 0 - not rhel9cis_futurepwchgdate_autofix - name: "5.6.1.5 | PATCH | Ensure all users last password change date is in the past | Fix accounts with pw change in the future" - command: passwd --expire {{ item }} + ansible.builtin.shell: passwd --expire {{ item }} when: - rhel9cis_5_6_1_5_user_list.stdout | length > 0 - rhel9cis_futurepwchgdate_autofix - with_items: - - "{{ rhel9cis_5_6_1_5_user_list.stdout_lines }}" + loop: "{{ rhel9cis_5_6_1_5_user_list.stdout_lines }}" + vars: + warn_control_id: '5.6.1.5' when: - rhel9cis_rule_5_6_1_5 tags: diff --git a/tasks/section_5/cis_5.6.x.yml b/tasks/section_5/cis_5.6.x.yml index f1052c3..349095a 100644 --- a/tasks/section_5/cis_5.6.x.yml +++ b/tasks/section_5/cis_5.6.x.yml @@ -3,11 +3,10 @@ - name: "5.6.2 | PATCH | Ensure system accounts are secured" block: - name: "5.6.2 | Ensure system accounts are secured | Set nologin" - user: + ansible.builtin.user: name: "{{ item.id }}" shell: /usr/sbin/nologin - with_items: - - "{{ rhel9cis_passwd }}" + loop: "{{ rhel9cis_passwd }}" when: - item.id != "root" - item.id != "sync" @@ -21,11 +20,10 @@ label: "{{ item.id }}" - name: "5.6.2 | PATCH | Ensure system accounts are secured | Lock accounts" - user: + ansible.builtin.user: name: "{{ item.id }}" password_lock: true - with_items: - - "{{ rhel9cis_passwd }}" + loop: "{{ rhel9cis_passwd }}" when: - item.id != "halt" - item.id != "shutdown" @@ -42,37 +40,35 @@ tags: - level1-server - level1-workstation - - automated - patch - accounts - rule_5.6.2 - name: "5.6.3 | PATCH | Ensure default user shell timeout is 900 seconds or less" - blockinfile: - create: true - mode: 0644 - dest: "{{ item.dest }}" + ansible.builtin.blockinfile: + path: "{{ item.path }}" state: "{{ item.state }}" marker: "# {mark} CIS 5.6.3 ANSIBLE MANAGED" + create: true + mode: 0644 block: | TMOUT={{ rhel9cis_shell_session_timeout.timeout }} export TMOUT readonly TMOUT - with_items: - - { dest: "{{ rhel9cis_shell_session_timeout.file }}", state: present } - - { dest: /etc/profile, state: "{{ (rhel9cis_shell_session_timeout.file == '/etc/profile') | ternary('present', 'absent') }}" } + loop: + - { path: "{{ rhel9cis_shell_session_timeout.file }}", state: present } + - { path: /etc/profile, state: "{{ (rhel9cis_shell_session_timeout.file == '/etc/profile') | ternary('present', 'absent') }}" } when: - rhel9cis_rule_5_6_3 tags: - level1-server - level1-workstation - - automated - patch - accounts - rule_5.6.3 - name: "5.6.4 | PATCH | Ensure default group for the root account is GID 0" - user: + ansible.builtin.user: name: root group: 0 when: @@ -80,7 +76,6 @@ tags: - level1-server - level1-workstation - - automated - patch - accounts - rule_5.6.4 @@ -88,7 +83,7 @@ - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive" block: - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/login.defs pam_umask settings" - replace: + ansible.builtin.replace: path: /etc/login.defs regexp: "{{ item.regexp }}" replace: "{{ item.replace }}" @@ -97,13 +92,13 @@ - { regexp: '(USERGROUPS_ENAB\s+)yes', replace: '\1 no' } - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/bashrc" - replace: + ansible.builtin.replace: path: /etc/bashrc regexp: '^(?i)(\s+UMASK|UMASK)\s0[0-2][0-6]' replace: '\1 027' - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/profile" - replace: + ansible.builtin.replace: path: /etc/profile regexp: '^(?i)(\s+UMASK|UMASK)\s0[0-2][0-6]' replace: '\1 027' @@ -112,7 +107,19 @@ tags: - level1-server - level1-workstation - - automated - patch - accounts - rule_5.6.5 + +- name: "5.6.6 | PATCH | Ensure root password is set" + ansible.builtin.debug: + msg: "The root password has been set as per the assert in early stages" + when: + - rhel9cis_rule_5_6_6 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - root + - rule_5.6.6 diff --git a/tasks/section_5/main.yml b/tasks/section_5/main.yml index b7db859..5aed1c1 100644 --- a/tasks/section_5/main.yml +++ b/tasks/section_5/main.yml @@ -3,24 +3,24 @@ # Access, Authentication, and Authorization - name: "SECTION | 5.1 | Configure time-based job schedulers" - import_tasks: cis_5.1.x.yml + ansible.builtin.import_tasks: cis_5.1.x.yml - name: "SECTION | 5.2 | Configure SSH Server" - include_tasks: cis_5.2.x.yml + ansible.builtin.import_tasks: cis_5.2.x.yml when: - "'openssh-server' in ansible_facts.packages" - name: "SECTION | 5.3 | Configure privilege escalation" - include_tasks: cis_5.3.x.yml + ansible.builtin.import_tasks: cis_5.3.x.yml - name: "SECTION | 5.4 | Configure authselect" - import_tasks: cis_5.4.x.yml + ansible.builtin.import_tasks: cis_5.4.x.yml - name: "SECTION | 5.5 | Configure PAM " - import_tasks: cis_5.5.x.yml + ansible.builtin.import_tasks: cis_5.5.x.yml - name: "SECTION | 5.6.1.x | Shadow Password Suite Parameters" - import_tasks: cis_5.6.1.x.yml + ansible.builtin.import_tasks: cis_5.6.1.x.yml - name: "SECTION | 5.6.x | Misc. User Account Settings" - import_tasks: cis_5.6.x.yml + ansible.builtin.import_tasks: cis_5.6.x.yml diff --git a/tasks/section_6/cis_6.1.x.yml b/tasks/section_6/cis_6.1.x.yml index 29d98b3..298492d 100644 --- a/tasks/section_6/cis_6.1.x.yml +++ b/tasks/section_6/cis_6.1.x.yml @@ -1,72 +1,38 @@ --- -- name: "6.1.1 | AUDIT | Audit system file permissions" - block: - - name: "6.1.1 | AUDIT | Audit system file permissions | Audit the packages" - shell: rpm -Va --nomtime --nosize --nomd5 --nolinkto - args: - warn: false - changed_when: false - failed_when: false - register: rhel9cis_6_1_1_packages_rpm - - - name: "6.1.1 | AUDIT | Audit system file permissions | Create list and warning" - block: - - name: "6.1.1 | AUDIT | Audit system file permissions | Add file discrepancy list to system" - copy: - dest: "{{ rhel9cis_rpm_audit_file }}" # noqa template-instead-of-copy - content: "{{ rhel9cis_6_1_1_packages_rpm.stdout }}" - owner: root - group: root - mode: 0640 - - - name: "6.1.1 | AUDIT | Audit system file permissions | Message out alert for package descrepancies" - debug: - msg: | - "Warning!! You have some package descrepancies issues. - The file list can be found in {{ rhel9cis_rpm_audit_file }}" - - - name: "6.1.1 | AUDIT | Audit system file permissions | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.1.1' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_1_1_packages_rpm.stdout|length > 0 - - - name: "6.1.1 | AUDIT | Audit system file permissions | Message out no package descrepancies" - debug: - msg: "Good News! There are no package descrepancies" - when: rhel9cis_6_1_1_packages_rpm.stdout|length == 0 +- name: "6.1.1 | PATCH | Ensure permissions on /etc/passwd are configured" + ansible.builtin.file: + path: /etc/passwd + owner: root + group: root + mode: 0644 when: - rhel9cis_rule_6_1_1 tags: - - level2-server - - level2-workstation - - manual - - audit + - level1-server + - level1-workstation + - patch - permissions - rule_6.1.1 -- name: "6.1.2 | PATCH | Ensure sticky bit is set on all world-writable directories" - shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d -perm -0002 2>/dev/null | xargs chmod a+t - args: - warn: false - changed_when: false - failed_when: false +- name: "6.1.2 | PATCH | Ensure permissions on /etc/passwd- are configured" + ansible.builtin.file: + path: /etc/passwd- + owner: root + group: root + mode: 0644 when: - rhel9cis_rule_6_1_2 tags: - - skip_ansible_lint - level1-server - level1-workstation - - automated - patch - - stickybits - - permissons - - rule_1.1.21 + - permissions + - rule_6.1.2 -- name: "6.1.3 | PATCH | Ensure permissions on /etc/passwd are configured" - file: - dest: /etc/passwd +- name: "6.1.3 | PATCH | Ensure permissions on /etc/group are configured" + ansible.builtin.file: + path: /etc/group- owner: root group: root mode: 0644 @@ -75,46 +41,43 @@ tags: - level1-server - level1-workstation - - automated - patch - permissions - rule_6.1.3 -- name: "6.1.4 | PATCH | Ensure permissions on /etc/shadow are configured" - file: - dest: /etc/shadow +- name: "6.1.4 | PATCH | Ensure permissions on /etc/group- are configured" + ansible.builtin.file: + path: /etc/group- owner: root group: root - mode: 0000 + mode: 0644 when: - rhel9cis_rule_6_1_4 tags: - level1-server - level1-workstation - - automated - patch - - permissions + - permissionss - rule_6.1.4 -- name: "6.1.5 | PATCH | Ensure permissions on /etc/group are configured" - file: - dest: /etc/group- +- name: "6.1.5 | PATCH | Ensure permissions on /etc/shadow are configured" + ansible.builtin.file: + path: /etc/shadow owner: root group: root - mode: 0644 + mode: 0000 when: - rhel9cis_rule_6_1_5 tags: - level1-server - level1-workstation - - automated - patch - permissions - rule_6.1.5 -- name: "6.1.6 | PATCH | Ensure permissions on /etc/gshadow are configured" - file: - dest: /etc/gshadow +- name: "6.1.6 | PATCH | Ensure permissions on /etc/shadow- are configured" + ansible.builtin.file: + path: /etc/shadow- owner: root group: root mode: 0000 @@ -123,196 +86,216 @@ tags: - level1-server - level1-workstation - - automated - patch - permissions - rule_6.1.6 -- name: "6.1.7 | PATCH | Ensure permissions on /etc/passwd- are configured" - file: - dest: /etc/passwd- +- name: "6.1.7 | PATCH | Ensure permissions on /etc/gshadow are configured" + ansible.builtin.file: + path: /etc/gshadow owner: root group: root - mode: 0644 + mode: 0000 when: - rhel9cis_rule_6_1_7 tags: - level1-server - level1-workstation - - automated - patch - permissions - rule_6.1.7 -- name: "6.1.6 | PATCH | Ensure permissions on /etc/shadow- are configured" - file: - dest: /etc/shadow- +- name: "6.1.8 | PATCH | Ensure permissions on /etc/gshadow- are configured" + ansible.builtin.file: + path: /etc/gshadow- owner: root group: root mode: 0000 when: - - rhel9cis_rule_6_1_6 + - rhel9cis_rule_6_1_8 tags: - level1-server - level1-workstation - - automated - patch - permissions - - rule_6.1.6 + - rule_6.1.10 -- name: "6.1.9 | PATCH | Ensure permissions on /etc/group- are configured" - file: - dest: /etc/group- - owner: root - group: root - mode: 0644 +- name: "6.1.9 | PATCH | Ensure no world writable files exist" + block: + - name: "6.1.9 | AUDIT | Ensure no world writable files exist | Get list of world-writable files" + ansible.builtin.shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002 + failed_when: false + changed_when: false + register: rhel_09_6_1_9_perms_results + + - name: "6.1.9 | PATCH | Ensure no world writable files exist | Adjust world-writable files if they exist (Configurable)" + ansible.builtin.file: + path: '{{ item }}' + mode: o-w + state: touch + loop: "{{ rhel_09_6_1_9_perms_results.stdout_lines }}" + when: + - rhel_09_6_1_9_perms_results.stdout_lines is defined + - rhel9cis_no_world_write_adjust when: - rhel9cis_rule_6_1_9 tags: - level1-server - level1-workstation - - automated - patch - - permissionss + - files + - permissions - rule_6.1.9 -- name: "6.1.10 | PATCH | Ensure permissions on /etc/gshadow- are configured" - file: - dest: /etc/gshadow- - owner: root - group: root - mode: 0000 +- name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist" + block: + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | Finding all unowned files or directories" + ansible.builtin.shell: find "{{ item.mount }}" -xdev -nouser + changed_when: false + failed_when: false + check_mode: false + register: rhel_09_6_1_10_audit + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" + when: item['device'].startswith('/dev') and not 'bind' in item['options'] + + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | Displaying any unowned files or directories" + ansible.builtin.debug: + msg: "Warning !! Manual intervention is required -- missing owner on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_10_audit.results }}" + when: + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.10' + when: + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + when: - rhel9cis_rule_6_1_10 tags: - level1-server - level1-workstation - - automated - - patch + - audit + - files - permissions - rule_6.1.10 -- name: "6.1.11 | PATCH | Ensure no world writable files exist" +- name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist" block: - - name: "6.1.11 | AUDIT | Ensure no world writable files exist | Get list of world-writable files" - shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002 + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | Finding all ungrouped files or directories" + ansible.builtin.shell: find "{{ item.mount }}" -xdev -nogroup + check_mode: false failed_when: false changed_when: false - register: rhel_08_6_1_11_perms_results + register: rhel_09_6_1_11_audit + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" + when: item['device'].startswith('/dev') and not 'bind' in item['options'] - - name: "6.1.11 | AUDIT | Ensure no world writable files exist | Alert no world-writable files exist" - debug: - msg: "Good news! We have not found any world-writable files on your system" + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | Displaying all ungrouped files or directories" + ansible.builtin.debug: + msg: "Warning !! Manual intervention is required -- missing group on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_11_audit.results }}" when: - - rhel_08_6_1_11_perms_results.stdout is not defined + - item.stdout_lines is defined + - item.stdout_lines | length > 0 - - name: "6.1.11 | PATCH | Ensure no world writable files exist | Adjust world-writable files if they exist (Configurable)" - file: - path: '{{ item }}' - mode: o-w - state: touch - with_items: "{{ rhel_08_6_1_11_perms_results.stdout_lines }}" + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.11' when: - - rhel_08_6_1_11_perms_results.stdout_lines is defined - - rhel9cis_no_world_write_adjust + - item.stdout_lines is defined + - item.stdout_lines | length > 0 when: - rhel9cis_rule_6_1_11 tags: - level1-server - level1-workstation - - automated - - patch + - audit - files - permissions - rule_6.1.11 -- name: "6.1.12 | AUDIT | Ensure no unowned files or directories exist" - block: - - name: "6.1.12 | AUDIT | Ensure no unowned files or directories exist | Finding all unowned files or directories" - command: find "{{ item.mount }}" -xdev -nouser - changed_when: false - failed_when: false - check_mode: false - register: rhel_08_6_1_12_audit - with_items: "{{ ansible_mounts }}" - loop_control: - label: "{{ item.mount }}" - when: item['device'].startswith('/dev') and not 'bind' in item['options'] - - - name: "6.1.12 | AUDIT | Ensure no unowned files or directories exist | Displaying any unowned files or directories" - debug: - msg: "Manual intervention is required -- missing owner on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" - with_items: "{{ rhel_08_6_1_12_audit.results }}" - when: - - item.stdout_lines is defined - - item.stdout_lines | length > 0 +- name: "6.1.12 | PATCH | Ensure sticky bit is set on all world-writable directories" + ansible.builtin.shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d -perm -0002 2>/dev/null | xargs chmod a+t + changed_when: false + failed_when: false when: - rhel9cis_rule_6_1_12 tags: - level1-server - level1-workstation - - automated - - audit - - files - - permissions - - rule_6.1.12 + - patch + - stickybits + - permissons + - rule_1.1.21 -- name: "6.1.13 | AUDIT | Ensure no ungrouped files or directories exist" +- name: "6.1.13 | AUDIT | Audit SUID executables" block: - - name: "6.1.13 | AUDIT | Ensure no ungrouped files or directories exist | Finding all ungrouped files or directories" - command: find "{{ item.mount }}" -xdev -nogroup - check_mode: false + - name: "6.1.13 | AUDIT | Audit SUID executables | Find all SUID executables" + ansible.builtin.shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 failed_when: false changed_when: false - register: rhel_08_6_1_13_audit - with_items: "{{ ansible_mounts }}" + register: rhel_09_6_1_13_perms_results + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.mount }}" - when: item['device'].startswith('/dev') and not 'bind' in item['options'] - - name: "6.1.13 | AUDIT | Ensure no ungrouped files or directories exist | Displaying all ungrouped files or directories" - debug: - msg: "Manual intervention is required -- missing group on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" - with_items: "{{ rhel_08_6_1_13_audit.results }}" + - name: "6.1.13 | AUDIT | Audit SUID executables | Alert SUID executables exist" + ansible.builtin.debug: + msg: "Warning!! Manual intervention is required -- SUID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_13_perms_results.stdout_lines }}" when: - - item.stdout_lines is defined - - item.stdout_lines | length > 0 + - rhel_09_6_1_13_perms_results.stdout is defined + + - name: "6.1.13 | AUDIT | Audit SUID executables | Alert SUID executables exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.13' + when: + - rhel_09_6_1_13_perms_results.stdout is defined when: - rhel9cis_rule_6_1_13 tags: - level1-server - level1-workstation - - automated + - manual - audit - files - - permissions - rule_6.1.13 -- name: "6.1.14 | AUDIT | Audit SUID executables" +- name: "6.1.14 | AUDIT | Audit SGID executables" block: - - name: "6.1.14 | AUDIT | Audit SUID executables | Find all SUID executables" - shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 + - name: "6.1.14 | AUDIT | Audit SGID executables | Find all SGID executables" + ansible.builtin.shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -2000 failed_when: false changed_when: false - register: rhel_08_6_1_14_perms_results - with_items: "{{ ansible_mounts }}" + register: rhel_09_6_1_14_perms_results + loop: "{{ ansible_mounts }}" loop_control: label: "{{ item.mount }}" - - name: "6.1.14 | AUDIT | Audit SUID executables | Alert no SUID executables exist" - debug: - msg: "Good news! We have not found any SUID executable files on your system" - failed_when: false - changed_when: false + - name: "6.1.14 | AUDIT | Audit SGID executables | Alert SGID executables exist" + ansible.builtin.debug: + msg: "Manual intervention is required -- SGID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_14_perms_results.stdout_lines }}" when: - - rhel_08_6_1_14_perms_results.stdout is not defined + - rhel_09_6_1_14_perms_results.stdout is defined - - name: "6.1.14 | AUDIT | Audit SUID executables | Alert SUID executables exist" - debug: - msg: "Manual intervention is required -- SUID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" - with_items: "{{ rhel_08_6_1_14_perms_results.stdout_lines }}" + - name: "6.1.14 | AUDIT | Audit SGID executables| warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.14' when: - - rhel_08_6_1_14_perms_results.stdout is defined + - rhel_09_6_1_14_perms_results.stdout is defined when: - rhel9cis_rule_6_1_14 tags: @@ -323,37 +306,41 @@ - files - rule_6.1.14 -- name: "6.1.15 | AUDIT | Audit SGID executables" +- name: "6.1.15 | AUDIT | Audit system file permissions" block: - - name: "6.1.15 | AUDIT | Audit SGID executables | Find all SGID executables" - shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -2000 - failed_when: false + - name: "6.1.15 | AUDIT | Audit system file permissions | Audit the packages" + ansible.builtin.shell: rpm -Va --nomtime --nosize --nomd5 --nolinkto changed_when: false - register: rhel_08_6_1_15_perms_results - with_items: "{{ ansible_mounts }}" - loop_control: - label: "{{ item.mount }}" - - - name: "6.1.15 | AUDIT | Audit SGID executables | Alert no SGID executables exist" - debug: - msg: "Good news! We have not found any SGID executable files on your system" failed_when: false - changed_when: false - when: - - rhel_08_6_1_15_perms_results.stdout is not defined + register: rhel9cis_6_1_15_packages_rpm - - name: "6.1.15 | AUDIT | Audit SGID executables | Alert SGID executables exist" - debug: - msg: "Manual intervention is required -- SGID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" - with_items: "{{ rhel_08_6_1_15_perms_results.stdout_lines }}" - when: - - rhel_08_6_1_15_perms_results.stdout is defined + - name: "6.1.15 | AUDIT | Audit system file permissions | Create list and warning" + block: + - name: "6.1.15 | AUDIT | Audit system file permissions | Add file discrepancy list to system" + ansible.builtin.copy: + dest: "{{ rhel9cis_rpm_audit_file }}" # noqa template-instead-of-copy + content: "{{ rhel9cis_6_1_15_packages_rpm.stdout }}" + owner: root + group: root + mode: 0640 + + - name: "6.1.15 | AUDIT | Audit system file permissions | Message out alert for package descrepancies" + ansible.builtin.debug: + msg: | + "Warning!! You have some package descrepancies issues. + The file list can be found in {{ rhel9cis_rpm_audit_file }}" + + - name: "6.1.15 | AUDIT | Audit system file permissions | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.15' + when: rhel9cis_6_1_15_packages_rpm.stdout|length > 0 when: - rhel9cis_rule_6_1_15 tags: - - level1-server - - level1-workstation + - level2-server + - level2-workstation - manual - audit - - files + - permissions - rule_6.1.15 diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index 235146e..bfd371a 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -1,511 +1,451 @@ --- -- name: "6.2.1 | PATCH | Ensure password fields are not empty" - command: passwd -l {{ item }} - changed_when: false - failed_when: false - with_items: "{{ empty_password_accounts.stdout_lines }}" +- name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords" + block: + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | discover" + ansible.builtin.shell: awk -F':' '($2 != "x" ) { print $1 " is not set to shadowed passwords "}' /etc/passwd + changed_when: false + register: shadow_passwd + + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | Output" + ansible.builtin.debug: + msg: | + - "Warning!! Below are the accounts that do not have shadowed passwords set" + - "{{ shadow_passwd.stdout_line }}" + when: shadow_passwd.stdout | length > 0 + + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | warning fact" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.1' + when: shadow_passwd.stdout | length >= 1 + when: - - empty_password_accounts.rc - rhel9cis_rule_6_2_1 tags: - level1-server - level1-workstation - - automated - patch - accounts - rule_6.2.1 - -- name: "6.2.2 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group" - block: - - name: "6.2.2 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Check /etc/passwd entries" - shell: pwck -r | grep 'no group' | awk '{ gsub("[:\47]",""); print $2}' - changed_when: false - failed_when: false - check_mode: false - register: rhel9cis_6_2_2_passwd_gid_check - - - name: "6.2.2 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Print message that all groups match between passwd and group files" - debug: - msg: "Good News! There are no users that have non-existent GUIDs (Groups)" - when: rhel9cis_6_2_2_passwd_gid_check.stdout | length == 0 - - - name: "6.2.2 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Print warning about users with invalid GIDs missing GID entries in /etc/group" - debug: - msg: "Warning!! The following users have non-existent GIDs (Groups): {{ rhel9cis_6_2_2_passwd_gid_check.stdout_lines | join (', ') }}" - when: rhel9cis_6_2_2_passwd_gid_check.stdout | length >= 1 - - - name: "6.2.2 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.2.2' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_2_2_passwd_gid_check.stdout | length >= 1 +- name: "6.2.2 | PATCH | Ensure password fields are not empty" + ansible.builtin.shell: passwd -l {{ item }} + changed_when: false + failed_when: false + loop: "{{ empty_password_accounts.stdout_lines }}" when: + - empty_password_accounts.rc - rhel9cis_rule_6_2_2 tags: - level1-server - level1-workstation - - automated - - audit + - patch - accounts - - groups - rule_6.2.2 -- name: "6.2.3 | AUDIT Ensure no duplicate UIDs exist" +- name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group" block: - - name: "6.2.3 | AUDIT | Ensure no duplicate UIDs exist | Check for duplicate UIDs" - shell: "pwck -r | awk -F: '{if ($3 in uid) print $1 ; else uid[$3]}' /etc/passwd" + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Check /etc/passwd entries" + ansible.builtin.shell: pwck -r | grep 'no group' | awk '{ gsub("[:\47]",""); print $2}' changed_when: false failed_when: false - register: rhel9cis_6_2_3_user_uid_check + check_mode: false + register: rhel9cis_6_2_3_passwd_gid_check - - name: "6.2.3 | AUDIT | Ensure no duplicate UIDs exist | Print message that no duplicate UIDs exist" - debug: - msg: "Good News! There are no duplicate UID's in the system" - when: rhel9cis_6_2_3_user_uid_check.stdout | length == 0 + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Print warning about users with invalid GIDs missing GID entries in /etc/group" + ansible.builtin.debug: + msg: "Warning!! The following users have non-existent GIDs (Groups): {{ rhel9cis_6_2_3_passwd_gid_check.stdout_lines | join (', ') }}" + when: rhel9cis_6_2_3_passwd_gid_check.stdout | length >= 1 - - name: "6.2.3 | AUDIT| Ensure no duplicate UIDs exist | Print warning about users with duplicate UIDs" - debug: - msg: "Warning!! The following users have UIDs that are duplicates: {{ rhel9cis_6_2_3_user_uid_check.stdout_lines }}" - when: rhel9cis_6_2_3_user_uid_check.stdout | length >= 1 - - - name: "6.2.3 | AUDIT| Ensure no duplicate UIDs exist | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.2.3' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_2_3_user_uid_check.stdout | length >= 1 + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.3' + when: rhel9cis_6_2_3_passwd_gid_check.stdout | length >= 1 when: - rhel9cis_rule_6_2_3 tags: - level1-server - level1-workstation - - automated - audit - accounts - - users - - rule_6.2.3 + - groups + - rule_6.2.2 -- name: "6.2.4 | AUDIT | Ensure no duplicate GIDs exist" +- name: "6.2.4 | AUDIT Ensure no duplicate UIDs exist" block: - - name: "6.2.4 | AUDIT | Ensure no duplicate GIDs exist | Check for duplicate GIDs" - shell: "pwck -r | awk -F: '{if ($3 in users) print $1 ; else users[$3]}' /etc/group" + - name: "6.2.4 | AUDIT | Ensure no duplicate UIDs exist | Check for duplicate UIDs" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($3 in uid) print $1 ; else uid[$3]}' /etc/passwd" changed_when: false failed_when: false - register: rhel9cis_6_2_4_user_user_check + register: rhel9cis_6_2_4_user_uid_check - - name: "6.2.4 | AUDIT | Ensure no duplicate GIDs exist | Print message that no duplicate GID's exist" - debug: - msg: "Good News! There are no duplicate GIDs in the system" - when: rhel9cis_6_2_4_user_user_check.stdout | length == 0 - - - name: "6.2.4 | AUDIT | Ensure no duplicate GIDs exist | Print warning about users with duplicate GIDs" - debug: - msg: "Warning!! The following groups have duplicate GIDs: {{ rhel9cis_6_2_4_user_user_check.stdout_lines }}" - when: rhel9cis_6_2_4_user_user_check.stdout | length >= 1 - - - name: "6.2.4 | AUDIT | Ensure no duplicate GIDs exist | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.2.4' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_2_4_user_user_check.stdout | length >= 1 + - name: "6.2.4 | AUDIT | Ensure no duplicate UIDs exist | Print warning about users with duplicate UIDs" + ansible.builtin.debug: + msg: "Warning!! The following users have UIDs that are duplicates: {{ rhel9cis_6_2_4_user_uid_check.stdout_lines }}" + when: rhel9cis_6_2_4_user_uid_check.stdout | length >= 1 + - name: "6.2.4 | AUDIT| Ensure no duplicate UIDs exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: rhel9cis_6_2_4_user_uid_check.stdout | length >= 1 + vars: + warn_control_id: '6.2.4' when: - rhel9cis_rule_6_2_4 tags: - level1-server - level1-workstation - - automated - audit - accounts - - groups + - users - rule_6.2.4 -- name: "6.2.5 | AUDIT | Ensure no duplicate user names exist" +- name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist" block: - - name: "6.2.5 | AUDIT | Ensure no duplicate user names exist | Check for duplicate User Names" - shell: "pwck -r | awk -F: '{if ($1 in users) print $1 ; else users[$1]}' /etc/passwd" + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | Check for duplicate GIDs" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($3 in users) print $1 ; else users[$3]}' /etc/group" changed_when: false failed_when: false - register: rhel9cis_6_2_5_user_username_check + register: rhel9cis_6_2_5_user_user_check - - name: "6.2.5 | AUDIT | Ensure no duplicate user names exist | Print message that no duplicate user names exist" - debug: - msg: "Good News! There are no duplicate user names in the system" - when: rhel9cis_6_2_5_user_username_check.stdout | length == 0 + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | Print warning about users with duplicate GIDs" + ansible.builtin.debug: + msg: "Warning!! The following groups have duplicate GIDs: {{ rhel9cis_6_2_5_user_user_check.stdout_lines }}" + when: rhel9cis_6_2_5_user_user_check.stdout | length >= 1 - - name: "6.2.5 | AUDIT | Ensure no duplicate user names exist | Print warning about users with duplicate User Names" - debug: - msg: "Warning!! The following user names are duplicates: {{ rhel9cis_6_2_5_user_username_check.stdout_lines }}" - when: rhel9cis_6_2_5_user_username_check.stdout | length >= 1 + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.5' + when: rhel9cis_6_2_5_user_user_check.stdout_lines | length >= 1 - - name: "6.2.5 | AUDIT | Ensure no duplicate user names exist | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.2.5' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_2_5_user_username_check.stdout | length >= 1 when: - rhel9cis_rule_6_2_5 tags: - level1-server - level1-workstation - - automated - audit - accounts - - users + - groups - rule_6.2.5 -- name: "6.2.6 | AUDIT |Ensure no duplicate group names exist" +- name: "6.2.6 | AUDIT | Ensure no duplicate user names exist" block: - - name: "6.2.6 | AUDIT | Ensure no duplicate group names exist | Check for duplicate group names" - shell: 'getent passwd | cut -d: -f1 | sort -n | uniq -d' + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | Check for duplicate User Names" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($1 in users) print $1 ; else users[$1]}' /etc/passwd" changed_when: false failed_when: false - check_mode: false - register: rhel9cis_6_2_6_group_group_check + register: rhel9cis_6_2_6_user_username_check - - name: "6.2.6 | AUDIT | Ensure no duplicate group names exist | Print message that no duplicate groups exist" - debug: - msg: "Good News! There are no duplicate group names in the system" - when: rhel9cis_6_2_6_group_group_check.stdout is defined + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | Print warning about users with duplicate User Names" + ansible.builtin.debug: + msg: "Warning!! The following user names are duplicates: {{ rhel9cis_6_2_6_user_username_check.stdout_lines }}" + when: rhel9cis_6_2_6_user_username_check.stdout | length >= 1 - - name: "6.2.6 | AUDIT | Ensure no duplicate group names exist | Print warning about users with duplicate group names" - debug: - msg: "Warning!! The following group names are duplicates: {{ rhel9cis_6_2_6_group_group_check.stdout_lines }}" - when: rhel9cis_6_2_6_group_group_check.stdout is not defined - - - name: "6.2.6 | AUDIT | Ensure no duplicate group names exist | warning count" - set_fact: - control_number: "{{ control_number }} + [ 'rule_6.2.6' ]" - warn_count: "{{ warn_count | int + 1 }}" - when: rhel9cis_6_2_6_group_group_check.stdout is not defined + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.6' + when: rhel9cis_6_2_6_user_username_check.stdout | length >= 1 when: - rhel9cis_rule_6_2_6 tags: - level1-server - level1-workstation - - automated - audit - accounts - - groups + - users - rule_6.2.6 -- name: "6.2.7 | PATCH | Ensure root PATH Integrity" +- name: "6.2.7 | AUDIT | Ensure no duplicate group names exist" block: - - name: "6.2.7 | AUDIT | Ensure root PATH Integrity | Determine empty value" - shell: 'echo $PATH | grep ::' + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | Check for duplicate group names" + ansible.builtin.shell: 'getent passwd | cut -d: -f1 | sort -n | uniq -d' changed_when: false - failed_when: rhel9cis_6_2_7_path_colon.rc == 0 + failed_when: false check_mode: false - register: rhel9cis_6_2_7_path_colon + register: rhel9cis_6_2_7_group_group_check - - name: "6.2.7 | AUDIT | Ensure root PATH Integrity | Determin colon end" - shell: 'echo $PATH | grep :$' - changed_when: false - failed_when: rhel9cis_6_2_7_path_colon_end.rc == 0 - check_mode: false - register: rhel9cis_6_2_7_path_colon_end + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | Print warning about users with duplicate group names" + ansible.builtin.debug: + msg: "Warning!! The following group names are duplicates: {{ rhel9cis_6_2_7_group_group_check.stdout_lines }}" + when: rhel9cis_6_2_7_group_group_check.stdout is not defined - - name: "6.2.7 | AUDIT | Ensure root PATH Integrity | Determine dot in path" - shell: "/bin/bash --login -c 'env | grep ^PATH=' | sed -e 's/PATH=//' -e 's/::/:/' -e 's/:$//' -e 's/:/\\n/g'" - changed_when: false - failed_when: '"." in rhel9cis_6_2_7_dot_in_path.stdout_lines' - check_mode: false - register: rhel9cis_6_2_7_dot_in_path - - - name: "6.2.7 | AUDIT | Ensure root PATH Integrity | Alert on empty value, colon end, and dot in path" - debug: - msg: - - "The following paths have an empty value: {{ rhel9cis_6_2_7_path_colon.stdout_lines }}" - - "The following paths have colon end: {{ rhel9cis_6_2_7_path_colon_end.stdout_lines }}" - - "The following paths have a dot in the path: {{ rhel9cis_6_2_7_dot_in_path.stdout_lines }}" - - - name: "6.2.7 | PATCH | Ensure root PATH Integrity | Determine rights and owner" - file: > - path='{{ item }}' - follow=yes - state=directory - owner=root - mode='o-w,g-w' - with_items: "{{ rhel9cis_6_2_7_dot_in_path.stdout_lines }}" + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.7' + when: rhel9cis_6_2_7_group_group_check.stdout is not defined when: - rhel9cis_rule_6_2_7 tags: - level1-server - level1-workstation - - automated - - patch - - paths + - audit + - accounts + - groups - rule_6.2.7 -- name: "6.2.8 | PATCH | Ensure root is the only UID 0 account" - command: passwd -l {{ item }} - changed_when: false - failed_when: false - with_items: "{{ rhel9cis_uid_zero_accounts_except_root.stdout_lines }}" +- name: "6.2.8 | PATCH | Ensure root PATH Integrity" + block: + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Get root paths" + ansible.builtin.shell: sudo -Hiu root env | grep '^PATH' | cut -d= -f2 + changed_when: false + register: rhel9cis_6_2_8_root_paths + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Get root paths" + ansible.builtin.shell: sudo -Hiu root env | grep '^PATH' | cut -d= -f2 | tr ":" "\n" + changed_when: false + register: rhel9cis_6_2_8_root_paths_split + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Set fact" + ansible.builtin.set_fact: + root_paths: "{{ rhel9cis_6_2_8_root_paths.stdout }}" + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for empty dirs" + ansible.builtin.shell: 'echo {{ root_paths }} | grep -q "::" && echo "roots path contains a empty directory (::)"' + changed_when: false + failed_when: root_path_empty_dir.rc not in [ 0, 1 ] + register: root_path_empty_dir + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for trailing ':'" + ansible.builtin.shell: '{{ root_paths }} | cut -d= -f2 | grep -q ":$" && echo "roots path contains a trailing (:)"' + changed_when: false + failed_when: root_path_trailing_colon.rc not in [ 0, 1 ] + register: root_path_trailing_colon + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for owner and permissions" + block: + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for owner and permissions" + ansible.builtin.stat: + path: "{{ item }}" + register: root_path_perms + loop: "{{ rhel9cis_6_2_8_root_paths_split.stdout_lines }}" + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Set permissions" + ansible.builtin.file: + path: "{{ item.stat.path }}" + state: directory + owner: root + group: root + mode: "0755" + follow: false + loop: "{{ root_path_perms.results }}" + loop_control: + label: "{{ item }}" + when: + - item.stat.exists + - item.stat.isdir + - item.stat.pw_name != 'root' or item.stat.gr_name != 'root' or item.stat.woth or item.stat.wgrp when: - - rhel9cis_uid_zero_accounts_except_root.rc - rhel9cis_rule_6_2_8 tags: - level1-server - level1-workstation - - automated - patch - - accounts - - users + - paths - rule_6.2.8 -- name: "6.2.9 | PATCH | Ensure all users' home directories exist" - block: - - name: "6.2.9 | AUDIT | Ensure all users' home directories exist" - stat: - path: "{{ item }}" - register: rhel_08_6_2_9_audit - with_items: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | map(attribute='dir') | list }}" - - - name: "6.2.9 | AUDIT | Ensure all users' home directories exist" - command: find -H {{ item.0 | quote }} -not -type l -perm /027 - check_mode: false - changed_when: rhel_08_6_2_9_patch_audit.stdout | length > 0 - register: rhel_08_6_2_9_patch_audit - when: - - ansible_check_mode - - item.1.exists - with_together: - - "{{ rhel_08_6_2_9_audit.results | map(attribute='item') | list }}" - - "{{ rhel_08_6_2_9_audit.results | map(attribute='stat') | list }}" - loop_control: - label: "{{ item.0 }}" - - - name: "6.2.9 | PATCH | Ensure all users' home directories exist" - file: - path: "{{ item.0 }}" - recurse: true - mode: a-st,g-w,o-rwx - register: rhel_08_6_2_9_patch - when: - - not ansible_check_mode - - item.1.exists - with_together: - - "{{ rhel_08_6_2_9_audit.results | map(attribute='item') | list }}" - - "{{ rhel_08_6_2_9_audit.results | map(attribute='stat') | list }}" - loop_control: - label: "{{ item.0 }}" - - # set default ACLs so the homedir has an effective umask of 0027 - - name: "6.2.9 | PATCH | Ensure all users' home directories exist" - acl: - path: "{{ item.0 }}" - default: true - state: present - recursive: true - etype: "{{ item.1.etype }}" - permissions: "{{ item.1.mode }}" - when: - - not system_is_container - with_nested: - - "{{ (ansible_check_mode | ternary(rhel_08_6_2_9_patch_audit, rhel_08_6_2_9_patch)).results | - rejectattr('skipped', 'defined') | map(attribute='item') | map('first') | list }}" - - - - etype: group - mode: rx - - etype: other - mode: '0' +- name: "6.2.9 | PATCH | Ensure root is the only UID 0 account" + ansible.builtin.shell: passwd -l {{ item }} + changed_when: false + failed_when: false + loop: "{{ rhel9cis_uid_zero_accounts_except_root.stdout_lines }}" when: + - rhel9cis_uid_zero_accounts_except_root.rc - rhel9cis_rule_6_2_9 tags: - level1-server - level1-workstation - - automated - patch + - accounts - users - rule_6.2.9 +- name: "6.2.10 | PATCH | Ensure local interactive user home directories exist" + block: + - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Create dir if absent" + ansible.builtin.file: + path: "{{ item.dir }}" + state: directory + owner: "{{ item.id }}" + group: "{{ item.gid }}" + register: rhel_09_6_2_10_home_dir + loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" + loop_control: + label: "{{ item.id }}" -- name: "6.2.10 | PATCH | Ensure users own their home directories" - file: - path: "{{ item.dir }}" - owner: "{{ item.id }}" - state: directory - with_items: "{{ rhel9cis_passwd }}" - loop_control: - label: "{{ rhel9cis_passwd_label }}" + # set default ACLs so the homedir has an effective umask of 0027 + - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Set group ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: group + permissions: rx + state: present + loop: "{{ 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" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: other + permissions: 0 + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container when: - - item.uid >= min_int_uid | int - - item.id != 'nobody' - - (item.id != 'tss' and item.dir != '/dev/null') - rhel9cis_rule_6_2_10 tags: - - skip_ansible_lint # settings found on 6_2_7 - level1-server - level1-workstation - - automated - patch - users - rule_6.2.10 -- name: "6.2.11 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" - block: - - name: "6.2.11 | AUDIT | Ensure users' home directories permissions are 750 or more restrictive" - stat: - path: "{{ item }}" - with_items: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int) | selectattr('uid', '<=', max_int_uid | 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" - command: find -H {{ item.0 | quote }} -not -type l -perm /027 - check_mode: false - changed_when: rhel_08_6_2_11_patch_audit.stdout | length > 0 - register: rhel_08_6_2_11_patch_audit - when: - - ansible_check_mode - - item.1.exists - with_together: - - "{{ rhel_08_6_2_11_audit.results | map(attribute='item') | list }}" - - "{{ rhel_08_6_2_11_audit.results | map(attribute='stat') | list }}" - loop_control: - label: "{{ item.0 }}" - - - name: "6.2.11 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" - file: - path: "{{ item.0 }}" - recurse: true - mode: a-st,g-w,o-rwx - register: rhel_08_6_2_11_patch - when: - - not ansible_check_mode - - item.1.exists - with_together: - - "{{ rhel_08_6_2_11_audit.results | map(attribute='item') | list }}" - - "{{ rhel_08_6_2_11_audit.results | map(attribute='stat') | list }}" - loop_control: - label: "{{ item.0 }}" - - # set default ACLs so the homedir has an effective umask of 0027 - - name: "6.2.11 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" - acl: - path: "{{ item.0 }}" - default: true - state: present - recursive: true - etype: "{{ item.1.etype }}" - permissions: "{{ item.1.mode }}" - when: - - not system_is_container - with_nested: - - "{{ (ansible_check_mode | ternary(rhel_08_6_2_11_patch_audit, rhel_08_6_2_11_patch)).results | - rejectattr('skipped', 'defined') | map(attribute='item') | map('first') | list }}" - - - - etype: group - mode: rx - - etype: other - mode: '0' +- name: "6.2.11 | PATCH | Ensure local interactive users own their home directories" + ansible.builtin.file: + path: "{{ item.dir }}" + owner: "{{ item.id }}" + state: directory + loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" + loop_control: + label: "{{ item.id }}" when: + - item.uid >= min_int_uid | int + - item.id != 'nobody' + - (item.id != 'tss' and item.dir != '/dev/null') + - item.shell != '/sbin/nologin' - rhel9cis_rule_6_2_11 tags: - level1-server - level1-workstation - - automated - patch - users - - permissions - rule_6.2.11 -- name: "6.2.12 | PATCH | Ensure users' dot files are not group or world-writable" +- name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive" block: - - name: "6.2.12 | AUDIT | Ensure users' dot files are not group or world-writable | Check for files" - shell: find /home/ -maxdepth 2 -name "\.*" -perm /g+w,o+w - changed_when: false - failed_when: false - register: rhel9cis_6_2_12_audit + - name: "6.2.12 | AUDIT | Ensure local interactive user home directories are mode 750 or more restrictive | get stat" + ansible.builtin.stat: + path: "{{ item }}" + register: rhel_09_6_2_12_home_dir_perms + loop: "{{ interactive_users_home.stdout_lines }}" - - name: "6.2.12 | AUDIT | Ensure users' dot files are not group or world-writable | Alert on files found" - debug: - msg: "Good news! We have not found any group or world-writable dot files on your sytem" + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | amend if needed" + ansible.builtin.file: + path: "{{ item.stat.path }}" + state: directory + mode: "0750" + loop: "{{ rhel_09_6_2_12_home_dir_perms.results }}" + loop_control: + label: "{{ item }}" when: - - rhel9cis_6_2_12_audit.stdout is not defined + - item.stat.mode > '0750' - - name: "6.2.12 | PATCH | Ensure users' dot files are not group or world-writable | Changes files if configured" - file: - path: '{{ item }}' - mode: go-w - with_items: "{{ rhel9cis_6_2_12_audit.stdout_lines }}" - when: - - rhel9cis_6_2_12_audit.stdout is defined - - rhel9cis_dotperm_ansiblemanaged + # set default ACLs so the homedir has an effective umask of 0027 + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | Set group ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: group + permissions: rx + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container + + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | Set other ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: other + permissions: 0 + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container when: - rhel9cis_rule_6_2_12 tags: - level1-server - level1-workstation - - automated - patch - users - permissions - rule_6.2.12 -- name: "6.2.13 | PATCH | Ensure users' .netrc Files are not group or world accessible" - command: /bin/true - changed_when: false - failed_when: false +- name: "6.2.13 | PATCH | Ensure no local interactive user has .netrc files" + ansible.builtin.file: + path: "{{ item }}/.netrc" + state: absent + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_13 tags: - level1-server - level1-workstation - - automated - patch - users - permissions - - notimplemented - rule_6.2.13 -- name: "6.2.14 | PATCH | Ensure no users have .forward files" - file: - path: "~{{ item }}/.forward" +- name: "6.2.14 | PATCH | Ensure no local interactive user has .forward files" + ansible.builtin.file: + path: "{{ item }}/.forward" state: absent - with_items: - - "{{ users.stdout_lines }}" + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_14 tags: - level1-server - level1-workstation - - automated - patch - users - files - rule_6.2.14 -- name: "6.2.15 | PATCH | Ensure no users have .netrc files" - file: - path: "~{{ item }}/.netrc" +- name: "6.2.15 | PATCH | Ensure no local interactive user has .rhosts files" + ansible.builtin.file: + path: "~{{ item }}/.rhosts" state: absent - with_items: - - "{{ users.stdout_lines }}" + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_15 tags: - level1-server - level1-workstation - - automated - patch - users - files - rule_6.2.15 -- name: "6.2.16 | PATCH | Ensure no users have .rhosts files" - file: - path: "~{{ item }}/.rhosts" - state: absent - with_items: "{{ users.stdout_lines }}" +- name: "6.2.16 | PATCH | Ensure local interactive user dot files are not group or world writable" + block: + - name: "6.2.16 | AUDIT | Ensure local interactive user dot files are not group or world writable | Check for files" + ansible.builtin.find: + path: /home + depth: 3 + patterns: ".*" + hidden: true + recurse: true + file_type: file + register: user_dot_files + + - name: "6.2.16 | AUDIT | Ensure local interactive user dot files are not group or world writable | update permissions" + ansible.builtin.file: + path: "{{ item.path }}" + mode: go-w + loop: "{{ user_dot_files.files }}" + loop_control: + label: "{{ item.path }}" + when: - rhel9cis_rule_6_2_16 tags: - level1-server - level1-workstation - - automated - patch - users - - files + - permissions - rule_6.2.16 diff --git a/tasks/section_6/main.yml b/tasks/section_6/main.yml index b6acabf..35328e5 100644 --- a/tasks/section_6/main.yml +++ b/tasks/section_6/main.yml @@ -1,7 +1,7 @@ --- - name: "SECTION | 6.1 | System File Permissions" - import_tasks: cis_6.1.x.yml + ansible.builtin.import_tasks: cis_6.1.x.yml - name: "SECTION | 6.2 | User and Group Settings" - import_tasks: cis_6.2.x.yml + ansible.builtin.import_tasks: cis_6.2.x.yml diff --git a/tasks/warning_facts.yml b/tasks/warning_facts.yml new file mode 100644 index 0000000..6e80487 --- /dev/null +++ b/tasks/warning_facts.yml @@ -0,0 +1,20 @@ +--- + +# This task is used to create variables used in giving a warning summary for manual tasks +# that need attention +# +# The warn_control_list and warn_count vars start life in vars/main.yml but get updated +# as the tasks that have a warning complete +# +# Those two variables are used in the tasks/main.yml to display a list of warnings +# +# warn_control_id is set within the task itself and has the control ID as the value +# +# warn_control_list is the main variable to be used and is a list made up of the warn_control_id’s +# +# warn_count the main variable for the number of warnings and each time a warn_control_id is added +# the count increases by a value of 1 +- name: "{{ warn_control_id }} | AUDIT | Set fact for manual task warning." + ansible.builtin.set_fact: + warn_control_list: "{{ warn_control_list }} [{{ warn_control_id }}]" + warn_count: "{{ warn_count | int + 1 }}" diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 index 1431ed4..fde2a67 100644 --- a/templates/ansible_vars_goss.yml.j2 +++ b/templates/ansible_vars_goss.yml.j2 @@ -3,7 +3,7 @@ ## metadata for benchmark ## metadata for Audit benchmark -benchmark_version: '2.0.0' +benchmark_version: '1.0.0' # Set if genuine RHEL (subscription manager check) not for derivatives e.g. CentOS # If run via script this is discovered and set @@ -44,7 +44,6 @@ rhel9cis_set_boot_pass: {{ rhel9cis_set_boot_pass }} # 1.1.1 Disable unused filesystems rhel9cis_rule_1_1_1_1: {{ rhel9cis_rule_1_1_1_1 }} rhel9cis_rule_1_1_1_2: {{ rhel9cis_rule_1_1_1_2 }} -rhel9cis_rule_1_1_1_3: {{ rhel9cis_rule_1_1_1_3 }} # 1.1.2 Configure /tmp rhel9cis_rule_1_1_2_1: {{ rhel9cis_rule_1_1_2_1 }} rhel9cis_rule_1_1_2_2: {{ rhel9cis_rule_1_1_2_2 }} @@ -54,7 +53,6 @@ rhel9cis_rule_1_1_2_4: {{ rhel9cis_rule_1_1_2_4 }} rhel9cis_rule_1_1_3_1: {{ rhel9cis_rule_1_1_3_1 }} rhel9cis_rule_1_1_3_2: {{ rhel9cis_rule_1_1_3_2 }} rhel9cis_rule_1_1_3_3: {{ rhel9cis_rule_1_1_3_3 }} -rhel9cis_rule_1_1_3_4: {{ rhel9cis_rule_1_1_3_4 }} # 1.1.4 Configure /var/tmp rhel9cis_rule_1_1_4_1: {{ rhel9cis_rule_1_1_4_1 }} rhel9cis_rule_1_1_4_2: {{ rhel9cis_rule_1_1_4_2 }} @@ -74,28 +72,25 @@ rhel9cis_rule_1_1_6_4: {{ rhel9cis_rule_1_1_6_4 }} rhel9cis_rule_1_1_7_1: {{ rhel9cis_rule_1_1_7_1 }} rhel9cis_rule_1_1_7_2: {{ rhel9cis_rule_1_1_7_2 }} rhel9cis_rule_1_1_7_3: {{ rhel9cis_rule_1_1_7_3 }} -rhel9cis_rule_1_1_7_4: {{ rhel9cis_rule_1_1_7_4 }} -rhel9cis_rule_1_1_7_5: {{ rhel9cis_rule_1_1_7_5 }} # 1.1.8 Configure /dev/shm rhel9cis_rule_1_1_8_1: {{ rhel9cis_rule_1_1_8_1 }} rhel9cis_rule_1_1_8_2: {{ rhel9cis_rule_1_1_8_2 }} rhel9cis_rule_1_1_8_3: {{ rhel9cis_rule_1_1_8_3 }} -# 1.9 autofs +rhel9cis_rule_1_1_8_4: {{ rhel9cis_rule_1_1_8_4 }} +# 1.9 usb-storage rhel9cis_rule_1_1_9: {{ rhel9cis_rule_1_1_9 }} -# 1.10 usb-storage -rhel9cis_rule_1_1_10: {{ rhel9cis_rule_1_1_10 }} # 1.2 Configure Software Updates -rhel9cis_rule_1_2_1: {% if ansible_distribution == "RedHat" %}True{% else %}False{% endif %} # Only run if Redhat and Subscribed +rhel9cis_rule_1_2_1: {{ rhel9cis_rule_1_2_1 }} rhel9cis_rule_1_2_2: {{ rhel9cis_rule_1_2_2 }} rhel9cis_rule_1_2_3: {{ rhel9cis_rule_1_2_3 }} rhel9cis_rule_1_2_4: {{ rhel9cis_rule_1_2_4 }} # 1.3 Filesystem Integrity Checking rhel9cis_rule_1_3_1: {{ rhel9cis_rule_1_3_1 }} rhel9cis_rule_1_3_2: {{ rhel9cis_rule_1_3_2 }} +rhel9cis_rule_1_3_3: {{ rhel9cis_rule_1_3_3 }} # 1.4 Secure Boot Settings rhel9cis_rule_1_4_1: {{ rhel9cis_rule_1_4_1 }} rhel9cis_rule_1_4_2: {{ rhel9cis_rule_1_4_2 }} -rhel9cis_rule_1_4_3: {{ rhel9cis_rule_1_4_3 }} # 1.5 Additional Process Hardening rhel9cis_rule_1_5_1: {{ rhel9cis_rule_1_5_1 }} rhel9cis_rule_1_5_2: {{ rhel9cis_rule_1_5_2 }} @@ -108,6 +103,7 @@ rhel9cis_rule_1_6_1_4: {{ rhel9cis_rule_1_6_1_4 }} rhel9cis_rule_1_6_1_5: {{ rhel9cis_rule_1_6_1_5 }} rhel9cis_rule_1_6_1_6: {{ rhel9cis_rule_1_6_1_6 }} rhel9cis_rule_1_6_1_7: {{ rhel9cis_rule_1_6_1_7 }} +rhel9cis_rule_1_6_1_8: {{ rhel9cis_rule_1_6_1_8 }} # 1.7 Command Line Warning Banners rhel9cis_rule_1_7_1: {{ rhel9cis_rule_1_7_1 }} rhel9cis_rule_1_7_2: {{ rhel9cis_rule_1_7_2 }} @@ -121,6 +117,11 @@ rhel9cis_rule_1_8_2: {{ rhel9cis_rule_1_8_2 }} rhel9cis_rule_1_8_3: {{ rhel9cis_rule_1_8_3 }} rhel9cis_rule_1_8_4: {{ rhel9cis_rule_1_8_4 }} rhel9cis_rule_1_8_5: {{ rhel9cis_rule_1_8_5 }} +rhel9cis_rule_1_8_6: {{ rhel9cis_rule_1_8_6 }} +rhel9cis_rule_1_8_7: {{ rhel9cis_rule_1_8_7 }} +rhel9cis_rule_1_8_8: {{ rhel9cis_rule_1_8_8 }} +rhel9cis_rule_1_8_9: {{ rhel9cis_rule_1_8_9 }} +rhel9cis_rule_1_8_10: {{ rhel9cis_rule_1_8_10 }} # 1.9 Ensure updates, patches, and additional security software are installed rhel9cis_rule_1_9: {{ rhel9cis_rule_1_9 }} # Ensure system-wide crypto policy is not legacy @@ -151,24 +152,19 @@ rhel9cis_rule_2_2_15: {{ rhel9cis_rule_2_2_15 }} rhel9cis_rule_2_2_16: {{ rhel9cis_rule_2_2_16 }} rhel9cis_rule_2_2_17: {{ rhel9cis_rule_2_2_17 }} rhel9cis_rule_2_2_18: {{ rhel9cis_rule_2_2_18 }} -rhel9cis_rule_2_2_19: {{ rhel9cis_rule_2_2_19 }} -rhel9cis_rule_2_2_20: {{ rhel9cis_rule_2_2_20 }} # 2.3 service clients rhel9cis_rule_2_3_1: {{ rhel9cis_rule_2_3_1 }} rhel9cis_rule_2_3_2: {{ rhel9cis_rule_2_3_2 }} rhel9cis_rule_2_3_3: {{ rhel9cis_rule_2_3_3 }} rhel9cis_rule_2_3_4: {{ rhel9cis_rule_2_3_4 }} -rhel9cis_rule_2_3_5: {{ rhel9cis_rule_2_3_5 }} -rhel9cis_rule_2_3_6: {{ rhel9cis_rule_2_3_6 }} -rhel9cis_rule_2_4: true # todo +rhel9cis_rule_2_4: true # Section 3 rules # 3.1 Disable unused network protocols and devices rhel9cis_rule_3_1_1: {{ rhel9cis_rule_3_1_1 }} rhel9cis_rule_3_1_2: {{ rhel9cis_rule_3_1_2 }} rhel9cis_rule_3_1_3: {{ rhel9cis_rule_3_1_3 }} -rhel9cis_rule_3_1_4: {{ rhel9cis_rule_3_1_4 }} # 3.2 Network Parameters (Host Only) rhel9cis_rule_3_2_1: {{ rhel9cis_rule_3_2_1 }} rhel9cis_rule_3_2_2: {{ rhel9cis_rule_3_2_2 }} @@ -185,11 +181,7 @@ rhel9cis_rule_3_3_9: {{ rhel9cis_rule_3_3_9 }} # 3.4.1 Configure firewalld rhel9cis_rule_3_4_1_1: {{ rhel9cis_rule_3_4_1_1 }} rhel9cis_rule_3_4_1_2: {{ rhel9cis_rule_3_4_1_2 }} -rhel9cis_rule_3_4_1_3: {{ rhel9cis_rule_3_4_1_3 }} -rhel9cis_rule_3_4_1_4: {{ rhel9cis_rule_3_4_1_4 }} -rhel9cis_rule_3_4_1_5: {{ rhel9cis_rule_3_4_1_5 }} -rhel9cis_rule_3_4_1_6: {{ rhel9cis_rule_3_4_1_6 }} -rhel9cis_rule_3_4_1_7: {{ rhel9cis_rule_3_4_1_7 }} + # 3.4.1 Configure nftables rhel9cis_rule_3_4_2_1: {{ rhel9cis_rule_3_4_2_1 }} rhel9cis_rule_3_4_2_2: {{ rhel9cis_rule_3_4_2_2 }} @@ -198,10 +190,7 @@ rhel9cis_rule_3_4_2_4: {{ rhel9cis_rule_3_4_2_4 }} rhel9cis_rule_3_4_2_5: {{ rhel9cis_rule_3_4_2_5 }} rhel9cis_rule_3_4_2_6: {{ rhel9cis_rule_3_4_2_6 }} rhel9cis_rule_3_4_2_7: {{ rhel9cis_rule_3_4_2_7 }} -rhel9cis_rule_3_4_2_8: {{ rhel9cis_rule_3_4_2_8 }} -rhel9cis_rule_3_4_2_9: {{ rhel9cis_rule_3_4_2_9 }} -rhel9cis_rule_3_4_2_10: {{ rhel9cis_rule_3_4_2_10 }} -rhel9cis_rule_3_4_2_11: {{ rhel9cis_rule_3_4_2_11 }} + # Section 4 rules # 4.1 Configure System Accounting @@ -238,6 +227,18 @@ rhel9cis_rule_4_1_3_19: {{ rhel9cis_rule_4_1_3_19 }} rhel9cis_rule_4_1_3_20: {{ rhel9cis_rule_4_1_3_20 }} rhel9cis_rule_4_1_3_21: {{ rhel9cis_rule_4_1_3_21 }} +# 4.1.4 Configure auditd file Access +rhel9cis_rule_4_1_4_1: {{ rhel9cis_rule_4_1_4_1 }} +rhel9cis_rule_4_1_4_2: {{ rhel9cis_rule_4_1_4_2 }} +rhel9cis_rule_4_1_4_3: {{ rhel9cis_rule_4_1_4_3 }} +rhel9cis_rule_4_1_4_4: {{ rhel9cis_rule_4_1_4_4 }} +rhel9cis_rule_4_1_4_5: {{ rhel9cis_rule_4_1_4_5 }} +rhel9cis_rule_4_1_4_6: {{ rhel9cis_rule_4_1_4_6 }} +rhel9cis_rule_4_1_4_7: {{ rhel9cis_rule_4_1_4_7 }} +rhel9cis_rule_4_1_4_8: {{ rhel9cis_rule_4_1_4_8 }} +rhel9cis_rule_4_1_4_9: {{ rhel9cis_rule_4_1_4_9 }} +rhel9cis_rule_4_1_4_10: {{ rhel9cis_rule_4_1_4_10 }} + # 4.2.1 Configure rsyslog rhel9cis_rule_4_2_1_1: {{ rhel9cis_rule_4_2_1_1 }} rhel9cis_rule_4_2_1_2: {{ rhel9cis_rule_4_2_1_2 }} @@ -262,9 +263,8 @@ rhel9cis_rule_4_2_2_7: {{ rhel9cis_rule_4_2_2_7 }} rhel9cis_rule_4_2_3: {{ rhel9cis_rule_4_2_3 }} # 4.3 Logrotate -rhel9cis_rule_4_3_1: {{ rhel9cis_rule_4_3_1 }} -rhel9cis_rule_4_3_2: {{ rhel9cis_rule_4_3_2 }} -rhel9cis_rule_4_3_3: {{ rhel9cis_rule_4_3_3 }} +rhel9cis_rule_4_3: {{ rhel9cis_rule_4_3 }} + # Section 5 # Authentication and Authorization @@ -331,6 +331,7 @@ rhel9cis_rule_5_6_2: {{ rhel9cis_rule_5_6_2 }} rhel9cis_rule_5_6_3: {{ rhel9cis_rule_5_6_3 }} rhel9cis_rule_5_6_4: {{ rhel9cis_rule_5_6_4 }} rhel9cis_rule_5_6_5: {{ rhel9cis_rule_5_6_5 }} +rhel9cis_rule_5_6_6: {{ rhel9cis_rule_5_6_6 }} # Section 6 # 6 System Maintenance @@ -386,17 +387,21 @@ rhel9cis_warning_banner: {{ rhel9cis_warning_banner }} # aide setup via - cron, timer rhel9_aide_scan: cron +# 1.8 Gnome Desktop +rhel9cis_dconf_db_name: {{ rhel9cis_dconf_db_name }} +rhel9cis_screensaver_idle_delay: {{ rhel9cis_screensaver_idle_delay }} # Set max value for idle-delay in seconds (between 1 and 900) +rhel9cis_screensaver_lock_delay: {{ rhel9cis_screensaver_lock_delay }} # Set max value for lock-delay in seconds (between 0 and 5) + # Section 2 ## 2.2 Special Purposes # Set to 'true' if X Windows is needed in your environment rhel9cis_xwindows_required: false ### Service configuration booleans set true to keep service -rhel9cis_xinetd_server: {{ rhel9cis_xinetd_server }} rhel9cis_avahi_server: {{ rhel9cis_avahi_server }} rhel9cis_cups_server: {{ rhel9cis_cups_server }} rhel9cis_dhcp_server: {{ rhel9cis_dhcp_server }} rhel9cis_dns_server: {{ rhel9cis_dns_server }} -rhel9cis_ftp_server: {{ rhel9cis_ftp_server }} +rhel9cis_dnsmasq_server: {{ rhel9cis_dnsmasq_server }} rhel9cis_vsftpd_server: {{ rhel9cis_vsftpd_server }} rhel9cis_tftp_server: {{ rhel9cis_tftp_server }} rhel9cis_httpd_server: {{ rhel9cis_httpd_server }} @@ -406,7 +411,6 @@ rhel9cis_imap_server: {{ rhel9cis_imap_server }} rhel9cis_samba_server: {{ rhel9cis_samba_server }} rhel9cis_squid_server: {{ rhel9cis_squid_server }} rhel9cis_snmp_server: {{ rhel9cis_snmp_server }} -rhel9cis_nis_server: {{ rhel9cis_nis_server }} rhel9cis_telnet_server: {{ rhel9cis_telnet_server }} rhel9cis_is_mail_server: {{ rhel9cis_is_mail_server }} @@ -421,12 +425,10 @@ rhel9cis_use_rsync_server: {{ rhel9cis_use_rsync_server }} rhel9cis_use_rsync_service: {{ rhel9cis_use_rsync_service }} #### 2.3 Service clients -rhel9cis_ypbind_required: {{ rhel9cis_ypbind_required }} -rhel9cis_rsh_required: {{ rhel9cis_rsh_required }} -rhel9cis_talk_required: {{ rhel9cis_talk_required }} rhel9cis_telnet_required: {{ rhel9cis_telnet_required }} rhel9cis_openldap_clients_required: {{ rhel9cis_openldap_clients_required }} rhel9cis_tftp_client: {{ rhel9cis_tftp_client }} +rhel9cis_ftp_client: {{ rhel9cis_ftp_client }} # Section 3 @@ -441,9 +443,9 @@ rhel9cis_is_router: {{ rhel9cis_is_router }} rhel9cis_firewall: {{ rhel9cis_firewall }} ##### firewalld rhel9cis_default_zone: {{ rhel9cis_default_zone }} -rhel9cis_firewalld_nftables_state: {{ rhel9cis_firewalld_nftables_state }} # Note if absent removes the firewalld pkg dependancy + #### nftables -rhel9cis_nftables_firewalld_state: {{ rhel9cis_nftables_firewalld_state }} + rhel9cis_nft_tables_autonewtable: {{ rhel9cis_nft_tables_autonewtable }} rhel9cis_nft_tables_tablename: {{ rhel9cis_nft_tables_tablename }} rhel9cis_nft_tables_autochaincreate: {{ rhel9cis_nft_tables_autochaincreate }} @@ -451,8 +453,18 @@ rhel9cis_nft_tables_autochaincreate: {{ rhel9cis_nft_tables_autochaincreate }} # Section 4 +## Set if host is a logserver +rhel9cis_remote_log_server: {{ rhel9cis_remote_log_server }} + +# Remote logserver settings +rhel9cis_remote_log_host: {{ rhel9cis_remote_log_host }} +rhel9cis_remote_log_port: {{ rhel9cis_remote_log_port }} +rhel9cis_remote_log_protocol: {{ rhel9cis_remote_log_protocol }} +rhel9cis_remote_log_retrycount: {{ rhel9cis_remote_log_retrycount }} +rhel9cis_remote_log_queuesize: {{ rhel9cis_remote_log_queuesize }} + ## syslog -rhel9cis_syslog: {{ rhel9cis_preferred_log_capture }} +rhel9cis_syslog: {{ rhel9cis_syslog }} # Section 5 ## 5.2.4 Note the following to understand precedence and layout diff --git a/templates/etc/aide.conf.d/crypt_audit_procs.conf.j2 b/templates/etc/aide.conf.d/crypt_audit_procs.conf.j2 new file mode 100644 index 0000000..fb12b29 --- /dev/null +++ b/templates/etc/aide.conf.d/crypt_audit_procs.conf.j2 @@ -0,0 +1,7 @@ +# Audit Tools +/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 diff --git a/templates/etc/dconf/db/00-automount_lock.j2 b/templates/etc/dconf/db/00-automount_lock.j2 new file mode 100644 index 0000000..3534474 --- /dev/null +++ b/templates/etc/dconf/db/00-automount_lock.j2 @@ -0,0 +1,9 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +# Lock desktop media-handling automount setting +/org/gnome/desktop/media-handling/automount + +# Lock desktop media-handling automount-open +/org/gnome/desktop/media-handling/automount-open diff --git a/templates/etc/dconf/db/00-autorun_lock.j2 b/templates/etc/dconf/db/00-autorun_lock.j2 new file mode 100644 index 0000000..04e23a5 --- /dev/null +++ b/templates/etc/dconf/db/00-autorun_lock.j2 @@ -0,0 +1,6 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +# Lock desktop media-handling settings +/org/gnome/desktop/media-handling/autorun-never diff --git a/templates/etc/dconf/db/00-media-automount.j2 b/templates/etc/dconf/db/00-media-automount.j2 new file mode 100644 index 0000000..227498e --- /dev/null +++ b/templates/etc/dconf/db/00-media-automount.j2 @@ -0,0 +1,7 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +[org/gnome/desktop/media-handling] +automount=false +automount-open=false diff --git a/templates/etc/dconf/db/00-media-autorun.j2 b/templates/etc/dconf/db/00-media-autorun.j2 new file mode 100644 index 0000000..a8c297f --- /dev/null +++ b/templates/etc/dconf/db/00-media-autorun.j2 @@ -0,0 +1,6 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +[org/gnome/desktop/media-handling] +autorun-never=true diff --git a/templates/etc/dconf/db/00-screensaver.j2 b/templates/etc/dconf/db/00-screensaver.j2 new file mode 100644 index 0000000..5aa21c1 --- /dev/null +++ b/templates/etc/dconf/db/00-screensaver.j2 @@ -0,0 +1,17 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + + +# Specify the dconf path +[org/gnome/desktop/session] + +# Number of seconds of inactivity before the screen goes blank +# Set to 0 seconds if you want to deactivate the screensaver. +idle-delay=uint32 {{ rhel9cis_screensaver_idle_delay }} + +# Specify the dconf path +[org/gnome/desktop/screensaver] + +# Number of seconds after the screen is blank before locking the screen +lock-delay=uint32 {{ rhel9cis_screensaver_lock_delay }} diff --git a/templates/etc/dconf/db/00-screensaver_lock.j2 b/templates/etc/dconf/db/00-screensaver_lock.j2 new file mode 100644 index 0000000..5d5869f --- /dev/null +++ b/templates/etc/dconf/db/00-screensaver_lock.j2 @@ -0,0 +1,9 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +# Lock desktop screensaver idle-delay setting +/org/gnome/desktop/session/idle-delay + +# Lock desktop screensaver lock-delay setting +/org/gnome/desktop/screensaver/lock-delay diff --git a/templates/etc/dconf/db/gdm.d/01-banner-message.j2 b/templates/etc/dconf/db/gdm.d/01-banner-message.j2 new file mode 100644 index 0000000..f83a74a --- /dev/null +++ b/templates/etc/dconf/db/gdm.d/01-banner-message.j2 @@ -0,0 +1,7 @@ +## Ansible controlled file +# Added as part of CIS +# provided by MindPointGroup LLC + +[org/gnome/login-screen] +banner-message-enable=true +banner-message-text="{{ rhel9cis_warning_banner }}" diff --git a/vars/main.yml b/vars/main.yml index 2ba64a1..9815eea 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,12 +1,12 @@ --- # vars file for RHEL9-CIS -min_ansible_version: 2.9.4 +min_ansible_version: 2.10.1 rhel9cis_allowed_crypto_policies: - 'DEFAULT' - 'FUTURE' - 'FIPS' # Used to control warning summary -control_number: "" +warn_control_list: "" warn_count: 0