From a54b5216eb256900b144c01f7604d7ed0fed133b Mon Sep 17 00:00:00 2001 From: Mark Bolwell Date: Fri, 7 Jan 2022 09:06:18 +0000 Subject: [PATCH] Initial Signed-off-by: Mark Bolwell --- .ansible-lint | 11 + .gitattributes | 6 + .gitignore | 46 ++ .yamllint | 23 + CONTRIBUTING.rst | 67 +++ Changelog.md | 93 ++++ LICENSE | 21 + README.md | 121 +++++ ansible.cfg | 24 + defaults/main.yml | 622 ++++++++++++++++++++++ files/etc/systemd/system/tmp.mount | 25 + group_vars/docker | 28 + group_vars/vagrant | 28 + handlers/main.yml | 134 +++++ local.yml | 12 + meta/main.yml | 18 + site.yml | 11 + tasks/LE_audit_setup.yml | 22 + tasks/audit_homedirperms.yml | 46 ++ tasks/check_prereqs.yml | 36 ++ tasks/main.yml | 111 ++++ tasks/parse_etc_password.yml | 32 ++ tasks/post.yml | 56 ++ tasks/post_remediation_audit.yml | 43 ++ tasks/pre_remediation_audit.yml | 118 ++++ tasks/prelim.yml | 119 +++++ tasks/section_1/cis_1.1.1.x.yml | 102 ++++ tasks/section_1/cis_1.1.x.yml | 363 +++++++++++++ tasks/section_1/cis_1.10.yml | 15 + tasks/section_1/cis_1.11.yml | 15 + tasks/section_1/cis_1.2.x.yml | 100 ++++ tasks/section_1/cis_1.3.x.yml | 44 ++ tasks/section_1/cis_1.4.x.yml | 50 ++ tasks/section_1/cis_1.5.x.yml | 76 +++ tasks/section_1/cis_1.6.x.yml | 54 ++ tasks/section_1/cis_1.7.1.x.yml | 115 ++++ tasks/section_1/cis_1.8.1.x.yml | 96 ++++ tasks/section_1/cis_1.8.2.yml | 27 + tasks/section_1/cis_1.9.yml | 15 + tasks/section_1/main.yml | 42 ++ tasks/section_2/cis_2.1.1.yml | 14 + tasks/section_2/cis_2.2.1.x.yml | 42 ++ tasks/section_2/cis_2.2.x.yml | 288 ++++++++++ tasks/section_2/cis_2.3.x.yml | 43 ++ tasks/section_2/main.yml | 13 + tasks/section_3/cis_3.1.x.yml | 43 ++ tasks/section_3/cis_3.2.x.yml | 155 ++++++ tasks/section_3/cis_3.3.x.yml | 61 +++ tasks/section_3/cis_3.4.1.1.yml | 13 + tasks/section_3/cis_3.4.2.x.yml | 102 ++++ tasks/section_3/cis_3.4.3.x.yml | 264 +++++++++ tasks/section_3/cis_3.4.4.1.x.yml | 144 +++++ tasks/section_3/cis_3.4.4.2.x.yml | 136 +++++ tasks/section_3/cis_3.5.yml | 32 ++ tasks/section_3/cis_3.6.yml | 17 + tasks/section_3/main.yml | 41 ++ tasks/section_4/cis_4.1.1.x.yml | 103 ++++ tasks/section_4/cis_4.1.2.x.yml | 53 ++ tasks/section_4/cis_4.1.x.yml | 205 +++++++ tasks/section_4/cis_4.2.1.x.yml | 177 ++++++ tasks/section_4/cis_4.2.2.x.yml | 43 ++ tasks/section_4/cis_4.2.3.yml | 13 + tasks/section_4/cis_4.3.yml | 24 + tasks/section_4/main.yml | 23 + tasks/section_5/cis_5.1.x.yml | 147 +++++ tasks/section_5/cis_5.2.x.yml | 345 ++++++++++++ tasks/section_5/cis_5.3.x.yml | 90 ++++ tasks/section_5/cis_5.4.x.yml | 131 +++++ tasks/section_5/cis_5.5.1.x.yml | 117 ++++ tasks/section_5/cis_5.5.x.yml | 96 ++++ tasks/section_5/cis_5.6.yml | 35 ++ tasks/section_5/cis_5.7.yml | 22 + tasks/section_5/main.yml | 27 + tasks/section_6/cis_6.1.x.yml | 294 ++++++++++ tasks/section_6/cis_6.2.x.yml | 519 ++++++++++++++++++ tasks/section_6/main.yml | 7 + templates/ansible_vars_goss.yml.j2 | 474 +++++++++++++++++ templates/audit/99_auditd.rules.j2 | 79 +++ templates/chrony.conf.j2 | 93 ++++ templates/etc/99-sysctl.conf.j2 | 75 +++ templates/etc/issue.j2 | 1 + templates/etc/issue.net.j2 | 1 + templates/etc/motd.j2 | 1 + templates/etc/systemd/system/tmp.mount.j2 | 28 + templates/hosts.allow.j2 | 11 + templates/ntp.conf.j2 | 59 ++ vars/main.yml | 5 + 87 files changed, 7693 insertions(+) create mode 100644 .ansible-lint create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 CONTRIBUTING.rst create mode 100644 Changelog.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 ansible.cfg create mode 100644 defaults/main.yml create mode 100644 files/etc/systemd/system/tmp.mount create mode 100644 group_vars/docker create mode 100644 group_vars/vagrant create mode 100644 handlers/main.yml create mode 100644 local.yml create mode 100644 meta/main.yml create mode 100644 site.yml create mode 100644 tasks/LE_audit_setup.yml create mode 100644 tasks/audit_homedirperms.yml create mode 100644 tasks/check_prereqs.yml create mode 100644 tasks/main.yml create mode 100644 tasks/parse_etc_password.yml create mode 100644 tasks/post.yml create mode 100644 tasks/post_remediation_audit.yml create mode 100644 tasks/pre_remediation_audit.yml create mode 100644 tasks/prelim.yml create mode 100644 tasks/section_1/cis_1.1.1.x.yml create mode 100644 tasks/section_1/cis_1.1.x.yml create mode 100644 tasks/section_1/cis_1.10.yml create mode 100644 tasks/section_1/cis_1.11.yml create mode 100644 tasks/section_1/cis_1.2.x.yml create mode 100644 tasks/section_1/cis_1.3.x.yml create mode 100644 tasks/section_1/cis_1.4.x.yml create mode 100644 tasks/section_1/cis_1.5.x.yml create mode 100644 tasks/section_1/cis_1.6.x.yml create mode 100644 tasks/section_1/cis_1.7.1.x.yml create mode 100644 tasks/section_1/cis_1.8.1.x.yml create mode 100644 tasks/section_1/cis_1.8.2.yml create mode 100644 tasks/section_1/cis_1.9.yml create mode 100644 tasks/section_1/main.yml create mode 100644 tasks/section_2/cis_2.1.1.yml create mode 100644 tasks/section_2/cis_2.2.1.x.yml create mode 100644 tasks/section_2/cis_2.2.x.yml create mode 100644 tasks/section_2/cis_2.3.x.yml create mode 100644 tasks/section_2/main.yml create mode 100644 tasks/section_3/cis_3.1.x.yml create mode 100644 tasks/section_3/cis_3.2.x.yml create mode 100644 tasks/section_3/cis_3.3.x.yml create mode 100644 tasks/section_3/cis_3.4.1.1.yml create mode 100644 tasks/section_3/cis_3.4.2.x.yml create mode 100644 tasks/section_3/cis_3.4.3.x.yml create mode 100644 tasks/section_3/cis_3.4.4.1.x.yml create mode 100644 tasks/section_3/cis_3.4.4.2.x.yml create mode 100644 tasks/section_3/cis_3.5.yml create mode 100644 tasks/section_3/cis_3.6.yml create mode 100644 tasks/section_3/main.yml create mode 100644 tasks/section_4/cis_4.1.1.x.yml create mode 100644 tasks/section_4/cis_4.1.2.x.yml create mode 100644 tasks/section_4/cis_4.1.x.yml create mode 100644 tasks/section_4/cis_4.2.1.x.yml create mode 100644 tasks/section_4/cis_4.2.2.x.yml create mode 100644 tasks/section_4/cis_4.2.3.yml create mode 100644 tasks/section_4/cis_4.3.yml create mode 100644 tasks/section_4/main.yml create mode 100644 tasks/section_5/cis_5.1.x.yml create mode 100644 tasks/section_5/cis_5.2.x.yml create mode 100644 tasks/section_5/cis_5.3.x.yml create mode 100644 tasks/section_5/cis_5.4.x.yml create mode 100644 tasks/section_5/cis_5.5.1.x.yml create mode 100644 tasks/section_5/cis_5.5.x.yml create mode 100644 tasks/section_5/cis_5.6.yml create mode 100644 tasks/section_5/cis_5.7.yml create mode 100644 tasks/section_5/main.yml create mode 100644 tasks/section_6/cis_6.1.x.yml create mode 100644 tasks/section_6/cis_6.2.x.yml create mode 100644 tasks/section_6/main.yml create mode 100644 templates/ansible_vars_goss.yml.j2 create mode 100644 templates/audit/99_auditd.rules.j2 create mode 100644 templates/chrony.conf.j2 create mode 100644 templates/etc/99-sysctl.conf.j2 create mode 100644 templates/etc/issue.j2 create mode 100644 templates/etc/issue.net.j2 create mode 100644 templates/etc/motd.j2 create mode 100644 templates/etc/systemd/system/tmp.mount.j2 create mode 100644 templates/hosts.allow.j2 create mode 100644 templates/ntp.conf.j2 create mode 100644 vars/main.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 00000000..f2a7e7cc --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,11 @@ +parseable: true +quiet: true +skip_list: + - '204' + - '305' + - '303' + - '403' + - '306' + - '602' +use_default_rules: true +verbosity: 0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..b2daffb9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# adding github settings to show correct language +*.sh linguist-detectable=true +*.yml linguist-detectable=true +*.ps1 linguist-detectable=true +*.j2 linguist-detectable=true +*.md linguist-documentation diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b1c61c0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +.env +*.log +*.retry +.cache +.vagrant +tests/*redhat-subscription +tests/Dockerfile +*.iso +*.box +packer_cache +delete* +ignore* +test_inv +# VSCode +.vscode + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# DS_Store +.DS_Store +._* + +# Linux Editors +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +.elc +auto-save-list +tramp +.\#* +*.swp +*.swo +rh-creds.env +travis.env + +# Lockdown-specific +benchparse/ +*xccdf.xml +*.retry + +# GitHub Action/Workflow files +.github/ diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000..fdea6296 --- /dev/null +++ b/.yamllint @@ -0,0 +1,23 @@ +--- +ignore: | + tests/ + molecule/ + .gitlab-ci.yml + *molecule.yml + +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 + brackets: + max-spaces-inside: 1 + level: error + line-length: disable diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..23ce2fb7 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,67 @@ +Contributing to MindPoint Group Projects +======================================== + +Rules +----- +1) All commits must be GPG signed (details in Signing section) +2) All commits must have Signed-off-by (Signed-off-by: Joan Doe ) in the commit message (details in Signing section) +3) All work is done in your own branch +4) All pull requests go into the devel branch. There are automated checks for signed commits, signoff in commit message, and functional testing) +5) Be open and nice to eachother + +Workflow +-------- +- Your work is done in your own individual branch. Make sure to to Signed-off and GPG sign all commits you intend to merge +- All community Pull Requests are into the devel branch. There are automated checks for GPG signed, Signed-off in commits, and functional tests before being approved. If your pull request comes in from outside of our repo, the pull request will go into a staging branch. There is info needed from our repo for our CI/CD testing. +- Once your changes are merged and a more detailed review is complete, an authorized member will merge your changes into the main branch for a new release + +Signing your contribution +------------------------- + +We've chosen to use the Developer's Certificate of Origin (DCO) method +that is employed by the Linux Kernel Project, which provides a simple +way to contribute to MindPoint Group projects. + +The process is to certify the below DCO 1.1 text +:: + + Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + + (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + + (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +:: + +Then, when it comes time to submit a contribution, include the +following text in your contribution commit message: + +:: + + Signed-off-by: Joan Doe + +:: + + +This message can be entered manually, or if you have configured git +with the correct `user.name` and `user.email`, you can use the `-s` +option to `git commit` to automatically include the signoff message. diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 00000000..09ba0bed --- /dev/null +++ b/Changelog.md @@ -0,0 +1,93 @@ +# Changes to rhel9CIS + +## 1.3.3 + +- update to audit script + - variable for audit OS agnostic + - removal of included library module (not required) + +- Issues included + - #135 - running levels - upadted tags + - #138 - auditd immutable + - #139 - 5.2.13 valus updated + - #140 + - #141 - check mode update + - #142 + - #143 - labels added + - #144 + - #146 - undefined variable added + - #147 - removed warn statement + - #149 - shell timeout + +## 1.3.2 + +- issues with crypto policies on ec2 - added skip for rules if system_is_ec2 variable + - cis_1.10 ## Change crypto breaks installing products + - cis_1.11 ## Change crypto breaks installing products + +## 1.3.1 + +- CIS 1.0.1 updates +- Added Issue and PR templates +- Added better reboot logic +- Added options to ensure idempotence +- Enhanced flush handlers +- Typo fixes +- mount check improvements +- Linting fixes +- Added systemd tmp mount +- Added systemd tmpfs block +- #110 tmp.mount support + - thanks to @erpadmin + +## 1.3 + +- extentions to LE audit capability +- more lint and layout changes +- sugroup assertion added 5.7 +- added extra logic variable to authselect/config section 5.3 related +- AlmaLinux and Rocky tested (comments in readme - also rsyslog installed at build or will fail) +- section 1.1 mount work has been rewritten and systemd tmp mount options added + +## 1.2.3 + +- #117 sugroup enhancements + - thanks to @ihotz +- #112 use of dnf module not shell + - thanks to @wolskie + +## 1.2.2 + +- #33 mkgrub missing variable issues - efi and bios path resolution + - thanks to @mrampant & @mickey1928geo +- #102 2.2.2 xorg pkg removal extended + - thanks to @RosarioVinoth +- #104 5.4.1 pwquality logic + - thanks to @RosarioVinoth +- #107 Idempotence improvement for 4.1.1.3 and 4.1.1.4 + - thanks to @andreyzher +- lint changes and updates to sync with ansible-galaxy + +## v1.2.1 + +- bootloader and default variables +- empty strings lint updates +- #87 +- rule 6.1.1 - audit only - outputs file discrepancies to {{ rhel9cis_rpm_audit_file }} +- #88 +- checkmode_improvements added to relevant tasks +- PR #96 +- crypto policy idempotency + +## v1.2.0 + +- #86 +- Adding on the goss auditing tool +- remove deprecated warnings +- format and layout +- general improvements +- readme updates +- use ansible package_facts +- #90 +- cis fix - nfs-server not nfs + - Thanks to danderemer diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5b33d4ad --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Mindpoint Group / Lockdown Enterprise / Lockdown Enterprise Releases + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..0e4d1f62 --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# RHEL 9 CIS (predicted) - ALPHA + +## This is purely development and those who have access to the RH developer branches for testing + +### This will work on RHEL8 and derivatives currently + +![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 [CIS RedHat Enterprise Linux 8 Benchmark v1.0.1 - 05-19-2021 ](https://www.cisecurity.org/cis-benchmarks/) + +## Caution(s) + +This role **will make changes to the system** which may have unintended concequences. This is not an auditing tool but rather a remediation tool to be used after an audit has been conducted. + +This role was developed against a clean install of the Operating System. If you are implimenting to an existing system please review this role for any site specific changes that are needed. + +To use release version please point to main branch + +## Documentation + +- [Getting Started](https://www.lockdownenterprise.com/docs/getting-started-with-lockdown) +- [Customizing Roles](https://www.lockdownenterprise.com/docs/customizing-lockdown-enterprise) +- [Per-Host Configuration](https://www.lockdownenterprise.com/docs/per-host-lockdown-enterprise-configuration) +- [Getting the Most Out of the Role](https://www.lockdownenterprise.com/docs/get-the-most-out-of-lockdown-enterprise) +- [Wiki](https://github.com/ansible-lockdown/RHEL9-CIS/wiki) +- [Repo GitHub Page](https://ansible-lockdown.github.io/RHEL9-CIS/) + +## Auditing (new) + +This can be turned on or off within the defaults/main.yml file with the variable rhel9cis_run_audit. The value is false by default, please refer to the wiki for more details. + +This is a much quicker, very lightweight, checking (where possible) config compliance and live/running settings. + +A new form of auditing has been develeoped, by using a small (12MB) go binary called [goss](https://github.com/aelsabbahy/goss) along with the relevant configurations to check. Without the need for infrastructure or other tooling. +This audit will not only check the config has the correct setting but aims to capture if it is running with that configuration also trying to remove [false positives](https://www.mindpointgroup.com/blog/is-compliance-scanning-still-relevant/) in the process. + +Refer to [RHEL9-CIS-Audit](https://github.com/ansible-lockdown/RHEL9-CIS-Audit). + +## Requirements + +RHEL 9 - Other versions are not supported. + +- 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:** + +- 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) + - [Ansible Getting Started](https://docs.ansible.com/ansible/latest/user_guide/intro_getting_started.html) + - [Tower User Guide](https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html) + - [Ansible Community Info](https://docs.ansible.com/ansible/latest/community/index.html) +- Functioning Ansible and/or Tower Installed, configured, and running. This includes all of the base Ansible/Tower configurations, needed packages installed, and infrastructure setup. +- Please read through the tasks in this role to gain an understanding of what each control is doing. Some of the tasks are disruptive and can have unintended consiquences in a live production system. Also familiarize yourself with the variables in the defaults/main.yml file or the [Main Variables Wiki Page](https://github.com/ansible-lockdown/RHEL9-CIS/wiki/Main-Variables). + +## Dependencies + +- Python3 +- Ansible 2.9+ +- python-def (should be included in RHEL 9) +- libselinux-python + +## 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. + +## Tags + +There are many tags available for added control precision. Each control has it's own set of tags noting what level, if it's scored/notscored, what OS element it relates to, if it's a patch or audit, and the rule number. + +Below is an example of the tag section from a control within this role. Using this example if you set your run to skip all controls with the tag services, this task will be skipped. The opposite can also happen where you run only controls tagged with services. + +```txt + tags: + - level1-server + - level1-workstation + - scored + - avahi + - services + - patch + - rule_2.2.4 +``` + +## Example Audit Summary + +This is based on a vagrant image with selections enabled. e.g. No Gui or firewall. +Note: More tests are run during audit as we check config and running state. + +```txt + +ok: [default] => { + "msg": [ + "The pre remediation results are: ['Total Duration: 5.454s', 'Count: 338, Failed: 47, Skipped: 5'].", + "The post remediation results are: ['Total Duration: 5.007s', 'Count: 338, Failed: 46, Skipped: 5'].", + "Full breakdown can be found in /var/tmp", + "" + ] +} + +PLAY RECAP ******************************************************************************************************************************************* +default : ok=270 changed=23 unreachable=0 failed=0 skipped=140 rescued=0 ignored=0 +``` + +## Branches + +- devel - This is the default branch and the working development branch. Community pull requests will pull into this branch +- main - This is the release branch +- reports - This is a protected branch for our scoring reports, no code should ever go here +- all other branches** - Individual community member branches + +## Community Contribution + +We encourage you (the community) to contribute to this role. Please read the rules below. + +- Your work is done in your own individual branch. Make sure to Signed-off and GPG sign all commits you intend to merge. +- All community Pull Requests are pulled into the devel branch +- Pull Requests into devel will confirm your commits have a GPG signature, Signed-off, and a functional test before being approved +- Once your changes are merged and a more detailed review is complete, an authorized member will merge your changes into the main branch for a new release diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 00000000..831f01d9 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,24 @@ +[defaults] +host_key_checking=False +display_skipped_hosts=True +system_warnings=False +command_warnings=False +nocows=1 +retry_files_save_path=/dev/null +library=~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules:./library + +[privilege_escalation] + +[paramiko_connection] +record_host_keys=False + +[ssh_connection] +transfer_method=scp + +[accelerate] + +[selinux] + +[colors] + +[diff] diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 00000000..e9c7a4b3 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,622 @@ +--- +# defaults file for rhel9-cis + +rhel9cis_skip_for_travis: false +rhel9cis_system_is_container: false +# rhel9cis is left off the front of this var for consistency in testing pipeline +# system_is_ec2 toggle will disable tasks that fail on Amazon EC2 instances. Set true to skip and false to run tasks +system_is_ec2: false + +rhel9cis_notauto: false +rhel9cis_section1: true +rhel9cis_section2: true +rhel9cis_section3: true +rhel9cis_section4: true +rhel9cis_section5: true +rhel9cis_section6: true + +rhel9cis_level_1: true +rhel9cis_level_2: true + +rhel9cis_selinux_disable: false +rhel9cis_legacy_boot: false + +## Python Binary +## This is used for python3 Installations where python2 OS modules are used in ansible +python2_bin: /bin/python2.7 + +## Benchmark name used by audting control role +# The audit variable found at the base +benchmark: RHEL9-CIS + +# Whether to skip the reboot +rhel9cis_skip_reboot: true + +#### Basic external goss audit enablement settings #### +#### Precise details - per setting can be found at the bottom of this file #### + +### Goss is required on the remote host +setup_audit: false +# How to retrive goss +# Options are copy or download - detailed settings at the bottom of this file +# you will need to access to either github or the file already dowmloaded +get_goss_file: download + +# how to get audit files onto host options +# options are git/copy/get_url - use local if already available to to the host (adjust paths accordingly) +audit_content: git + +# enable audits to run - this runs the audit and get the latest content +run_audit: false + +# Timeout for those cmds that take longer to run where timeout set +audit_cmd_timeout: 30000 + +### End Goss enablements #### +#### Detailed settings found at the end of this document #### + +# These variables correspond with the CIS rule IDs or paragraph numbers defined in +# the CIS benchmark documents. +# PLEASE NOTE: These work in coordination with the section # group variables and tags. +# You must enable an entire section in order for the variables below to take effect. +# 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_1_4: true +rhel9cis_rule_1_1_1_5: true +rhel9cis_rule_1_1_2: true +rhel9cis_rule_1_1_3: true +rhel9cis_rule_1_1_4: true +rhel9cis_rule_1_1_5: true +rhel9cis_rule_1_1_6: true +rhel9cis_rule_1_1_7: true +rhel9cis_rule_1_1_8: true +rhel9cis_rule_1_1_9: true +rhel9cis_rule_1_1_10: true +rhel9cis_rule_1_1_11: true +rhel9cis_rule_1_1_12: true +rhel9cis_rule_1_1_13: true +rhel9cis_rule_1_1_14: true +rhel9cis_rule_1_1_15: true +rhel9cis_rule_1_1_16: true +rhel9cis_rule_1_1_17: 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_22: true +rhel9cis_rule_1_1_23: 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_2_5: 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_5_1: true +rhel9cis_rule_1_5_2: true +rhel9cis_rule_1_5_3: true +rhel9cis_rule_1_6_1: true +rhel9cis_rule_1_6_2: true +rhel9cis_rule_1_7_1_1: true +rhel9cis_rule_1_7_1_2: true +rhel9cis_rule_1_7_1_3: true +rhel9cis_rule_1_7_1_4: true +rhel9cis_rule_1_7_1_5: true +rhel9cis_rule_1_7_1_6: true +rhel9cis_rule_1_7_1_7: true +rhel9cis_rule_1_8_1_1: true +rhel9cis_rule_1_8_1_2: true +rhel9cis_rule_1_8_1_3: true +rhel9cis_rule_1_8_1_4: true +rhel9cis_rule_1_8_1_5: true +rhel9cis_rule_1_8_1_6: true +rhel9cis_rule_1_8_2: true +rhel9cis_rule_1_9: true +rhel9cis_rule_1_10: true +rhel9cis_rule_1_11: true + +# Section 2 rules +rhel9cis_rule_2_1_1: true +rhel9cis_rule_2_1_2: true +rhel9cis_rule_2_1_3: true +rhel9cis_rule_2_1_4: true +rhel9cis_rule_2_1_5: true +rhel9cis_rule_2_1_6: true +rhel9cis_rule_2_1_7: true +rhel9cis_rule_2_2_1_1: true +rhel9cis_rule_2_2_1_2: true +rhel9cis_rule_2_2_1_3: true +rhel9cis_rule_2_2_2: true +rhel9cis_rule_2_2_3: true +rhel9cis_rule_2_2_4: true +rhel9cis_rule_2_2_5: true +rhel9cis_rule_2_2_6: true +rhel9cis_rule_2_2_7: true +rhel9cis_rule_2_2_8: true +rhel9cis_rule_2_2_9: true +rhel9cis_rule_2_2_10: true +rhel9cis_rule_2_2_11: true +rhel9cis_rule_2_2_12: true +rhel9cis_rule_2_2_13: true +rhel9cis_rule_2_2_14: true +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_3_1: true +rhel9cis_rule_2_3_2: true +rhel9cis_rule_2_3_3: true + +# Section 3 rules +rhel9cis_rule_3_1_1: true +rhel9cis_rule_3_1_2: true +rhel9cis_rule_3_2_1: true +rhel9cis_rule_3_2_2: true +rhel9cis_rule_3_2_3: true +rhel9cis_rule_3_2_4: true +rhel9cis_rule_3_2_5: true +rhel9cis_rule_3_2_6: true +rhel9cis_rule_3_2_7: true +rhel9cis_rule_3_2_8: true +rhel9cis_rule_3_2_9: true +rhel9cis_rule_3_3_1: true +rhel9cis_rule_3_3_2: true +rhel9cis_rule_3_3_3: true +rhel9cis_rule_3_3_4: true +rhel9cis_rule_3_4_1_1: true +rhel9cis_rule_3_4_2_1: true +rhel9cis_rule_3_4_2_2: true +rhel9cis_rule_3_4_2_3: true +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_3_1: true +rhel9cis_rule_3_4_3_2: true +rhel9cis_rule_3_4_3_3: true +rhel9cis_rule_3_4_3_4: true +rhel9cis_rule_3_4_3_5: true +rhel9cis_rule_3_4_3_6: true +rhel9cis_rule_3_4_3_7: true +rhel9cis_rule_3_4_3_8: true +rhel9cis_rule_3_4_4_1_1: true +rhel9cis_rule_3_4_4_1_2: true +rhel9cis_rule_3_4_4_1_3: true +rhel9cis_rule_3_4_4_1_4: true +rhel9cis_rule_3_4_4_1_5: true +rhel9cis_rule_3_4_4_2_1: true +rhel9cis_rule_3_4_4_2_2: true +rhel9cis_rule_3_4_4_2_3: true +rhel9cis_rule_3_4_4_2_4: true +rhel9cis_rule_3_4_4_2_5: true +rhel9cis_rule_3_5: true +rhel9cis_rule_3_6: true + +# Section 4 rules +rhel9cis_rule_4_1_1_1: true +rhel9cis_rule_4_1_1_2: true +rhel9cis_rule_4_1_1_3: true +rhel9cis_rule_4_1_1_4: true +rhel9cis_rule_4_1_2_1: true +rhel9cis_rule_4_1_2_2: true +rhel9cis_rule_4_1_2_3: true +rhel9cis_rule_4_1_3: true +rhel9cis_rule_4_1_4: true +rhel9cis_rule_4_1_5: true +rhel9cis_rule_4_1_6: true +rhel9cis_rule_4_1_7: true +rhel9cis_rule_4_1_8: true +rhel9cis_rule_4_1_9: true +rhel9cis_rule_4_1_10: true +rhel9cis_rule_4_1_11: true +rhel9cis_rule_4_1_12: true +rhel9cis_rule_4_1_13: true +rhel9cis_rule_4_1_14: true +rhel9cis_rule_4_1_15: true +rhel9cis_rule_4_1_16: true +rhel9cis_rule_4_1_17: true +rhel9cis_rule_4_2_1_1: true +rhel9cis_rule_4_2_1_2: true +rhel9cis_rule_4_2_1_3: true +rhel9cis_rule_4_2_1_4: true +rhel9cis_rule_4_2_1_5: true +rhel9cis_rule_4_2_1_6: true +rhel9cis_rule_4_2_2_1: true +rhel9cis_rule_4_2_2_2: true +rhel9cis_rule_4_2_2_3: true +rhel9cis_rule_4_2_3: true +rhel9cis_rule_4_3: true + +# Section 5 rules +rhel9cis_rule_5_1_1: true +rhel9cis_rule_5_1_2: true +rhel9cis_rule_5_1_3: true +rhel9cis_rule_5_1_4: true +rhel9cis_rule_5_1_5: true +rhel9cis_rule_5_1_6: true +rhel9cis_rule_5_1_7: true +rhel9cis_rule_5_1_8: true +rhel9cis_rule_5_2_1: true +rhel9cis_rule_5_2_2: true +rhel9cis_rule_5_2_3: true +rhel9cis_rule_5_2_4: true +rhel9cis_rule_5_2_5: true +rhel9cis_rule_5_2_6: true +rhel9cis_rule_5_2_7: true +rhel9cis_rule_5_2_8: true +rhel9cis_rule_5_2_9: true +rhel9cis_rule_5_2_10: true +rhel9cis_rule_5_2_12: true +rhel9cis_rule_5_2_11: true +rhel9cis_rule_5_2_13: true +rhel9cis_rule_5_2_14: true +rhel9cis_rule_5_2_15: true +rhel9cis_rule_5_2_16: true +rhel9cis_rule_5_2_17: true +rhel9cis_rule_5_2_18: true +rhel9cis_rule_5_2_19: true +rhel9cis_rule_5_2_20: true +rhel9cis_rule_5_3_1: true +rhel9cis_rule_5_3_2: true +rhel9cis_rule_5_3_3: true +rhel9cis_rule_5_4_1: true +rhel9cis_rule_5_4_2: true +rhel9cis_rule_5_4_3: true +rhel9cis_rule_5_4_4: true +rhel9cis_rule_5_5_1_1: true +rhel9cis_rule_5_5_1_2: true +rhel9cis_rule_5_5_1_3: true +rhel9cis_rule_5_5_1_4: true +rhel9cis_rule_5_5_1_5: true +rhel9cis_rule_5_5_2: true +rhel9cis_rule_5_5_3: true +rhel9cis_rule_5_5_4: true +rhel9cis_rule_5_5_5: true +rhel9cis_rule_5_6: true +rhel9cis_rule_5_7: true + +# Section 6 rules +rhel9cis_rule_6_1_1: true +rhel9cis_rule_6_1_2: true +rhel9cis_rule_6_1_3: true +rhel9cis_rule_6_1_4: true +rhel9cis_rule_6_1_5: true +rhel9cis_rule_6_1_6: true +rhel9cis_rule_6_1_7: true +rhel9cis_rule_6_1_8: true +rhel9cis_rule_6_1_9: true +rhel9cis_rule_6_1_10: true +rhel9cis_rule_6_1_11: true +rhel9cis_rule_6_1_12: true +rhel9cis_rule_6_1_13: true +rhel9cis_rule_6_1_14: true +rhel9cis_rule_6_2_1: true +rhel9cis_rule_6_2_2: true +rhel9cis_rule_6_2_3: true +rhel9cis_rule_6_2_4: true +rhel9cis_rule_6_2_5: true +rhel9cis_rule_6_2_6: true +rhel9cis_rule_6_2_7: true +rhel9cis_rule_6_2_8: false +rhel9cis_rule_6_2_9: true +rhel9cis_rule_6_2_10: true +rhel9cis_rule_6_2_11: true +rhel9cis_rule_6_2_12: true +rhel9cis_rule_6_2_13: true +rhel9cis_rule_6_2_14: true +rhel9cis_rule_6_2_15: true +rhel9cis_rule_6_2_16: true +rhel9cis_rule_6_2_17: true +rhel9cis_rule_6_2_18: true +rhel9cis_rule_6_2_19: true +rhel9cis_rule_6_2_20: true + +# Service configuration booleans set true to keep service +rhel9cis_avahi_server: false +rhel9cis_cups_server: false +rhel9cis_dhcp_server: false +rhel9cis_ldap_server: false +rhel9cis_telnet_server: false +rhel9cis_nfs_server: false +rhel9cis_rpc_server: false +rhel9cis_ntalk_server: false +rhel9cis_rsyncd_server: false +rhel9cis_tftp_server: false +rhel9cis_rsh_server: false +rhel9cis_nis_server: false +rhel9cis_snmp_server: false +rhel9cis_squid_server: false +rhel9cis_smb_server: false +rhel9cis_dovecot_server: false +rhel9cis_httpd_server: false +rhel9cis_vsftpd_server: false +rhel9cis_named_server: false +rhel9cis_nfs_rpc_server: false +rhel9cis_is_mail_server: false +rhel9cis_bind: false +rhel9cis_vsftpd: false +rhel9cis_httpd: false +rhel9cis_dovecot: false +rhel9cis_samba: false +rhel9cis_squid: false +rhel9cis_net_snmp: false +rhel9cis_allow_autofs: false + +## Section 1 vars + +# 1.1.2 +# These settings go into the /etc/fstab file for the /tmp mount settings +# The value must contain nosuid,nodev,noexec to conform to CIS standards +# rhel9cis_tmp_tmpfs_settings: "defaults,rw,nosuid,nodev,noexec,relatime 0 0" +# If set true uses the tmp.mount service else using fstab configuration +rhel9cis_tmp_svc: false + +# 1.2.1 +# This is the login information for your RedHat Subscription +# DO NOT USE PLAIN TEXT PASSWORDS!!!!! +# The intent here is to use a password utility like Ansible Vault here +rhel9cis_rh_sub_user: user +rhel9cis_rh_sub_password: password + +# 1.2.2 +# Do you require rhnsd +# RedHat Satellite Subscription items +rhel9cis_rhnsd_required: false + +# 1.3.3 var log location variable +rhel9cis_varlog_location: "/var/log/sudo.log" + +# xinetd required +rhel9cis_xinetd_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.10/1.11 Set crypto policy (LEGACY, DEFAULT, FUTURE, FIPS) +# Control 1.10 sates not ot use LEGACY and control 1.11 says to use FUTURE or FIPS. +rhel9cis_crypto_policy: "FUTURE" + +# System network parameters (host only OR host and router) +rhel9cis_is_router: false + +# IPv6 required +rhel9cis_ipv6_required: true + +# AIDE +rhel9cis_config_aide: true +# AIDE cron settings +rhel9cis_aide_cron: + cron_user: root + cron_file: /etc/crontab + aide_job: '/usr/sbin/aide --check' + aide_minute: 0 + aide_hour: 5 + aide_day: '*' + aide_month: '*' + aide_weekday: '*' + +# SELinux policy +rhel9cis_selinux_pol: targeted + +# Whether or not to run tasks related to auditing/patching the desktop environment +rhel9cis_gui: no + +# Set to 'true' if X Windows is needed in your environment +rhel9cis_xwindows_required: false + +rhel9cis_openldap_clients_required: false +rhel9cis_telnet_required: false +rhel9cis_talk_required: false +rhel9cis_rsh_required: false +rhel9cis_ypbind_required: false + +# 2.2.1.1 Time Synchronization - Either chrony or ntp +rhel9cis_time_synchronization: chrony + +# 2.2.1.2 Time Synchronization servers - used in template file chrony.conf.j2 +rhel9cis_time_synchronization_servers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 2.pool.ntp.org + - 3.pool.ntp.org + +rhel9cis_chrony_server_options: "minpoll 8" +rhel9cis_ntp_server_options: "iburst" + +## Section3 vars +# 3.4.2 | PATCH | Ensure /etc/hosts.allow is configured +rhel9cis_host_allow: + - "10.0.0.0/255.0.0.0" + - "172.16.0.0/255.240.0.0" + - "192.168.0.0/255.255.0.0" + +# Firewall Service - either firewalld, iptables, or nftables +rhel9cis_firewall: firewalld + +# 3.4.2.4 Default zone setting +rhel9cis_default_zone: public + +# 3.4.2.5 Zone and Interface setting +rhel9cis_int_zone: customezone +rhel9cis_interface: eth0 + +rhel9cis_firewall_services: + - ssh + - dhcpv6-client + +# 3.4.3.2 Set nftables new table create +rhel9cis_nft_tables_autonewtable: true +rhel9cis_nft_tables_tablename: filter + +# 3.4.3.3 Set nftables new chain create +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. +# End Banner + +## Section4 vars + +rhel9cis_auditd: + space_left_action: email + action_mail_acct: root + admin_space_left_action: halt + max_log_file_action: keep_logs + +rhel9cis_logrotate: "daily" + +# The audit_back_log_limit value should never be below 8192 +rhel9cis_audit_back_log_limit: 8192 + +# The max_log_file parameter should be based on your sites policy +rhel9cis_max_log_file_size: 10 + +# RHEL-09-4.2.1.4/4.2.1.5 remote and destation log server name +rhel9cis_remote_log_server: logagg.example.com + +# RHEL-09-4.2.1.5 +rhel9cis_system_is_log_server: false + +## Section5 vars + +rhel9cis_sshd: + clientalivecountmax: 0 + clientaliveinterval: 900 + ciphers: "aes256-ctr,aes192-ctr,aes128-ctr" + macs: "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com" + logingracetime: 60 + # WARNING: make sure you understand the precedence when working with these values!! + # allowusers: + # allowgroups: systems dba + # denyusers: + # denygroups: +rhel9cis_pam_faillock: + attempts: 5 + interval: 900 + unlock_time: 900 + fail_for_root: no + remember: 5 + pwhash: sha512 + +# 5.2.5 SSH LogLevel setting. Options are INFO or VERBOSE +rhel9cis_ssh_loglevel: INFO + +# 5.2.19 SSH MaxSessions setting. Must be 4 our less +rhel9cis_ssh_maxsessions: 4 +rhel9cis_inactivelock: + lock_days: 30 + +# 5.3.1/5.3.2 Custom authselect profile settings. Settings in place now will fail, they are place holders from the control example +# Due to the way many multiple options and ways to configure this control needs to be enabled and settings adjusted to minimise risk +rhel9cis_use_authconfig: false +rhel9cis_authselect: + custom_profile_name: custom-profile + default_file_to_copy: "sssd --symlink-meta" + options: with-sudo with-faillock without-nullok + +# 5.3.1 Enable automation to create custom profile settings, using the settings above +rhel9cis_authselect_custom_profile_create: false + +# 5.3.2 Enable automation to select custom profile options, using the settings above +rhel9cis_authselect_custom_profile_select: false + +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 + +rhel9cis_vartmp: + source: /tmp + fstype: none + opts: "defaults,nodev,nosuid,noexec,bind" + enabled: no +## PAM +rhel9cis_pam_password: + minlen: "14" + minclass: "4" + +# Starting GID for interactive users +rhel9cis_int_gid: 1000 + +# 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) +rhel9cis_shell_session_timeout: + file: /etc/profile.d/tmout.sh + timeout: 600 +# RHEL-09-5.4.1.5 Allow ansible to expire password for account with a last changed date in the future. False will just display users in violation, true will expire those users passwords +rhel9cis_futurepwchgdate_autofix: true + +# 5.7 +# rhel9cis_sugroup: sugroup # change accordingly wheel is default + +# wheel users list +rhel9cis_sugroup_users: "root" + +## Section6 vars + +# RHEL-09_6.1.1 +rhel9cis_rpm_audit_file: /var/tmp/rpm_file_check + +# RHEL-09_6.1.10 Allow ansible to adjust world-writable files. False will just display world-writable files, True will remove world-writable +rhel9cis_no_world_write_adjust: true +rhel9cis_passwd_label: "{{ (this_item | default(item)).id }}: {{ (this_item | default(item)).dir }}" +# 6.2.9 +rhel9cis_dotperm_ansiblemanaged: true +#### Goss Configuration Settings #### + +### Goss binary settings ### +goss_version: + release: v0.3.16 + checksum: 'sha256:827e354b48f93bce933f5efcd1f00dc82569c42a179cf2d384b040d8a80bfbfb' +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" + +## if get_goss_file - copy the following needs to be updated for your environment +## it is expected that it will be copied from somewhere accessible to the control node +## e.g copy from ansible control node to remote host +copy_goss_from_path: /some/accessible/path + +### Goss Audit Benchmark file ### +## managed by the control audit_content +# git +audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" +audit_git_version: main + +# copy: +audit_local_copy: "some path to copy from" + +# get_url: +audit_files_url: "some url maybe s3?" + +# Where the goss audit configuration will be stored +audit_files: "/var/tmp/{{ benchmark }}-Audit/" + +## Goss configuration information +# Where the goss configs and outputs are stored +audit_out_dir: '/var/tmp' +audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit/" +pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}_pre_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" +post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}_post_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" + +## The following should not need changing +goss_file: "{{ audit_conf_dir }}goss.yml" +audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_hostname }}.yml" +audit_results: | + The pre remediation results are: {{ pre_audit_summary }}. + The post remediation results are: {{ post_audit_summary }}. + Full breakdown can be found in {{ audit_out_dir }} diff --git a/files/etc/systemd/system/tmp.mount b/files/etc/systemd/system/tmp.mount new file mode 100644 index 00000000..47ca6625 --- /dev/null +++ b/files/etc/systemd/system/tmp.mount @@ -0,0 +1,25 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Temporary Directory +Documentation=man:hier(7) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +ConditionPathIsSymbolicLink=!/tmp +DefaultDependencies=no +Conflicts=umount.target +Before=local-fs.target umount.target + +[Mount] +What=tmpfs +Where=/tmp +Type=tmpfs +Options=mode=1777,strictatime,noexec,nodev,nosuid + +# Make 'systemctl enable tmp.mount' work: +[Install] +WantedBy=local-fs.target diff --git a/group_vars/docker b/group_vars/docker new file mode 100644 index 00000000..5b6e3b29 --- /dev/null +++ b/group_vars/docker @@ -0,0 +1,28 @@ +--- +ansible_user: root +# AIDE cron settings +rhel9cis_aide_cron: + cron_user: root + cron_file: /var/spool/cron/root + aide_job: '/usr/sbin/aide --check' + aide_minute: 0 + aide_hour: 5 + aide_day: '*' + aide_month: '*' + aide_weekday: '*' + +rhel9cis_sshd: + clientalivecountmax: 3 + clientaliveinterval: 300 + ciphers: "aes256-ctr,aes192-ctr,aes128-ctr" + macs: "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com" + logingracetime: 60 + # - make sure you understand the precedence when working with these values!! + allowusers: vagrant + allowgroups: vagrant + denyusers: root + denygroups: root + +# Workarounds for Docker +rhel9cis_skip_for_travis: true +rhel9cis_selinux_disable: true diff --git a/group_vars/vagrant b/group_vars/vagrant new file mode 100644 index 00000000..1c0fb37f --- /dev/null +++ b/group_vars/vagrant @@ -0,0 +1,28 @@ +--- +ansible_user: vagrant +# AIDE cron settings +rhel9cis_aide_cron: + cron_user: root + cron_file: /var/spool/cron/root + aide_job: '/usr/sbin/aide --check' + aide_minute: 0 + aide_hour: 5 + aide_day: '*' + aide_month: '*' + aide_weekday: '*' + +rhel9cis_sshd: + clientalivecountmax: 3 + clientaliveinterval: 300 + ciphers: 'aes256-ctr,aes192-ctr,aes128-ctr' + macs: 'hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com' + logingracetime: 60 + # - make sure you understand the precedence when working with these values!! + allowusers: vagrant + allowgroups: vagrant + denyusers: root + denygroups: root + +# Vagrant can touch code that Docker cannot +rhel9cis_skip_for_travis: false +rhel9cis_selinux_disable: false diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 00000000..fdd93548 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,134 @@ +--- +# handlers file for RHEL9-CIS + +- name: sysctl flush ipv4 route table + become: yes + sysctl: + name: net.ipv4.route.flush + value: '1' + sysctl_set: yes + ignore_errors: yes + when: ansible_virtualization_type != "docker" + tags: + - skip_ansible_lint + +- name: sysctl flush ipv6 route table + become: yes + sysctl: + name: net.ipv6.route.flush + value: '1' + sysctl_set: yes + when: ansible_virtualization_type != "docker" + +- name: update sysctl + template: + src: etc/99-sysctl.conf.j2 + dest: /etc/sysctl.d/99-sysctl.conf + owner: root + group: root + mode: 0600 + notify: reload sysctl + when: ansible_virtualization_type != "docker" + +- name: reload sysctl + sysctl: + name: net.ipv4.route.flush + value: '1' + state: present + reload: yes + ignoreerrors: yes + when: ansible_virtualization_type != "docker" + +- name: systemd restart tmp.mount + become: yes + systemd: + name: tmp.mount + daemon_reload: yes + enabled: yes + masked: no + state: reloaded + +- name: systemd restart var-tmp.mount + become: yes + systemd: + name: var-tmp.mount + daemon_reload: yes + enabled: yes + masked: no + state: reloaded + +- name: remount tmp + command: mount -o remount /tmp + args: + warn: false + +- name: restart firewalld + become: yes + service: + name: firewalld + state: restarted + +- name: restart xinetd + become: yes + service: + name: xinetd + state: restarted + +- name: restart sshd + become: yes + service: + name: sshd + state: restarted + +- name: restart postfix + become: yes + service: + name: postfix + state: restarted + +- name: reload dconf + become: yes + command: dconf update + +- name: update auditd + template: + src: audit/99_auditd.rules.j2 + dest: /etc/audit/rules.d/99_auditd.rules + owner: root + group: root + mode: 0600 + notify: restart auditd + +- name: restart auditd + command: /sbin/service auditd restart + changed_when: no + check_mode: no + failed_when: no + args: + warn: no + when: + - not rhel9cis_skip_for_travis + tags: + - skip_ansible_lint + +- name: grub2cfg + command: "grub2-mkconfig -o {{ grub_cfg.stat.lnk_source }}" + ignore_errors: True + tags: + - skip_ansible_lint + +- name: restart rsyslog + become: yes + service: + name: rsyslog + state: restarted + +- name: restart syslog-ng + become: yes + service: + name: syslog-ng + state: restarted + +- name: systemd_daemon_reload + systemd: + daemon-reload: yes diff --git a/local.yml b/local.yml new file mode 100644 index 00000000..2c649b2f --- /dev/null +++ b/local.yml @@ -0,0 +1,12 @@ +--- + +- hosts: localhost + connection: local + become: true + vars: + is_container: false + + roles: + - role: "{{ playbook_dir }}" + rhel9cis_system_is_container: "{{ is_container | default(false) }}" + rhel9cis_skip_for_travis: false diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 00000000..cf060a31 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,18 @@ +--- +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" + company: "MindPoint Group" + license: MIT + role_name: rhel9_cis + min_ansible_version: 2.9.0 + platforms: + - name: EL + versions: + - 9 + galaxy_tags: + - system + - security + - cis + - hardening +dependencies: [] diff --git a/site.yml b/site.yml new file mode 100644 index 00000000..2763e43b --- /dev/null +++ b/site.yml @@ -0,0 +1,11 @@ +--- +- hosts: all + become: true + vars: + is_container: false + + roles: + + - role: "{{ playbook_dir }}" + rhel9cis_system_is_container: "{{ is_container | default(false) }}" + rhel9cis_skip_for_travis: false diff --git a/tasks/LE_audit_setup.yml b/tasks/LE_audit_setup.yml new file mode 100644 index 00000000..61a4cdf1 --- /dev/null +++ b/tasks/LE_audit_setup.yml @@ -0,0 +1,22 @@ +--- + +- name: Download audit binary + get_url: + url: "{{ goss_url }}" + dest: "{{ audit_bin }}" + owner: root + group: root + checksum: "{{ goss_version.checksum }}" + mode: 0555 + when: + - get_goss_file == 'download' + +- name: copy audit binary + copy: + src: + dest: "{{ audit_bin }}" + mode: 0555 + owner: root + group: root + when: + - get_goss_file == 'copy' diff --git a/tasks/audit_homedirperms.yml b/tasks/audit_homedirperms.yml new file mode 100644 index 00000000..596fed5a --- /dev/null +++ b/tasks/audit_homedirperms.yml @@ -0,0 +1,46 @@ +--- +- name: "SCORED | 6.2.8 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + find: + paths: + - "{{ homedir }}" + recurse: true + file_type: any + register: rhel_09_6_2_8_results + when: + - rhel9cis_rule_6_2_8|bool + tags: + - level1 + - patch + - rule_6.2.8 + +- name: "SCORED | 6.2.8 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + file: + path: "{{ line_item.path }}" + mode: 0640 + loop: "{{ rhel_09_6_2_8_results.files }}" + loop_control: + label: "{{ line_item.path }}" + loop_var: line_item + when: + - rhel_09_6_2_8_results.files.isreg is defined + - rhel9cis_rule_6_2_8|bool + tags: + - level1 + - patch + - rule_6.2.8 + +- name: "SCORED | 6.2.8 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + file: + path: "{{ line_item.path }}" + mode: 0750 + loop: "{{ rhel_09_6_2_8_results.files }}" + loop_control: + label: "{{ line_item.path }}" + loop_var: line_item + when: + - rhel_09_6_2_8_results.files.isdir is defined + - rhel9cis_rule_6_2_8|bool + tags: + - level1 + - patch + - rule_6.2.8 diff --git a/tasks/check_prereqs.yml b/tasks/check_prereqs.yml new file mode 100644 index 00000000..5ce4ab44 --- /dev/null +++ b/tasks/check_prereqs.yml @@ -0,0 +1,36 @@ +--- + +- name: "PREREQ | Add the required packages | Python 3" + block: + - name: Check if python36-rpm package installed + command: rpm -q python36-rpm + failed_when: ( python36_rpm_present.rc not in [ 0, 1 ] ) + changed_when: false + args: + warn: false + register: python36_rpm_present + + - name: Add the EPEL repository required for the python36-rpm pkg + package: + name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm + state: present + register: epel_installed + when: + - python36_rpm_present.rc != '0' + + - name: "PREREQ | Check required packages installed | Python3 " + package: + name: "{{ item }}" + state: present + register: python3reqs_installed + loop: + - python36-rpm + - libselinux-python3 + + - name: Disable Epel repo if installed earlier + command: yum-config-manager disable epel + when: epel_installed.changed + when: + - ( ansible_python.version.major == 3 and ansible_python.version.minor == 6 ) + vars: + ansible_python_interpreter: "{{ python2_bin }}" diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 00000000..ef1cef5c --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,111 @@ +--- +# tasks file for RHEL9-CIS +- name: Check OS version and family + fail: + msg: "This role can only be run against RHEL 8. {{ ansible_distribution }} {{ ansible_distribution_major_version }} is not supported." + when: + - ansible_os_family == 'RedHat' + - ansible_distribution_major_version is version_compare('9', '!=') + tags: + - always + +- name: Check ansible version + fail: + msg: You must use ansible 2.9 or greater + when: not ansible_version.full is version_compare('2.9', '>=') + tags: + - always + +- name: Check crypto-policy input + assert: + that: rhel9cis_crypto_policy in rhel9cis_allowed_crypto_policies + +- name: Check rhel9cis_bootloader_password_hash variable has been changed + assert: + that: 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" + when: + - rhel9cis_set_boot_pass + - rhel9cis_rule_1_5_2 + +- name: "check sugroup exists if used" + block: + - name: "Check su group exists if defined" + shell: grep -w "{{ rhel9cis_sugroup }}" /etc/group + 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 + +- include: prelim.yml + become: yes + tags: + - prelim_tasks + - always + +- import_tasks: pre_remediation_audit.yml + when: + - run_audit + +- name: Gather the package facts + package_facts: + manager: auto + tags: + - always + +- include: parse_etc_password.yml + become: yes + when: rhel9cis_section6 + +- include: section_1/main.yml + become: yes + when: rhel9cis_section1 + tags: + - rhel9cis_section1 + +- include: section_2/main.yml + become: yes + when: rhel9cis_section2 + +- include: section_3/main.yml + become: yes + when: rhel9cis_section3 + +- include: section_4/main.yml + become: yes + when: rhel9cis_section4 + +- include: section_5/main.yml + become: yes + when: rhel9cis_section5 + +- include: section_6/main.yml + become: yes + when: rhel9cis_section6 + +- include: post.yml + become: yes + tags: + - post_tasks + - always + +- import_tasks: post_remediation_audit.yml + when: + - run_audit + +- name: Show Audit Summary + debug: + msg: "{{ audit_results.split('\n') }}" + when: + - run_audit diff --git a/tasks/parse_etc_password.yml b/tasks/parse_etc_password.yml new file mode 100644 index 00000000..de29ff12 --- /dev/null +++ b/tasks/parse_etc_password.yml @@ -0,0 +1,32 @@ +--- + +- 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" + command: cat /etc/passwd + changed_when: no + check_mode: no + register: rhel9cis_passwd_file_audit + + - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" + 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 }}" + vars: + ld_passwd_regex: >- + ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) + ld_passwd_yaml: | + id: >-4 + \g + password: >-4 + \g + uid: \g + gid: \g + gecos: >-4 + \g + dir: >-4 + \g + shell: >-4 + \g + tags: + - always diff --git a/tasks/post.yml b/tasks/post.yml new file mode 100644 index 00000000..35c3b79d --- /dev/null +++ b/tasks/post.yml @@ -0,0 +1,56 @@ +--- +# Post tasks + +- name: Perform DNF package cleanup + dnf: + autoremove: yes + changed_when: no + +- name: trigger update sysctl + command: /bin/true + changed_when: false + check_mode: false + notify: update sysctl + when: + - rhel9cis_rule_1_6_1 or + rhel9cis_rule_1_6_2 or + rhel9cis_rule_3_1_2 or + rhel9cis_rule_3_1_2 or + rhel9cis_rule_3_2_1 or + rhel9cis_rule_3_2_2 or + rhel9cis_rule_3_2_3 or + rhel9cis_rule_3_2_4 or + rhel9cis_rule_3_2_5 or + rhel9cis_rule_3_2_6 or + rhel9cis_rule_3_2_7 or + rhel9cis_rule_3_2_8 or + rhel9cis_rule_3_2_9 + tags: + - sysctl + +- name: trigger update auditd + command: /bin/true + notify: update auditd + changed_when: false + check_mode: false + when: + - rhel9cis_rule_4_1_3 or + rhel9cis_rule_4_1_4 or + rhel9cis_rule_4_1_5 or + rhel9cis_rule_4_1_6 or + rhel9cis_rule_4_1_7 or + rhel9cis_rule_4_1_8 or + rhel9cis_rule_4_1_9 or + rhel9cis_rule_4_1_10 or + rhel9cis_rule_4_1_11 or + rhel9cis_rule_4_1_12 + tags: + - auditd + +- name: flush handlers + meta: flush_handlers + +- name: Reboot host + reboot: + when: + - not rhel9cis_skip_reboot diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml new file mode 100644 index 00000000..17ef3f87 --- /dev/null +++ b/tasks/post_remediation_audit.yml @@ -0,0 +1,43 @@ +--- + +- 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 }}" + vars: + warn: false + +- name: Post Audit | ensure audit files readable by users + file: + path: "{{ item }}" + mode: 0644 + state: file + loop: + - "{{ post_audit_outfile }}" + - "{{ pre_audit_outfile }}" + +- name: Post Audit | Capture audit data if json format + block: + - name: "capture data {{ post_audit_outfile }}" + command: "cat {{ post_audit_outfile }}" + register: post_audit + changed_when: false + + - name: Capture post-audit result + set_fact: + post_audit_summary: "{{ post_audit.stdout | from_json |json_query(summary) }}" + vars: + summary: 'summary."summary-line"' + when: + - audit_format == "json" + +- name: Post Audit | Capture audit data if documentation format + block: + - name: "Post Audit | capture data {{ post_audit_outfile }}" + command: "tail -2 {{ post_audit_outfile }}" + register: post_audit + changed_when: false + + - name: Post Audit | Capture post-audit result + 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 new file mode 100644 index 00000000..4e568dc2 --- /dev/null +++ b/tasks/pre_remediation_audit.yml @@ -0,0 +1,118 @@ +--- + +- name: Pre Audit | Setup the audit + include_tasks: LE_audit_setup.yml + when: + - setup_audit + tags: + - setup_audit + +- name: "Pre Audit | Ensure {{ audit_conf_dir }} exists" + file: + path: "{{ audit_conf_dir }}" + state: directory + mode: '0755' + +- name: Pre Audit | If using git for content set up + block: + - name: Pre Audit | Install git (rh8 python3) + package: + name: git + state: present + when: ansible_distribution_major_version == 8 + + - name: Pre Audit | Install git (rh7 python2) + package: + name: git + state: present + vars: + ansible_python_interpreter: "{{ python2_bin }}" + when: ansible_distribution_major_version == 7 + + - name: Pre Audit | retrieve audit content files from git + git: + repo: "{{ audit_file_git }}" + dest: "{{ audit_conf_dir }}" + version: "{{ audit_git_version }}" + when: + - audit_content == 'git' + +- name: Pre Audit | copy to audit content files to server + copy: + src: "{{ audit_local_copy }}" + dest: "{{ audit_conf_dir }}" + mode: 0644 + when: + - audit_content == 'copy' + +- name: Pre Audit | get audit content from url + get_url: + url: "{{ audit_files_url }}" + dest: "{{ audit_conf_dir }}" + when: + - audit_content == 'get_url' + +- name: Pre Audit | Check Goss is available + block: + - name: Pre Audit | Check for goss file + stat: + path: "{{ audit_bin }}" + register: goss_available + + - name: Pre Audit | If audit ensure goss is available + assert: + msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" + when: + - not goss_available.stat.exists + when: + - run_audit + +- name: "Pre Audit | Check whether machine is UEFI-based" + stat: + path: /sys/firmware/efi + register: rhel9_efi_boot + tags: + - goss_template + +- name: Pre Audit | Copy ansible default vars values to test audit + template: + src: ansible_vars_goss.yml.j2 + dest: "{{ audit_vars_path }}" + mode: 0600 + when: + - run_audit + tags: + - 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 }}" + vars: + warn: false + +- name: Pre Audit | Capture audit data if json format + block: + - name: "Pre Audit | capture data {{ pre_audit_outfile }}" + command: "cat {{ pre_audit_outfile }}" + register: pre_audit + changed_when: false + + - name: Pre Audit | Capture pre-audit result + set_fact: + pre_audit_summary: "{{ pre_audit.stdout | from_json |json_query(summary) }}" + vars: + summary: 'summary."summary-line"' + when: + - audit_format == "json" + +- name: Pre Audit | Capture audit data if documentation format + block: + - name: "Pre Audit | capture data {{ pre_audit_outfile }}" + command: "tail -2 {{ pre_audit_outfile }}" + register: pre_audit + changed_when: false + + - name: Pre Audit | Capture pre-audit result + set_fact: + pre_audit_summary: "{{ pre_audit.stdout_lines }}" + when: + - audit_format == "documentation" diff --git a/tasks/prelim.yml b/tasks/prelim.yml new file mode 100644 index 00000000..5451c31d --- /dev/null +++ b/tasks/prelim.yml @@ -0,0 +1,119 @@ +--- +# Preliminary tasks that should always be run +# List users in order to look files inside each home directory +- name: "PRELIM | List users accounts" + command: "awk -F: '{print $1}' /etc/passwd" + args: + warn: no + changed_when: no + check_mode: no + register: users + +- name: "PRELIM | Gather accounts with empty password fields" + shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" + args: + warn: no + changed_when: no + check_mode: no + register: empty_password_accounts + +- 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: no + changed_when: no + check_mode: no + register: uid_zero_accounts_except_root + +- name: "PRELIM | Gather system-wide crypto-policy" + shell: update-crypto-policies --show + args: + warn: no + changed_when: no + check_mode: no + register: system_wide_crypto_policy + +- name: "PRELIM | if systemd coredump" + stat: + path: /etc/systemd/coredump.conf + register: systemd_coredump + when: + - rhel9cis_rule_1_6_1 + +- name: "PRELIM | Section 1.1 | Create list of mount points" + set_fact: + mount_names: "{{ ansible_mounts | map(attribute='mount') | list }}" + +- name: "PRELIM | Section 4.1 | Configure System Accounting (auditd)" + package: + name: audit + state: present + when: rhel9cis_level_2 + +- 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: no + register: priv_procs + tags: + - always + +- name: "PRELIM | Section 5.1 | Configure cron" + package: + name: cronie + state: present + +- name: "PRELIM | Install authconfig" + package: + name: authconfig + state: present + when: + - rhel9cis_use_authconfig + - rhel9cis_rule_5_3_1 or + rhel9cis_rule_5_3_2 or + rhel9cis_rule_5_3_3 + +- name: "PRELIM | Set facts based on boot type" + block: + - name: "PRELIM | Check whether machine is UEFI-based" + stat: + path: /sys/firmware/efi + register: rhel_09_efi_boot + + - name: "PRELIM | AUDIT | set legacy boot and grub path | Bios" + 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: + grub2_path: /etc/grub2-efi.cfg + when: rhel_09_efi_boot.stat.exists + +# - name: debug legacy boot var +# debug: +# msg: | +# legacy_boot={{ rhel9cis_legacy_boot }} +# grub2_path={{ grub2_path }} + +- name: "PRELIM | AUDIT | Ensure permissions on bootloader config are configured | Get grub config file stats" + stat: + path: "{{ grub2_path }}" + changed_when: false + register: grub_cfg + +# - name: debug grub stat +# debug: +# var: grub_cfg.stat + +- name: "PRELIM | Check for rhnsd service" + shell: "systemctl show rhnsd | grep LoadState | cut -d = -f 2" + changed_when: false + check_mode: false + register: rhnsd_service_status + when: + - rhel9cis_rule_1_2_2 + tags: + - skip_ansible_lint diff --git a/tasks/section_1/cis_1.1.1.x.yml b/tasks/section_1/cis_1.1.1.x.yml new file mode 100644 index 00000000..fa381ad4 --- /dev/null +++ b/tasks/section_1/cis_1.1.1.x.yml @@ -0,0 +1,102 @@ +--- + +- name: "1.1.1.1 | L1 | PATCH | Ensure mounting of cramfs filesystems is disabled" + block: + - name: "1.1.1.1 | L1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Edit modprobe config" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install cramfs(\\s|$)" + line: "install cramfs /bin/true" + create: yes + mode: 0600 + + - name: "1.1.1.1 | L1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Disable cramfs" + modprobe: + name: cramfs + state: absent + when: ansible_connection != 'docker' + when: + - rhel9cis_rule_1_1_1_1 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.1.1.1 + - cramfs + +- name: "1.1.1.2 | L2 | PATCH | Ensure mounting of vFAT filesystems is limited" + block: + - name: "1.1.1.2 | L2 | PATCH | Ensure mounting of vFAT filesystems is limited | Edit modprobe config" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install vfat(\\s|$)" + line: "install vfat /bin/true" + create: yes + mode: 0600 + + - name: "1.1.1.2 | L2 | PATCH | Ensure mounting of vFAT filesystems is limited | Disable vFAT" + modprobe: + name: vfat + state: absent + when: ansible_connection != 'docker' + when: + - rhel9cis_rule_1_1_1_2 + - rhel9cis_legacy_boot + tags: + - level2-server + - level2-workstation + - scored + - patch + - rule_1.1.1.2 + - vfat + +- name: "1.1.1.3 | L1 | PATCH | Ensure mounting of squashfs filesystems is disabled" + block: + - name: "1.1.1.3 | L1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Edit modprobe config" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install squashfs(\\s|$)" + line: "install squashfs /bin/true" + create: yes + mode: 0600 + + - name: "1.1.1.3 | L1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Disable squashfs" + modprobe: + name: squashfs + state: absent + when: ansible_connection != 'docker' + when: + - rhel9cis_rule_1_1_1_3 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.1.1.3 + - squashfs + +- name: "1.1.1.4 | L1 | PATCH | Ensure mounting of udf filesystems is disabled" + block: + - name: "1.1.1.4 | L1 | PATCH | Ensure mounting of udf filesystems is disable | Edit modprobe config" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install udf(\\s|$)" + line: "install udf /bin/true" + create: yes + mode: 0600 + + - name: "1.1.1.4 | L1 | PATCH | Ensure mounting of udf filesystems is disable | Disable udf" + modprobe: + name: udf + state: absent + when: ansible_connection != 'docker' + when: + - rhel9cis_rule_1_1_1_4 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.1.1.4 + - udf diff --git a/tasks/section_1/cis_1.1.x.yml b/tasks/section_1/cis_1.1.x.yml new file mode 100644 index 00000000..65e2260d --- /dev/null +++ b/tasks/section_1/cis_1.1.x.yml @@ -0,0 +1,363 @@ +--- + +- name: | + "SCORED | 1.1.2 | PATCH | Ensure /tmp is configured" + "SCORED | 1.1.3 | PATCH | Ensure nodev option set on /tmp partition" + "SCORED | 1.1.4 | PATCH | Ensure nosuid option set on /tmp partition" + "SCORED | 1.1.5 | PATCH | Ensure noexec option set on /tmp partition" + "via fstab" + mount: + name: /tmp + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_4 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_5 %}nosuid{% endif %} + notify: remount tmp + loop: "{{ ansible_mounts }}" + when: + - item.mount == "/tmp" + - not rhel9cis_tmp_svc + - rhel9cis_rule_1_1_2 or + rhel9cis_rule_1_1_3 or + rhel9cis_rule_1_1_4 or + rhel9cis_rule_1_1_5 + tags: + - level1-server + - level1-workstation + - scored + - patch + - mounts + - rule_1.1.3 + - rule_1.1.4 + - rule_1.1.5 + +- name: | + "SCORED | 1.1.2 | PATCH | Ensure /tmp is configured" + "SCORED | 1.1.3 | PATCH | Ensure nodev option set on /tmp partition" + "SCORED | 1.1.4 | PATCH | Ensure nosuid option set on /tmp partition" + "SCORED | 1.1.5 | PATCH | Ensure noexec option set on /tmp partition" + "via systemd" + 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 + when: + - rhel9cis_tmp_svc + - rhel9cis_rule_1_1_2 or + rhel9cis_rule_1_1_3 or + rhel9cis_rule_1_1_4 or + rhel9cis_rule_1_1_5 + tags: + - level1-server + - level1-workstation + - scored + - patch + - mounts + - rule_1.1.3 + - rule_1.1.4 + - rule_1.1.5 + +- name: "1.1.6 | L2 | AUDIT | Ensure separate partition exists for /var" + block: + - name: "1.1.6 | L2 | AUDIT | Ensure separate partition exists for /var | Absent" + 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.6 | L2 | AUDIT | Ensure separate partition exists for /var | Present" + debug: + msg: "Congratulations: {{ required_mount }} exists." + when: + - required_mount in mount_names + vars: + required_mount: '/var' + when: + - rhel9cis_rule_1_1_6 + tags: + - level2-server + - level2-workstation + - scored + - patch + - mounts + - rule_1.1.6 + +- name: "1.1.7 | L2 | AUDIT | Ensure separate partition exists for /var/tmp | skips if mount absent" + block: + - name: "1.1.7 | L2 | AUDIT | Ensure separate partition exists for /var/tmp | Absent" + 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.7 | L2 | 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 + vars: + required_mount: '/var/tmp' + when: + - rhel9cis_rule_1_1_7 + tags: + - level2-server + - level2-workstation + - scored + - audit + - mounts + - rule_1.1.7 + +- name: | + "1.1.8 | L1 | PATCH | Ensure nodev option set on /var/tmp partition | skips if mount absent" + "1.1.9 | L1 | PATCH | Ensure nosuid option set on /var/tmp partition | skips if mount absent" + "1.1.10 | L1 | PATCH | Ensure noexec option set on /var/tmp partition | skips if mount absent" + mount: + name: /var/tmp + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_10 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_8 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_9 %}nosuid{% endif %} + loop: "{{ ansible_mounts }}" + when: + - var_tmp_mount_present is defined + - item.mount == "/var/tmp" + - rhel9cis_rule_1_1_7 # This is required so the check takes place + - rhel9cis_rule_1_1_8 or + rhel9cis_rule_1_1_9 or + rhel9cis_rule_1_1_10 + tags: + - level1-server + - level1-workstation + - scored + - patch + - mounts + - skip_ansible_lint + +- name: "1.1.11 | L2 | AUDIT | Ensure separate partition exists for /var/log" + block: + - name: "1.1.11 | L2 | AUDIT | Ensure separate partition exists for /var/log | Absent" + 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.11 | L2 | AUDIT | Ensure separate partition exists for /var/log | Present" + debug: + msg: "Congratulations: {{ required_mount }} exists." + when: + - required_mount in mount_names + vars: + required_mount: '/var/log' + when: + - rhel9cis_rule_1_1_11 + tags: + - level2-server + - level2-workstation + - scored + - audit + - mounts + - rule_1.1.11 + - skip_ansible_lint + +- name: "1.1.12 | L2 | AUDIT | Ensure separate partition exists for /var/log/audit" + block: + - name: "1.1.12 | L2 | AUDIT | Ensure separate partition exists for /var/log/audit | Absent" + 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.12 | L2 | AUDIT | Ensure separate partition exists for /var/log/audit | Present" + debug: + msg: "Congratulations: {{ required_mount }} exists." + when: + - required_mount in mount_names + vars: + required_mount: '/var/log/audit' + when: + - rhel9cis_rule_1_1_12 + tags: + - level2-server + - level2-workstation + - scored + - audit + - mounts + - rule_1.1.12 + + +- name: "1.1.13 | L2 | AUDIT | Ensure separate partition exists for /home" + block: + - name: "1.1.13 | L2 | AUDIT | Ensure separate partition exists for /home | Absent" + 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.13 | L2 | AUDIT | Ensure separate partition exists for /home | Present" + debug: + msg: "Congratulations: {{ required_mount }} exists." + register: home_mount_present + when: + - required_mount in mount_names + vars: + required_mount: '/home' + when: + - rhel9cis_rule_1_1_13 + tags: + - level2-server + - level2-workstation + - scored + - audit + - mounts + - rule_1.1.13 + - skip_ansible_lint + +- name: "1.1.14 | L1 | PATCH | Ensure nodev option set on /home partition | skips if mount absent" + mount: + name: /home + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_14 %}nodev{% endif %} + loop: "{{ ansible_mounts }}" + when: + - home_mount_present is defined + - item.mount == "/home" + - rhel9cis_rule_1_1_14 + tags: + - level1-server + - level1-workstation + - scored + - patch + - mounts + - rule_1.1.13 + - skip_ansible_lint + +- name: | + "1.1.15 | L1 | PATCH | Ensure nodev option set on /dev/shm partition | skips if mount absent + 1.1.16 | L1 | PATCH | Ensure nosuid option set on /dev/shm partition | skips if mount absent + 1.1.17 | L1 | PATCH | Ensure noexec option set on /dev/shm partition | skips if mount absent" + block: + - name: | + "1.1.15 | L1 | AUDIT | Ensure nodev option set on /dev/shm partition | Check for /dev/shm existence + 1.1.16 | L1 | AUDIT | Ensure nosuid option set on /dev/shm partition | Check for /dev/shm existence + 1.1.17 | L1 | 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: no + register: rhel9cis_1_1_15_dev_shm_status + + - name: | + "1.1.15 | L1 | PATCH | Ensure nodev option set on /dev/shm partition | skips if mount absent + 1.1.16 | L1 | PATCH | Ensure nosuid option set on /dev/shm partition | skips if mount absent + 1.1.17 | L1 | PATCH | Ensure noexec option set on /dev/shm partition | skips if mount absent" + mount: + name: /dev/shm + src: tmpfs + fstype: tmpfs + state: mounted + opts: defaults,{% if rhel9cis_rule_1_1_17 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_15 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_16 %}nosuid{% endif %} + when: "'dev/shm' in rhel9cis_1_1_15_dev_shm_status.stdout" + when: + - rhel9cis_rule_1_1_15 or + rhel9cis_rule_1_1_16 or + rhel9cis_rule_1_1_17 + tags: + - level1-server + - level1-workstation + - scored + - patch + - mounts + - rule_1.1.15 + - rule_1.1.16 + - rule_1.1.17 + +- name: | + "1.1.18 | L1 | PATCH | Ensure nodev option set on removable media partitions" + "1.1.19 | L1 | PATCH | Ensure nosuid option set on removable media partitions" + "1.1.20 | L1 | PATCH | Ensure noexec option set on removable media partitions" + debug: + msg: "--> Not relevant" + changed_when: false + when: + - rhel9cis_rule_1_1_18 or + rhel9cis_rule_1_1_19 or + rhel9cis_rule_1_1_20 + tags: + - level1-server + - level1-workstation + - notscored + - audit + - mounts + - rule_1.1.18 + - rule_1.1.19 + - rule_1.1.20 + +- name: "1.1.21 | L1 | 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: no + changed_when: false + failed_when: false + when: + - rhel9cis_rule_1_1_21 + tags: + - skip_ansible_lint + - level1-server + - level1-workstation + - patch + - stickybits + - permissons + - rule_1.1.21 + +- name: "1.1.22 | L1 | PATCH | Disable Automounting" + service: + name: autofs + enabled: no + when: + - not rhel9cis_allow_autofs + - "'autofs' in ansible_facts.packages" + - rhel9cis_rule_1_1_22 + tags: + - level1-server + - level2-workstation + - patch + - mounts + - automounting + - rule_1.1.22 + +- name: "1.1.23 | L1 | PATCH | Disable USB Storage" + block: + - name: "1.1.23 | L1 | PATCH | Disable USB Storage | Edit modprobe config" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install usb-storage(\\s|$)" + line: "install usb-storage /bin/true" + create: yes + owner: root + group: root + mode: 0600 + + - name: "1.1.23 | L1 | PATCH | Disable USB Storage | Edit modprobe config" + modprobe: + name: usb-storage + state: absent + when: + - rhel9cis_rule_1_1_23 + tags: + - level1-server + - level2-workstation + - patch + - mounts + - removable_storage + - rule_1.1.23 diff --git a/tasks/section_1/cis_1.10.yml b/tasks/section_1/cis_1.10.yml new file mode 100644 index 00000000..91b8cade --- /dev/null +++ b/tasks/section_1/cis_1.10.yml @@ -0,0 +1,15 @@ +--- + +- name: "1.10 | L1 | PATCH | Ensure system-wide crypto policy is not legacy" + shell: | + update-crypto-policies --set "{{ rhel9cis_crypto_policy }}" + update-crypto-policies + when: + - rhel9cis_rule_1_10 + - system_wide_crypto_policy['stdout'] == 'LEGACY' + tags: + - level1-server + - level1-workstation + - no system_is_ec2 + - patch + - rule_1.10 diff --git a/tasks/section_1/cis_1.11.yml b/tasks/section_1/cis_1.11.yml new file mode 100644 index 00000000..34245b18 --- /dev/null +++ b/tasks/section_1/cis_1.11.yml @@ -0,0 +1,15 @@ +--- + +- name: "1.11 | L2 | PATCH | Ensure system-wide crypto policy is FUTURE or FIPS" + shell: | + update-crypto-policies --set "{{ rhel9cis_crypto_policy }}" + update-crypto-policies + when: + - rhel9cis_rule_1_11 + - system_wide_crypto_policy['stdout'] not in rhel9cis_allowed_crypto_policies + tags: + - level2-server + - level2-workstation + - not system_is_ec2 + - patch + - rule_1.11 diff --git a/tasks/section_1/cis_1.2.x.yml b/tasks/section_1/cis_1.2.x.yml new file mode 100644 index 00000000..07a8285a --- /dev/null +++ b/tasks/section_1/cis_1.2.x.yml @@ -0,0 +1,100 @@ +--- + +- name: "1.2.1 | L1 | 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 + - notscored + - patch + - rule_1.2.1 + - skip_ansible_lint # Added as no_log still errors on ansuible-lint + +- name: "1.2.2 | L1 | PATCH | Disable the rhnsd Daemon" + service: + name: rhnsd + state: stopped + enabled: no + masked: true + when: + - ansible_distribution == "RedHat" + - rhnsd_service_status.stdout == "loaded" and not rhel9cis_rhnsd_required + - rhel9cis_rule_1_2_2 + tags: + - level1-server + - level1-workstation + - notscored + - patch + - rule_1.2.2 + +- name: "1.2.3 | L1 | AUDIT | Ensure GPG keys are configured" + command: gpg --quiet --with-fingerprint /etc/pki/rpm-gpg/RPM-GPG-KEY-{{ ansible_distribution|lower }}-release + when: + - rhel9cis_rule_1_2_3 + - ansible_distribution == "RedHat" + tags: + - level1-server + - level1-workstation + - notscored + - patch + - rule_1.2.3 + +- name: "1.2.4 | L1 | PATCH | Ensure gpgcheck is globally activated" + block: + - name: "1.2.4 | L1 | AUDIT | Ensure gpgcheck is globally activated | Find repos" + find: + paths: /etc/yum.repos.d + patterns: "*.repo" + register: yum_repos + changed_when: false + + - name: "1.2.4 | L1 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" + replace: + name: "{{ item.path }}" + regexp: "^gpgcheck=0" + replace: "gpgcheck=1" + with_items: + - "{{ yum_repos.files }}" + when: + - rhel9cis_rule_1_2_4 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.2.4 + +- name: "1.2.5 | L1 | Ensure package manager repositories are configured" + block: + - name: "1.2.5 | L1 | AUDIT | Ensure package manager repositories are configured | Get repo list" + command: dnf repolist + changed_when: false + failed_when: false + register: dnf_configured + check_mode: no + args: + warn: false + + - name: "1.2.5 | L1 | AUDIT | Ensure package manager repositories are configured | Display repo list" + debug: + msg: + - "Alert! Below are the configured repos. Please review and make sure all align with site policy" + - "{{ dnf_configured.stdout_lines }}" + when: + - rhel9cis_rule_1_2_5 + tags: + - level1-server + - level1-workstation + - notscored + - patch + - rule_1.2.5 + - skip_ansible_lint diff --git a/tasks/section_1/cis_1.3.x.yml b/tasks/section_1/cis_1.3.x.yml new file mode 100644 index 00000000..8456bc13 --- /dev/null +++ b/tasks/section_1/cis_1.3.x.yml @@ -0,0 +1,44 @@ +--- + +- name: "1.3.1 | L1 | PATCH | Ensure sudo is installed" + package: + name: sudo + state: present + when: + - rhel9cis_rule_1_3_1 + tags: + - level1-server + - level1-workstation + - scored + - sudo + - patch + - rule_1.3.1 + +- name: "1.3.2 | L1 | PATCH | Ensure sudo commands use pty" + lineinfile: + dest: /etc/sudoers + line: "Defaults use_pty" + state: present + when: + - rhel9cis_rule_1_3_2 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.3.2 + +- name: "1.3.3 | L1 | PATCH | Ensure sudo log file exists" + lineinfile: + dest: /etc/sudoers + regexp: '^Defaults logfile=' + line: 'Defaults logfile="{{ rhel9cis_varlog_location }}"' + state: present + when: + - rhel9cis_rule_1_3_3 + tags: + - level1-server + - level1-workstation + - scored + - 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 new file mode 100644 index 00000000..5956ae6e --- /dev/null +++ b/tasks/section_1/cis_1.4.x.yml @@ -0,0 +1,50 @@ +--- + +- name: "1.4.1 | L1 | PATCH | Ensure AIDE is installed" + block: + - name: "1.4.1 | L1 | PATCH | Ensure AIDE is installed | Install AIDE" + package: + name: aide + state: present + + - name: "1.4.1 | L1 | PATCH | Ensure AIDE is installed | Configure AIDE" + command: /usr/sbin/aide --init -B 'database_out=file:/var/lib/aide/aide.db.gz' + changed_when: false + failed_when: false + async: 45 + poll: 0 + args: + creates: /var/lib/aide/aide.db.gz + when: not ansible_check_mode + when: + - rhel9cis_config_aide + - rhel9cis_rule_1_4_1 + tags: + - level1-server + - level1-workstation + - scored + - aide + - patch + - rule_1.4.1 + +- name: "1.4.2 | L1 | PATCH | Ensure filesystem integrity is regularly checked" + cron: + name: Run AIDE integrity check + cron_file: "{{ rhel9cis_aide_cron['cron_file'] }}" + user: "{{ rhel9cis_aide_cron['cron_user'] }}" + minute: "{{ rhel9cis_aide_cron['aide_minute'] | default('0') }}" + hour: "{{ rhel9cis_aide_cron['aide_hour'] | default('5') }}" + day: "{{ rhel9cis_aide_cron['aide_day'] | default('*') }}" + month: "{{ rhel9cis_aide_cron['aide_month'] | default('*') }}" + weekday: "{{ rhel9cis_aide_cron['aide_weekday'] | default('*') }}" + job: "{{ rhel9cis_aide_cron['aide_job'] }}" + when: + - rhel9cis_rule_1_4_2 + tags: + - level1-server + - level1-workstation + - scored + - aide + - file_integrity + - patch + - rule_1.4.2 diff --git a/tasks/section_1/cis_1.5.x.yml b/tasks/section_1/cis_1.5.x.yml new file mode 100644 index 00000000..5b169468 --- /dev/null +++ b/tasks/section_1/cis_1.5.x.yml @@ -0,0 +1,76 @@ +--- + +- name: "1.5.1 | L1 | PATCH | Ensure permissions on bootloader config are configured" + block: + - name: "1.5.1 | L1 | PATCH | Ensure permissions on bootloader config are configured" + file: + path: "{{ grub_cfg.stat.lnk_source }}" + owner: root + group: root + mode: 0600 + + - name: "1.5.1 | L1 | 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' + loop: "{{ ansible_mounts }}" + when: + - not rhel9cis_legacy_boot + - item.mount == "/boot/efi" + when: + - rhel9cis_rule_1_5_1 + - grub_cfg.stat.exists + - grub_cfg.stat.islnk + tags: + - level1-server + - level1-workstation + - scored + - grub + - patch + - rule_1.5.1 + +- name: "1.5.2 | L1 | PATCH | Ensure bootloader password is set" + copy: + dest: /boot/grub2/user.cfg + content: "GRUB2_PASSWORD={{ rhel9cis_bootloader_password_hash }}" + owner: root + group: root + mode: 0600 + notify: grub2cfg + when: + - rhel9cis_set_boot_pass + - grub_pass is defined and grub_pass.passhash is defined + - grub_pass.passhash | length > 0 + - rhel9cis_rule_1_5_2 + tags: + - level1-server + - level1-workstation + - scored + - grub + - patch + - rule_1.5.2 + +- name: "1.5.3 | L1 | PATCH | Ensure authentication required for single user mode" + block: + - name: "1.5.3 | L1 | PATCH | Ensure authentication required for single user mode | Emergency service" + lineinfile: + dest: /usr/lib/systemd/system/emergency.service + regexp: '/sbin/sulogin' + line: 'ExecStart=-/usr/lib/systemd/systemd-sulogin-shell emergency' + + - name: "1.5.3 | L1 | PATCH | Ensure authentication required for single user mode | Rescue service" + lineinfile: + dest: /usr/lib/systemd/system/rescue.service + regexp: '/sbin/sulogin' + line: 'ExecStart=-/usr/lib/systemd/systemd-sulogin-shell rescue' + when: + - rhel9cis_rule_1_5_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.5.3 diff --git a/tasks/section_1/cis_1.6.x.yml b/tasks/section_1/cis_1.6.x.yml new file mode 100644 index 00000000..1b37c0de --- /dev/null +++ b/tasks/section_1/cis_1.6.x.yml @@ -0,0 +1,54 @@ +--- + +- name: "1.6.1 | L1 | PATCH | Ensure core dumps are restricted" + block: + - name: "1.6.1 | L1 | Ensure core dumps are restricted | Update limits.conf file" + lineinfile: + state: present + dest: /etc/security/limits.conf + regexp: '^#?\\*.*core' + line: '* hard core 0' + insertbefore: '^# End of file' + + - name: "1.6.1 | L1 | PATCH | Ensure core dumps are restricted | Set active kernel parameter" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + + - name: "1.6.1 | L1 | PATCH | Ensure core dumps are restricted | if systemd coredump" + lineinfile: + path: /etc/systemd/coredump.conf + regexp: "{{ item.regexp }}" + line: "{{ item.regexp }}{{ item.line }}" + state: present + with_items: + - {'regexp': 'Storage=', 'line': 'none'} + - {'regexp': 'ProcessSizeMax=', 'line': '0'} + notify: + - systemd_daemon_reload + when: + - systemd_coredump.stat.exists + when: + - rhel9cis_rule_1_6_1 + tags: + - level1-server + - level1-workstation + - scored + - sysctl + - patch + - rule_1.6.1 + +- name: "1.6.2 | L1 | PATCH | Ensure address space layout randomization (ASLR) is enabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + when: + - rhel9cis_rule_1_6_2 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.6.2 diff --git a/tasks/section_1/cis_1.7.1.x.yml b/tasks/section_1/cis_1.7.1.x.yml new file mode 100644 index 00000000..ea1f8056 --- /dev/null +++ b/tasks/section_1/cis_1.7.1.x.yml @@ -0,0 +1,115 @@ +--- + +- name: "1.7.1.1 | L2 | PATCH | Ensure SELinux is installed" + package: + name: libselinux + state: present + when: + - rhel9cis_rule_1_7_1_1 + tags: + - level2-server + - level2-workstation + - scored + - patch + - rule_1.7.1.1 + +- name: "1.7.1.2 | L2 | PATCH | Ensure SELinux is not disabled in bootloader configuration" + replace: + dest: /etc/default/grub + regexp: '(selinux|enforcing)\s*=\s*0\s*' + replace: '' + register: selinux_grub_patch + ignore_errors: yes + notify: grub2cfg + when: + - rhel9cis_rule_1_7_1_2 + tags: + - level2-server + - level2-workstation + - scored + - patch + - rule_1.7.1.2 + +- name: "1.7.1.3 | L2 | PATCH | Ensure SELinux policy is configured" + selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: enforcing + when: + - not rhel9cis_selinux_disable + - rhel9cis_rule_1_7_1_3 + tags: + - level2-server + - level2-workstation + - scored + - selinux + - patch + - rule_1.7.1.3 + +- name: "1.7.1.4 | L2 | PATCH | Ensure the SELinux state is enforcing" + selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: enforcing + when: + - not rhel9cis_selinux_disable + - rhel9cis_rule_1_7_1_4 + tags: + - level2-server + - level2-workstation + - scored + - selinux + - patch + - rule_1.7.1.4 + +- name: "1.7.1.5 | L2 | AUDIT | Ensure no unconfined daemons exist" + block: + - name: "1.7.1.5 | L2 | AUDIT | Ensure no unconfined daemons exist | Find the unconfined daemons" + shell: ps -eZ | grep unconfined_service_t | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }' + register: rhelcis_1_7_1_5_unconf_daemons + failed_when: false + changed_when: false + + - name: "1.7.1.5 | L2 | AUDIT | Ensure no unconfined daemons exist | Message on no unconfined daemones" + debug: + msg: "Good News! There are no unconfined daemons found on your system" + when: rhelcis_1_7_1_5_unconf_daemons.stdout | length == 0 + + - name: "1.7.1.5 | L2 | AUDIT | Ensure no unconfined daemons exist | Message on unconfined daemones" + debug: + msg: "Warning! You have unconfined daemons: {{ rhelcis_1_7_1_5_unconf_daemons.stdout_lines }}" + when: rhelcis_1_7_1_5_unconf_daemons.stdout | length > 0 + when: + - rhel9cis_rule_1_7_1_5 + tags: + - level2-server + - level2-workstation + - audit + - rule_1.7.1.5 + +- name: "1.7.1.6 | L2 | PATCH | Ensure SETroubleshoot is not installed" + package: + name: setroubleshoot + state: absent + when: + - rhel9cis_rule_1_7_1_6 + - "'setroubleshoot' in ansible_facts.packages" + tags: + - level2-server + - scored + - selinux + - patch + - rule_1.7.1.6 + +- name: "1.7.1.7 | L2 | PATCH | Ensure the MCS Translation Service (mcstrans) is not installed" + package: + name: mcstrans + state: absent + when: + - rhel9cis_rule_1_7_1_7 + tags: + - level2-server + - level2-workstation + - scored + - patch + - rule_1.7.1.7 diff --git a/tasks/section_1/cis_1.8.1.x.yml b/tasks/section_1/cis_1.8.1.x.yml new file mode 100644 index 00000000..d8cbec37 --- /dev/null +++ b/tasks/section_1/cis_1.8.1.x.yml @@ -0,0 +1,96 @@ +--- + +- name: "1.8.1.1 | L1 | PATCH | Ensure message of the day is configured properly" + template: + src: etc/motd.j2 + dest: /etc/motd + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_1 + tags: + - level1-server + - level1-workstation + - banner + - patch + - rule_1.8.1.1 + +- name: "1.8.1.2 | L1 | PATCH | Ensure local login warning banner is configured properly" + template: + src: etc/issue.j2 + dest: /etc/issue + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.8.1.2 + +- name: "1.8.1.3 | L1 | PATCH | Ensure remote login warning banner is configured properly" + template: + src: etc/issue.net.j2 + dest: /etc/issue.net + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_3 + tags: + - level1-server + - level1-workstation + - banner + - patch + - rule_1.8.1.3 + +- name: "1.8.1.4 | L1 | PATCH | Ensure permissions on /etc/motd are configured" + file: + dest: /etc/motd + state: file + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_4 + tags: + - level1-server + - level1-workstation + - perms + - patch + - rule_1.8.1.4 + +- name: "1.8.1.5 | L1 | PATCH | Ensure permissions on /etc/issue are configured" + file: + dest: /etc/issue + state: file + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_5 + tags: + - level1-server + - level1-workstation + - perms + - patch + - rule_1.8.1.5 + +- name: "1.8.1.6 | L1 | PATCH | Ensure permissions on /etc/issue.net are configured" + file: + dest: /etc/issue.net + state: file + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_1_8_1_6 + tags: + - level1-server + - level1-workstation + - perms + - patch + - rule_1.8.1.6 diff --git a/tasks/section_1/cis_1.8.2.yml b/tasks/section_1/cis_1.8.2.yml new file mode 100644 index 00000000..2062c69f --- /dev/null +++ b/tasks/section_1/cis_1.8.2.yml @@ -0,0 +1,27 @@ +--- + +- name: "1.8.2 | L1 | PATCH | Ensure GDM login banner is configured" + lineinfile: + dest: "{{ item.file }}" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + state: present + create: yes + owner: root + group: root + mode: 0644 + 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 }}' " } + when: + - rhel9cis_gui + - rhel9cis_rule_1_8_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.8.2 diff --git a/tasks/section_1/cis_1.9.yml b/tasks/section_1/cis_1.9.yml new file mode 100644 index 00000000..a67d5dbd --- /dev/null +++ b/tasks/section_1/cis_1.9.yml @@ -0,0 +1,15 @@ +--- + +- name: "1.9 | L1 | PATCH | Ensure updates, patches, and additional security software are installed" + package: + name: "*" + state: latest + when: + - rhel9cis_rule_1_9 + - not system_is_ec2 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.9 + - skip_ansible_lint diff --git a/tasks/section_1/main.yml b/tasks/section_1/main.yml new file mode 100644 index 00000000..5c7b083a --- /dev/null +++ b/tasks/section_1/main.yml @@ -0,0 +1,42 @@ +--- + +- name: "SECTION | 1.1 | FileSystem Configurations\n + SECTION | 1.1.1.x | Disable unused filesystems" + include: cis_1.1.1.x.yml +- include: cis_1.1.x.yml + +- name: "SECTION | 1.2 | Configure Software Updates" + include: cis_1.2.x.yml + +- name: "SECTION | 1.3 | Configure sudo" + include: cis_1.3.x.yml + +- name: "SECTION | 1.4 | Filesystem Integrity" + include: cis_1.4.x.yml + when: rhel9cis_config_aide + +- name: "SECTION | 1.5 | Secure Boot Settings" + include: cis_1.5.x.yml + +- name: "SECTION | 1.6 | Additional Process Hardening" + include: cis_1.6.x.yml + +- name: "SECTION | 1.7 | bootloader and Mandatory Access Control" + include: cis_1.7.1.x.yml + when: not rhel9cis_selinux_disable + +- name: "SECTION | 1.8 | Warning Banners" + include: cis_1.8.1.x.yml + +- name: "SECTION | 1.9 | Updated and Patches" + include: cis_1.9.yml + +- name: "SECTION | 1.10 | Crypto policies" + include: cis_1.10.yml + when: + - not system_is_ec2 + +- name: "SECTION | 1.11 | FIPS/FUTURE Crypto policies" + include: cis_1.11.yml + when: + - not system_is_ec2 diff --git a/tasks/section_2/cis_2.1.1.yml b/tasks/section_2/cis_2.1.1.yml new file mode 100644 index 00000000..5b563645 --- /dev/null +++ b/tasks/section_2/cis_2.1.1.yml @@ -0,0 +1,14 @@ +--- + +- name: "2.1.1 | L1 | PATCH | Ensure xinetd is not installed" + package: + name: xinetd + state: absent + when: + - rhel9cis_rule_2_1_1 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_2.1.1 diff --git a/tasks/section_2/cis_2.2.1.x.yml b/tasks/section_2/cis_2.2.1.x.yml new file mode 100644 index 00000000..fbdf9c1e --- /dev/null +++ b/tasks/section_2/cis_2.2.1.x.yml @@ -0,0 +1,42 @@ +--- + +- name: "2.2.1.1 | L1 | PATCH | Ensure time synchronization is in use - service install" + package: + name: "{{ rhel9cis_time_synchronization }}" + state: present + when: + - rhel9cis_rule_2_2_1_1 + - not rhel9cis_system_is_container + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.1.1 + +- name: "2.2.1.2 | L1 | PATCH | Ensure chrony is configured" + block: + - name: "2.2.1.2 | L1 | PATCH | Ensure chrony is configured | Set configuration" + template: + src: chrony.conf.j2 + dest: /etc/chrony.conf + owner: root + group: root + mode: 0644 + + - name: "2.2.1.2 | L1 | PATCH | Ensure chrony is configured | modify /etc/sysconfig/chronyd | 1" + lineinfile: + dest: /etc/sysconfig/chronyd + regexp: "^(#)?OPTIONS" + line: "OPTIONS=\"-u chrony\"" + state: present + create: yes + mode: 0644 + when: + - rhel9cis_time_synchronization == "chrony" + - rhel9cis_rule_2_2_1_2 + - not rhel9cis_system_is_container + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.1.2 diff --git a/tasks/section_2/cis_2.2.x.yml b/tasks/section_2/cis_2.2.x.yml new file mode 100644 index 00000000..f6203dab --- /dev/null +++ b/tasks/section_2/cis_2.2.x.yml @@ -0,0 +1,288 @@ +--- + +- name: "2.2.2 | L1 | PATCH | Ensure X Window System is not installed" + block: + - name: "2.2.2 | L1 | AUDIT | Ensure X Window System is not installed | capture xorg-x11 packages" + shell: rpm -qa | grep xorg-x11 + args: + warn: no + failed_when: xorg_x11_installed.rc >=2 + check_mode: no + changed_when: false + register: xorg_x11_installed + + - name: "2.2.2 | L1 | PATCH | Ensure X Window System is not installed | remove packages if found" + shell: "dnf remove {{ item }}" + args: + warn: no + with_items: + - xorg_x11_installed.stdout_lines + when: xorg_x11_installed.stdout | length > 0 + when: + - not rhel9cis_xwindows_required + - rhel9cis_rule_2_2_2 + tags: + - level1-server + - scored + - xwindows + - patch + - rule_2.2.2 + +- name: "2.2.3 | L1 | PATCH | Ensure rsync service is not enabled " + service: + name: rsyncd + state: stopped + enabled: no + when: + - not rhel9cis_rsyncd_server + - "'rsyncd' in ansible_facts.packages" + - rhel9cis_rule_2_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.3 + +- name: "2.2.4 | L1 | PATCH | Ensure Avahi Server is not enabled" + service: + name: avahi-daemon + state: stopped + enabled: no + when: + - not rhel9cis_avahi_server + - "'avahi' in ansible_facts.packages" + - rhel9cis_rule_2_2_4 + tags: + - level1-server + - level1-workstation + - scored + - avahi + - services + - patch + - rule_2.2.4 + +- name: "2.2.5 | L1 | PATCH | Ensure SNMP Server is not enabled" + service: + name: snmpd + state: stopped + enabled: no + when: + - not rhel9cis_snmp_server + - "'net-snmp' in ansible_facts.packages" + - rhel9cis_rule_2_2_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.5 + +- name: "2.2.6 | L1 | PATCH | Ensure HTTP Proxy Server is not enabled" + service: + name: squid + state: stopped + enabled: no + when: + - not rhel9cis_squid_server + - "'squid' in ansible_facts.packages" + - rhel9cis_rule_2_2_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.6 + +- name: "2.2.7 | L1 | PATCH | Ensure Samba is not enabled" + service: + name: smb + state: stopped + enabled: no + when: + - not rhel9cis_smb_server + - "'samba' in ansible_facts.packages" + - rhel9cis_rule_2_2_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.7 + +- name: "2.2.8 | L1 | PATCH | Ensure IMAP and POP3 server is not enabled" + service: + name: dovecot + state: stopped + enabled: no + when: + - not rhel9cis_dovecot_server + - "'dovecot' in ansible_facts.packages" + - rhel9cis_rule_2_2_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.8 + +- name: "2.2.9 | L1 | PATCH | Ensure HTTP server is not enabled" + service: + name: httpd + state: stopped + enabled: no + when: + - not rhel9cis_httpd_server + - "'httpd' in ansible_facts.packages" + - rhel9cis_rule_2_2_9 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.9 + +- name: "2.2.10 | L1 | PATCH | Ensure FTP Server is not enabled" + service: + name: vsftpd + state: stopped + enabled: no + when: + - not rhel9cis_vsftpd_server + - "'vsftpd' in ansible_facts.packages" + - rhel9cis_rule_2_2_10 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.10 + +- name: "2.2.11 | L1 | PATCH | Ensure DNS Server is not enabled" + service: + name: named + state: stopped + enabled: no + when: + - not rhel9cis_named_server + - "'bind' in ansible_facts.packages" + - rhel9cis_rule_2_2_11 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.11 + +- name: "2.2.12 | L1 | PATCH | Ensure NFS is not enabled" + service: + name: nfs-server + state: stopped + enabled: no + when: + - not rhel9cis_nfs_rpc_server + - "'nfs-utils' in ansible_facts.packages" + - rhel9cis_rule_2_2_12 + tags: + - level1-server + - level1-workstation + - scored + - nfs + - services + - patch + - rule_2.2.12 + +- name: "2.2.13 | L1 | PATCH | Ensure RPC is not enabled" + service: + name: rpcbind + state: stopped + enabled: no + when: + - not rhel9cis_nfs_rpc_server + - "'rpcbind' in ansible_facts.packages" + - rhel9cis_rule_2_2_13 + tags: + - level1-server + - level1-workstation + - scored + - rpc + - services + - patch + - rule_2.2.7 + +- name: "2.2.14 | L1 | PATCH | Ensure LDAP server is not enabled" + service: + name: slapd + state: stopped + enabled: no + when: + - not rhel9cis_ldap_server + - "'openldap-servers' in ansible_facts.packages" + - rhel9cis_rule_2_2_14 + tags: + - level1-server + - level1-workstation + - scored + - ldap + - services + - patch + - rule_2.2.6 + +- name: "2.2.15 | L1 | PATCH | Ensure DHCP Server is not enabled" + service: + name: dhcpd + state: stopped + enabled: no + when: + - not rhel9cis_dhcp_server + - "'dhcp' in ansible_facts.packages" + - rhel9cis_rule_2_2_15 + tags: + - level1-server + - level1-workstation + - scored + - dhcp + - services + - patch + - rule_2.2.15 + +- name: "2.2.16 | L1 | PATCH | Ensure CUPS is not enabled" + service: + name: cups + state: stopped + enabled: no + when: + - not rhel9cis_cups_server + - "'cups' in ansible_facts.packages" + - rhel9cis_rule_2_2_16 + tags: + - level1-server + - level2-workstation + - scored + - cups + - services + - patch + - rule_2.2.16 + +- name: "2.2.17 | L1 | PATCH | Ensure NIS Server is not enabled" + service: + name: ypserv + state: stopped + enabled: no + when: + - not rhel9cis_nis_server + - "'ypserv' in ansible_facts.packages" + - rhel9cis_rule_2_2_17 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.17 + +- name: "2.2.18 | L1 | PATCH | Ensure mail transfer agent is configured for local-only mode" + lineinfile: + dest: /etc/postfix/main.cf + regexp: "^(#)?inet_interfaces" + line: "inet_interfaces = loopback-only" + notify: restart postfix + when: + - not rhel9cis_is_mail_server + - "'postfix' in ansible_facts.packages" + - rhel9cis_rule_2_2_18 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.2.1 diff --git a/tasks/section_2/cis_2.3.x.yml b/tasks/section_2/cis_2.3.x.yml new file mode 100644 index 00000000..875eff8d --- /dev/null +++ b/tasks/section_2/cis_2.3.x.yml @@ -0,0 +1,43 @@ +--- + +- name: "2.3.1 | L1 | 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 + - patch + - rule_2.3.1 + +- name: "2.3.2 | L1 | PATCH | Ensure telnet client is not installed" + package: + name: telnet + state: absent + when: + - not rhel9cis_telnet_required + - "'telnet' in ansible_facts.packages" + - rhel9cis_rule_2_3_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.3.2 + +- name: "2.3.3 | L1 | PATCH | Ensure LDAP client is not installed" + package: + name: openldap-clients + state: absent + when: + - not rhel9cis_openldap_clients_required + - "'openldap-clients' in ansible_facts.packages" + - rhel9cis_rule_2_3_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_2.3.3 diff --git a/tasks/section_2/main.yml b/tasks/section_2/main.yml new file mode 100644 index 00000000..1c99c032 --- /dev/null +++ b/tasks/section_2/main.yml @@ -0,0 +1,13 @@ +--- + +- name: "SECTION | 2.1 | xinetd" + include: cis_2.1.1.yml + +- name: "SECTION | 2.2.1 | Time Synchronization" + include: cis_2.2.1.x.yml + +- name: "SECTION | 2.2 | Special Purpose Services" + include: cis_2.2.x.yml + +- name: "SECTION | 2.3 | Service Clients" + include: cis_2.3.x.yml diff --git a/tasks/section_3/cis_3.1.x.yml b/tasks/section_3/cis_3.1.x.yml new file mode 100644 index 00000000..ad692faf --- /dev/null +++ b/tasks/section_3/cis_3.1.x.yml @@ -0,0 +1,43 @@ +--- + +- name: "3.1.1 | L1 | PATCH | Ensure IP forwarding is disabled" + block: + - name: "3.1.1 | L1 | PATCH | Ensure IP forwarding is disabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + - sysctl flush ipv4 route table + + - name: "3.1.1 | L1 | PATCH | Ensure IP forwarding is disabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - sysctl flush ipv6 route table + - update sysctl + when: rhel9cis_ipv6_required + when: + - not rhel9cis_is_router + - rhel9cis_rule_3_1_1 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.1.1 + +- name: "3.1.2 | L1 | PATCH | Ensure packet redirect sending is disabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + - sysctl flush ipv4 route table + when: + - not rhel9cis_is_router + - rhel9cis_rule_3_1_2 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.1.2 diff --git a/tasks/section_3/cis_3.2.x.yml b/tasks/section_3/cis_3.2.x.yml new file mode 100644 index 00000000..ce855070 --- /dev/null +++ b/tasks/section_3/cis_3.2.x.yml @@ -0,0 +1,155 @@ +--- + +- name: "3.2.1 | L1 | PATCH | Ensure source routed packets are not accepted" + block: + - name: "3.2.1 | L1 | PATCH | Ensure source routed packets are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + - sysctl flush ipv4 route table + + - name: "3.2.1 | L1 | PATCH | Ensure source routed packets are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - sysctl flush ipv6 route table + - update sysctl + when: rhel9cis_ipv6_required + when: + - rhel9cis_rule_3_2_1 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.1 + +- name: "3.2.2 | L1 | PATCH | Ensure ICMP redirects are not accepted" + block: + - name: "3.2.2 | L1 | PATCH | Ensure ICMP redirects are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + - sysctl flush ipv4 route table + + - name: "3.2.2 | L1 | PATCH | Ensure ICMP redirects are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - sysctl flush ipv6 route table + - update sysctl + when: rhel9cis_ipv6_required + when: + - rhel9cis_rule_3_2_2 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.2 + +- name: "3.2.3 | L1 | PATCH | Ensure secure ICMP redirects are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_3 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.3 + +- name: "3.2.4 | L1 | PATCH | Ensure suspicious packets are logged" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_4 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.4 + +- name: "3.2.5 | L1 | PATCH | Ensure broadcast ICMP requests are ignored" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_5 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.5 + +- name: "3.2.6 | L1 | PATCH | Ensure bogus ICMP responses are ignored" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_6 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.6 + +- name: "3.2.7 | L1 | PATCH | Ensure Reverse Path Filtering is enabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_7 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.7 + +- name: "3.2.8 | L1 | PATCH | Ensure TCP SYN Cookies is enabled" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: update sysctl + when: + - rhel9cis_rule_3_2_8 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.2.8 + +- name: "3.2.9 | L2 | PATCH | Ensure IPv6 router advertisements are not accepted" + block: + - name: "3.2.9 | L2 | PATCH | Ensure IPv6 router advertisements are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - update sysctl + - sysctl flush ipv4 route table + + - name: "3.2.9 | L2 | PATCH | Ensure IPv6 router advertisements are not accepted" + debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/99-sysctl.conf" + notify: + - sysctl flush ipv6 route table + - update sysctl + when: rhel9cis_ipv6_required + when: + - rhel9cis_ipv6_required + - rhel9cis_rule_3_2_9 + tags: + - level2-server + - level2-workstation + - sysctl + - patch + - rule_3.2.9 diff --git a/tasks/section_3/cis_3.3.x.yml b/tasks/section_3/cis_3.3.x.yml new file mode 100644 index 00000000..8789558c --- /dev/null +++ b/tasks/section_3/cis_3.3.x.yml @@ -0,0 +1,61 @@ +--- + +- name: "3.3.1 | L2 | PATCH | Ensure DCCP is disabled" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install dccp(\\s|$)" + line: "install dccp /bin/true" + create: yes + mode: 0600 + when: + - rhel9cis_rule_3_3_1 + tags: + - level2-server + - level2-workstation + - patch + - rule_3.3.1 + +- name: "3.3.2 | L2 | PATCH | Ensure SCTP is disabled" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install sctp(\\s|$)" + line: "install sctp /bin/true" + create: yes + mode: 0600 + when: + - rhel9cis_rule_3_3_2 + tags: + - level2-server + - level2-workstation + - patch + - rule_3.3.2 + +- name: "3.3.3 | L2 | PATCH | Ensure RDS is disabled" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install rds(\\s|$)" + line: "install rds /bin/true" + create: yes + mode: 0600 + when: + - rhel9cis_rule_3_3_3 + tags: + - level2-server + - level2-workstation + - patch + - rule_3.3.3 + +- name: "3.3.4 | L2 | PATCH | Ensure TIPC is disabled" + lineinfile: + dest: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install tipc(\\s|$)" + line: "install tipc /bin/true" + create: yes + mode: 0600 + when: + - rhel9cis_rule_3_3_4 + tags: + - level2-server + - level2-workstation + - patch + - rule_3.3.4 diff --git a/tasks/section_3/cis_3.4.1.1.yml b/tasks/section_3/cis_3.4.1.1.yml new file mode 100644 index 00000000..3373d97c --- /dev/null +++ b/tasks/section_3/cis_3.4.1.1.yml @@ -0,0 +1,13 @@ +--- + +- name: "3.4.1.1 | L1 | PATCH | Ensure a Firewall package is installed" + package: + name: "{{ rhel9cis_firewall }}" + state: present + when: + - rhel9cis_rule_3_4_1_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.1.1 diff --git a/tasks/section_3/cis_3.4.2.x.yml b/tasks/section_3/cis_3.4.2.x.yml new file mode 100644 index 00000000..9f90c674 --- /dev/null +++ b/tasks/section_3/cis_3.4.2.x.yml @@ -0,0 +1,102 @@ +--- + +- name: "3.4.2.1 | L1 | PATCH | Ensure firewalld service is enabled and running" + service: + name: firewalld + state: started + enabled: yes + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_3_4_2_1 + +- name: "3.4.2.2 | L1 | PATCH | Ensure iptables is not enabled with firewalld" + systemd: + name: iptables + enabled: false + masked: true + ignore_errors: true + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_2 + tags: + - skip_ansible_lint + - level1-server + - level1-workstation + - patch + - rule_3_4_2_2 + +- name: "3.4.2.3 | L1 | PATCH | Ensure nftables is not enabled with firewalld" + systemd: + name: nftables + enabled: false + masked: true + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_3_4_2_3 + +- name: "3.4.2.4 | L1 | PATCH | Ensure default zone is set" + command: firewall-cmd --set-default-zone="{{ rhel9cis_default_zone }}" + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.2.4 + +- name: "3.4.2.5 | L1 | AUDIT | Ensure network interfaces are assigned to appropriate zone" + block: + - name: "3.4.2.5 | L1 | 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: no + register: rhel9cis_3_4_2_5_interfacepolicy + + - name: "3.4.2.5 | L1 | 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_2_5_interfacepolicy.stdout_lines }}" + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_5 + tags: + - level1-server + - level1-workstation + - audit + - rule_3.4.2.5 + +- name: "3.4.2.6 | L1 | AUDIT | Ensure firewalld drops unnecessary services and ports" + block: + - name: "3.4.2.6 | L1 | 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: no + register: rhel9cis_3_4_2_6_servicesport + + - name: "3.4.2.6 | L1 | 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_2_6_servicesport.stdout_lines }}" + when: + - rhel9cis_firewall == "firewalld" + - rhel9cis_rule_3_4_2_6 + tags: + - level1-server + - level1-workstation + - audit + - rule_3.4.2.6 diff --git a/tasks/section_3/cis_3.4.3.x.yml b/tasks/section_3/cis_3.4.3.x.yml new file mode 100644 index 00000000..202daa02 --- /dev/null +++ b/tasks/section_3/cis_3.4.3.x.yml @@ -0,0 +1,264 @@ +--- + +- name: "3.4.3.1 | L1 | PATCH | Ensure iptables are flushed with nftables" + command: ip6tables -F + when: + - rhel9cis_rule_3_4_3_1 + - rhel9cis_firewall != "iptables" + - rhel9cis_ipv6_required + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.1 + +- name: "3.4.3.2 | L1 | AUDIT | Ensure an nftables table exists" + block: + - name: "3.4.3.2 | L1 | AUDIT | Ensure a table exists | Check for tables" + command: nft list tables + changed_when: false + failed_when: false + register: rhel9cis_3_4_3_2_nft_tables + + - name: "3.4.3.2 | L1 | AUDIT | Ensure an nftables table exists | Show existing tables" + debug: + msg: + - "Below are the current nft tables, please review" + - "{{ rhel9cis_3_4_3_2_nft_tables.stdout_lines }}" + when: rhel9cis_3_4_3_2_nft_tables.stdout | length > 0 + + - name: "3.4.3.2 | L1 | 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_3_2_nft_tables.stdout | length == 0 + - not rhel9cis_nft_tables_autonewtable + + - name: "3.4.3.2 | L1 | PATCH | Ensure a table exists | Create table if needed" + command: nft create table inet "{{ rhel9cis_nft_tables_tablename }}" + failed_when: no + when: rhel9cis_nft_tables_autonewtable + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_3_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.2 + +- name: "3.4.3.3 | L1 | PATCH | Ensure nftables base chains exist" + block: + - name: "3.4.3.3 | L1 | Ensure nftables base chains exist | Get current chains for INPUT" + shell: nft list ruleset | grep 'hook input' + changed_when: false + failed_when: false + register: rhel9cis_3_4_3_3_input_chains + + - name: "3.4.3.3 | L1 | AUDIT | Ensure nftables base chains exist | Get current chains for FORWARD" + shell: nft list ruleset | grep 'hook forward' + changed_when: false + failed_when: false + register: rhel9cis_3_4_3_3_forward_chains + + - name: "3.4.3.3 | L1 | 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_3_3_output_chains + + - name: "3.4.3.3 | L1 | AUDIT | Ensure nftables base chains exist | Display chains for review" + debug: + msg: + - "Below are the current INPUT chains" + - "{{ rhel9cis_3_4_3_3_input_chains.stdout_lines }}" + - "Below are the current FORWARD chains" + - "{{ rhel9cis_3_4_3_3_forward_chains.stdout_lines }}" + - "Below are teh current OUTPUT chains" + - "{{ rhel9cis_3_4_3_3_output_chains.stdout_lines }}" + when: not rhel9cis_nft_tables_autochaincreate + + - name: "3.4.3.3 | L1 | PATCH | Ensure nftables base chains exist | Create chains if needed" + shell: "{{ item }}" + args: + warn: no + failed_when: no + with_items: + - 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_3_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.3 + +- name: "3.4.3.4 | L1 | PATCH | Ensure nftables loopback traffic is configured" + block: + - name: "3.4.3.4 | L1 | 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 + failed_when: false + register: rhel9cis_3_4_3_4_iiflo + + - name: "3.4.3.4 | L1 | 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_3_4_ipsaddr + + - name: "3.4.3.4 | L1 | AUDIT | Ensure nftables loopback traffic is configured | Gather ip6 saddr existence" + shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip6 saddr' + changed_when: false + failed_when: false + register: rhel9cis_3_4_3_4_ip6saddr + + - name: "3.4.3.4 | L1 | 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_3_4_iiflo.stdout' + + - name: "3.4.3.4 | L1 | 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_3_4_ipsaddr.stdout' + + - name: "3.4.3.4 | L1 | 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_3_4_ip6saddr.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_3_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.4 + +- name: "3.4.3.5 | L1 | PATCH | Ensure nftables outbound and established connections are configured" + block: + - name: "3.4.3.5 | L1 | 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_3_5_inconnectionrule + + - name: "3.4.3.5 | L1 | 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_3_5_outconnectionrule + + - name: "3.4.3.5 | L1 | 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_3_5_inconnectionrule.stdout' + + - name: "3.4.3.5 | L1 | 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_3_5_inconnectionrule.stdout' + + - name: "3.4.3.5 | L1 | 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_3_5_inconnectionrule.stdout' + + - name: "3.4.3.5 | L1 | 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_3_5_outconnectionrule.stdout' + + - name: "3.4.3.5 | L1 | 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_3_5_outconnectionrule.stdout' + + - name: "3.4.3.5 | L1 | 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_3_5_outconnectionrule.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_3_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.5 + +- name: "3.4.3.6 | L1 | PATCH | Ensure nftables default deny firewall policy" + block: + - name: "3.4.3.6 | L1 | 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_3_6_inputpolicy + + - name: "3.4.3.6 | L1 | 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_3_6_forwardpolicy + + - name: "3.4.3.6 | L1 | 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_3_6_outputpolicy + + - name: "3.4.3.6 | L1 | 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_3_6_sshallowcheck + + - name: "3.4.3.6 | L1 | 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_3_6_sshallowcheck.stdout' + + - name: "3.4.3.6 | L1 | 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_3_6_inputpolicy.stdout' + + - name: "3.4.3.6 | L1 | 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_3_6_forwardpolicy.stdout' + + - name: "3.4.3.6 | L1 | 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_3_6_outputpolicy.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_3_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.6 + +- name: "3.4.3.7 | L1 | PATCH | Ensure nftables service is enabled | Check if nftables is enabled" + service: + name: nftables + enabled: yes + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_3_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.7 + +- name: "3.4.3.8 | L1 | 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_3_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.3.8 diff --git a/tasks/section_3/cis_3.4.4.1.x.yml b/tasks/section_3/cis_3.4.4.1.x.yml new file mode 100644 index 00000000..edeb4980 --- /dev/null +++ b/tasks/section_3/cis_3.4.4.1.x.yml @@ -0,0 +1,144 @@ +--- + +- name: "3.4.4.1.1 | L1 | PATCH | Ensure iptables default deny firewall policy" + block: + - name: "3.4.4.1.1 | L1 | PATCH | Ensure iptables default deny firewall policy | Configure ssh to be allowed" + iptables: + chain: INPUT + protocol: tcp + destination_port: "22" + jump: ACCEPT + + - name: "3.4.4.1.1 | L1 | PATCH | Ensure iptables default deny firewall policy | Set drop items" + iptables: + policy: DROP + chain: "{{ item }}" + with_items: + - INPUT + - FORWARD + - OUTPUT + when: + - rhel9cis_rule_3_4_4_1_1 + - rhel9cis_firewall == "iptables" + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.1.1 + +- name: "3.4.4.1.2 | L1 | PATCH | Ensure iptables loopback traffic is configured" + block: + - name: "3.4.4.1.2 | L1 | Ensure iptables loopback traffic is configured | INPUT Loopback ACCEPT" + iptables: + action: append + chain: INPUT + in_interface: lo + jump: ACCEPT + + - name: "3.4.4.1.2 | L1 | PATCH | Ensure iptables loopback traffic is configured | OUTPUT Loopback ACCEPT" + iptables: + action: append + chain: OUTPUT + out_interface: lo + jump: ACCEPT + + - name: "3.4.4.1.2 | L1 | PATCH | Ensure iptables loopback traffic is configured | INPUT Loopback 127.0.0.0/8" + iptables: + action: append + chain: INPUT + source: 127.0.0.0/8 + jump: DROP + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.1.2 + +- name: "3.4.4.1.3 | L1 | PATCH | Ensure iptables outbound and established connections are configured" + iptables: + action: append + chain: '{{ item.chain }}' + protocol: '{{ item.protocol }}' + match: state + ctstate: '{{ item.ctstate }}' + jump: ACCEPT + with_items: + - { chain: OUTPUT, protocol: tcp, ctstate: 'NEW,ESTABLISHED' } + - { chain: OUTPUT, protocol: udp, ctstate: 'NEW,ESTABLISHED' } + - { chain: OUTPUT, protocol: icmp, ctstate: 'NEW,ESTABLISHED' } + - { chain: INPUT, protocol: tcp, ctstate: ESTABLISHED } + - { chain: INPUT, protocol: udp, ctstate: ESTABLISHED } + - { chain: INPUT, protocol: icmp, ctstate: ESTABLISHED } + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_1_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.1.3 + +- name: "3.4.4.1.4 | L1 | PATCH | Ensure iptables firewall rules exist for all open ports" + block: + - name: "3.4.4.1.4 | L1 | AUDIT | Ensure iptables firewall rules exist for all open ports | Get list of TCP open ports" + shell: netstat -ant |grep "tcp.*LISTEN" | awk '{ print $4 }'| sed 's/.*://' + changed_when: false + failed_when: false + register: rhel9cis_3_4_4_1_4_otcp + + - name: "3.4.4.1.4 | L1 | AUDIT | Ensure iptables firewall rules exist for all open ports | Get the list of udp open ports" + shell: netstat -ant |grep "udp.*LISTEN" | awk '{ print $4 }'| sed 's/.*://' + changed_when: false + failed_when: false + register: rhel9cis_3_4_4_1_4_oudp + + - name: "3.4.4.1.4 | L1 | PATCH | Ensure iptables firewall rules exist for all open ports | Adjust open tcp ports" + iptables: + action: append + chain: INPUT + protocol: tcp + destination_port: "{{ item }}" + match: state + ctstate: NEW + jump: ACCEPT + with_items: + - "{{ rhel9cis_3_4_4_1_4_otcp.stdout_lines }}" + when: rhel9cis_3_4_4_1_4_otcp.stdout is defined + + - name: "3.4.4.1.4 | L1 | PATCH | Ensure iptables firewall rules exist for all open ports | Adjust open udp ports" + iptables: + action: append + chain: INPUT + protocol: udp + destination_port: "{{ item }}" + match: state + ctstate: NEW + jump: ACCEPT + with_items: + - "{{ rhel9cis_3_4_4_1_4_oudp.stdout_lines }}" + when: rhel9cis_3_4_4_1_4_otcp.stdout is defined + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_1_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.1.4 + +- name: "3.4.4.1.5 | L1 | PATCH | Ensure iptables service is enabled and active | Check if iptables is enabled" + service: + name: iptables + enabled: yes + state: started + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_1_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.1.5 diff --git a/tasks/section_3/cis_3.4.4.2.x.yml b/tasks/section_3/cis_3.4.4.2.x.yml new file mode 100644 index 00000000..4e96f49c --- /dev/null +++ b/tasks/section_3/cis_3.4.4.2.x.yml @@ -0,0 +1,136 @@ +--- + +- name: "3.4.4.2.1 | L1 | PATCH | Ensure ip6tables default deny firewall policy" + block: + - name: "3.4.4.2.1 | L1 | Ensure ip6tables default deny firewall policy | Configure ssh to be allowed" + iptables: + chain: INPUT + protocol: tcp + destination_port: "22" + jump: ACCEPT + ip_version: ipv6 + + - name: "3.4.4.2.1 | L1 | PATCH | Ensure ip6tables default deny firewall policy | Set drop items" + iptables: + policy: DROP + chain: "{{ item }}" + ip_version: ipv6 + with_items: + - INPUT + - FORWARD + - OUTPUT + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_2_1 + - rhel9cis_ipv6_required + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.2.1 + +- name: "3.4.4.2.2 | L1 | PATCH | Ensure ip6tables loopback traffic is configured" + block: + - name: "3.4.4.2.2 | L1 | PATCH | Ensure ip6tables loopback traffic is configured | INPUT Loopback ACCEPT" + iptables: + action: append + chain: INPUT + in_interface: lo + jump: ACCEPT + ip_version: ipv6 + + - name: "3.4.4.2.2 | L1 | PATCH | Ensure ip6tables loopback traffic is configured | OUTPUT Loopback ACCEPT" + iptables: + action: append + chain: OUTPUT + out_interface: lo + jump: ACCEPT + ip_version: ipv6 + + - name: "3.4.4.2.2 | L1 | PATCH | Ensure ip6tables loopback traffic is configured | INPUT Loopback 127.0.0.0/8" + iptables: + action: append + chain: INPUT + source: ::1 + jump: DROP + ip_version: ipv6 + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_2_2 + - rhel9cis_ipv6_required + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.2.2 + +- name: "3.4.4.2.3 | L1 | PATCH | Ensure ip6tables outbound and established connections are configured" + iptables: + action: append + chain: '{{ item.chain }}' + protocol: '{{ item.protocol }}' + match: state + ctstate: '{{ item.ctstate }}' + jump: ACCEPT + ip_version: ipv6 + with_items: + - { chain: OUTPUT, protocol: tcp, ctstate: 'NEW,ESTABLISHED' } + - { chain: OUTPUT, protocol: udp, ctstate: 'NEW,ESTABLISHED' } + - { chain: OUTPUT, protocol: icmp, ctstate: 'NEW,ESTABLISHED' } + - { chain: INPUT, protocol: tcp, ctstate: ESTABLISHED } + - { chain: INPUT, protocol: udp, ctstate: ESTABLISHED } + - { chain: INPUT, protocol: icmp, ctstate: ESTABLISHED } + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_2_3 + - rhel9cis_ipv6_required + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.2.3 + +- name: "3.4.4.2.4 | L1 | PATCH | Ensure ip6tables firewall rules exist for all open ports" + block: + - name: "3.4.4.2.4 | L1 | AUDIT | Ensure ip6tables firewall rules exist for all open ports | Get list of TCP6 open ports" + shell: netstat -ant |grep "tcp6.*LISTEN" | awk '{ print $4 }'| sed 's/.*://' + changed_when: false + failed_when: false + register: rhel9cis_3_4_4_2_4_otcp + + - name: "3.4.4.2.4 | L1 | PATCH |Ensure ip6tables firewall rules exist for all open ports| Adjust open tcp6 ports" + iptables: + action: append + chain: INPUT + protocol: tcp + destination_port: "{{ item }}" + match: state + ctstate: NEW + jump: ACCEPT + ip_version: ipv6 + with_items: + - "{{ rhel9cis_3_4_4_2_4_otcp.stdout_lines }}" + when: rhel9cis_3_4_4_2_4_otcp.stdout is defined + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_2_4 + - rhel9cis_ipv6_required + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.2.4 + +- name: "3.4.4.2.5 | L1 | PATCH | Ensure ip6tables service is enabled and active | Check if ip6tables is enabled" + service: + name: ip6tables + enabled: yes + state: started + when: + - rhel9cis_firewall == "iptables" + - rhel9cis_rule_3_4_4_2_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_3.4.4.2.5 diff --git a/tasks/section_3/cis_3.5.yml b/tasks/section_3/cis_3.5.yml new file mode 100644 index 00000000..5a60a5e6 --- /dev/null +++ b/tasks/section_3/cis_3.5.yml @@ -0,0 +1,32 @@ +--- + +- name: "3.5 | L1 | PATCH | Ensure wireless interfaces are disabled" + block: + - name: "3.5 | L1 | AUDIT | Ensure wireless interfaces are disabled | Check if nmcli command is available" + command: rpm -q NetworkManager + changed_when: false + failed_when: false + check_mode: no + args: + warn: no + register: rhel_09_nmcli_available + + - name: "3.5 | L1 | AUDIT | Ensure wireless interfaces are disabled | Check if wifi is enabled" + command: 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.5 | L1 | PATCH | Ensure wireless interfaces are disabled | Disable wifi if enabled" + command: nmcli radio all off + changed_when: false + failed_when: false + when: rhel_09_wifi_enabled is changed + when: + - rhel9cis_rule_3_5 + tags: + - level1-server + - level2-workstation + - patch + - rule_3.5 diff --git a/tasks/section_3/cis_3.6.yml b/tasks/section_3/cis_3.6.yml new file mode 100644 index 00000000..9b393184 --- /dev/null +++ b/tasks/section_3/cis_3.6.yml @@ -0,0 +1,17 @@ +--- + +- name: "3.6 | L2 | PATCH | Disable IPv6" + replace: + dest: /etc/default/grub + regexp: '(^GRUB_CMDLINE_LINUX\s*\=\s*)(?:")(.+)(?/dev/null; done + changed_when: false + failed_when: false + check_mode: no + register: priv_procs + + - name: "4.1.12 | L2 | PATCH | Ensure successful file system mounts are collected" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_12 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.12 + +- name: "4.1.13 | L2 | PATCH | Ensure use of privileged commands is collected" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_13 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.13 + +- name: "4.1.14 | L2 | PATCH | Ensure file deletion events by users are collected" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_14 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.14 + +- name: "4.1.15 | L2 | PATCH | Ensure kernel module loading and unloading is collected" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_15 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.15 + +- name: "4.1.16 | L2 | PATCH | Ensure system administrator actions (sudolog) are collected" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_16 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.16 + +- name: "4.1.17 | L2 | PATCH | Ensure the audit configuration is immutable" + debug: + msg: "Control being set via Handler 'update auditd' which writes to /etc/audit.d/99_auditd.rules" + notify: update auditd + when: + - rhel9cis_rule_4_1_17 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.17 diff --git a/tasks/section_4/cis_4.2.1.x.yml b/tasks/section_4/cis_4.2.1.x.yml new file mode 100644 index 00000000..b9d525ea --- /dev/null +++ b/tasks/section_4/cis_4.2.1.x.yml @@ -0,0 +1,177 @@ +--- + +- name: "4.2.1.1 | L1 | PATCH | Ensure rsyslog installed" + package: + name: rsyslog + state: present + when: + - "'rsyslog' not in ansible_facts.packages" + - rhel9cis_rule_4_2_1_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.1.1 + +- name: "4.2.1.2 | L1 | PATCH | Ensure rsyslog Service is enabled" + service: + name: rsyslog + enabled: yes + when: + - rhel9cis_rule_4_2_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rsyslog + - rule_4.2.1.2 + +- name: "4.2.1.3 | L1 | PATCH | Ensure rsyslog default file permissions configured" + lineinfile: + dest: /etc/rsyslog.conf + regexp: '^\$FileCreateMode' + line: '$FileCreateMode 0640' + notify: restart rsyslog + when: + - rhel9cis_rule_4_2_1_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.1.3 + +- name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured" + block: + - name: "4.2.1.4 | L1 | AUDIT | Ensure logging is configured | rsyslog current config message out" + command: cat /etc/rsyslog.conf + become: yes + changed_when: false + failed_when: no + check_mode: no + register: rhel_09_4_2_1_4_audit + + - name: "4.2.1.4 | L1 | AUDIT | Ensure logging is configured | rsyslog current config message out" + debug: + msg: + - "These are the current logging configurations for rsyslog, please review:" + - "{{ rhel_09_4_2_1_4_audit.stdout_lines }}" + + - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | mail.* log setting" + blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "# {mark} MAIL LOG SETTINGS (ANSIBLE MANAGED)" + block: | + # mail logging additions to meet CIS standards + mail.* -/var/log/mail + mail.info -/var/log/mail.info + mail.warning -/var/log/mail.warning + mail.err /var/log/mail.err + insertafter: '# Log all the mail messages in one place.' + notify: restart rsyslog + when: rhel9cis_rsyslog_ansiblemanaged + + - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | news.crit log setting" + blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "# {mark} NEWS LOG SETTINGS (ANSIBLE MANAGED)" + block: | + # news logging additions to meet CIS standards + 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 + when: rhel9cis_rsyslog_ansiblemanaged + + - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | Misc. log setting" + blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "# {mark} MISC. LOG SETTINGS (ANSIBLE MANAGED)" + block: | + # misc. logging additions to meet CIS standards + *.=warning;*.=err -/var/log/warn + *.crit /var/log/warn + *.*;mail.none;news.none /var/log/messages + insertafter: '#### RULES ####' + notify: restart rsyslog + when: rhel9cis_rsyslog_ansiblemanaged + + - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | Local log settings" + blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "#{mark} LOCAL LOG SETTINGS (ANSIBLE MANAGED)" + block: | + # local log settings + local0,local1.* -/var/log/localmessages + local2,local3.* -/var/log/localmessages + local4,local5.* -/var/log/localmessages + local6,local7.* -/var/log/localmessages + *.emrg :omusrmsg:* + insertafter: '#### RULES ####' + notify: restart rsyslog + when: + - rhel9cis_rule_4_2_1_4 + tags: + - level1-server + - level1-workstation + - patch + - rsyslog + - rule_4.2.1.4 + +- name: "4.2.1.5 | L1 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" + 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 }} + insertafter: EOF + register: result + failed_when: + - result is failed + - result.rc != 257 + notify: restart rsyslog + when: + - rhel9cis_rule_4_2_1_5 + - rhel9cis_remote_log_server is defined + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.1.5 + - rsyslog + +- name: "4.2.1.6 | L1 | PATCH | Ensure remote rsyslog messages are only accepted on designated log hosts." + block: + - name: "4.2.1.6 | L1 | PATCH | Ensure remote rsyslog messages are only accepted on designated log hosts. | When not log host" + replace: + path: /etc/rsyslog.conf + regexp: '({{ item }})' + replace: '#\1' + notify: restart rsyslog + with_items: + - '^(\$ModLoad imtcp)' + - '^(\$InputTCPServerRun)' + when: not rhel9cis_system_is_log_server + + - name: "4.2.1.6 | L1 | PATCH | Ensure remote rsyslog messages are only accepted on designated log hosts. | When log host" + replace: + path: /etc/rsyslog.conf + regexp: '^#(.*{{ item }}.*)' + replace: '\1' + notify: restart rsyslog + with_items: + - 'ModLoad imtcp' + - 'InputTCPServerRun' + when: rhel9cis_system_is_log_server + when: + - rhel9cis_rule_4_2_1_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.1.6 + - rsyslog diff --git a/tasks/section_4/cis_4.2.2.x.yml b/tasks/section_4/cis_4.2.2.x.yml new file mode 100644 index 00000000..1c87ed47 --- /dev/null +++ b/tasks/section_4/cis_4.2.2.x.yml @@ -0,0 +1,43 @@ +--- + +- name: "4.2.2.1 | L1 | PATCH | Ensure journald is configured to send logs to rsyslog" + lineinfile: + dest: /etc/systemd/journald.conf + regexp: "^#ForwardToSyslog=|^ForwardToSyslog=" + line: ForwardToSyslog=yes + state: present + when: + - rhel9cis_rule_4_2_2_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.2.1 + +- name: "4.2.2.2 | L1 | PATCH | Ensure journald is configured to compress large log files" + lineinfile: + dest: /etc/systemd/journald.conf + regexp: "^#Compress=|^Compress=" + line: Compress=yes + state: present + when: + - rhel9cis_rule_4_2_2_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.2.2 + +- name: "4.2.2.3 | L1 | PATCH | Ensure journald is configured to write logfiles to persistent disk" + lineinfile: + dest: /etc/systemd/journald.conf + regexp: "^#Storage=|^Storage=" + line: Storage=persistent + state: present + when: + - rhel9cis_rule_4_2_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.2.3 diff --git a/tasks/section_4/cis_4.2.3.yml b/tasks/section_4/cis_4.2.3.yml new file mode 100644 index 00000000..d1992696 --- /dev/null +++ b/tasks/section_4/cis_4.2.3.yml @@ -0,0 +1,13 @@ +--- + +- name: "4.2.3 | L1 | 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 + when: + - rhel9cis_rule_4_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.2.3 diff --git a/tasks/section_4/cis_4.3.yml b/tasks/section_4/cis_4.3.yml new file mode 100644 index 00000000..853a2159 --- /dev/null +++ b/tasks/section_4/cis_4.3.yml @@ -0,0 +1,24 @@ +--- + +- name: "4.3 | L1 | PATCH | Ensure logrotate is configured" + block: + - name: "4.3 | L1 | AUDIT | Ensure logrotate is configured | Get logrotate settings" + find: + paths: /etc/logrotate.d/ + register: log_rotates + + - name: "4.3 | L1 | PATCH | Ensure logrotate is configured" + replace: + path: "{{ item.path }}" + regexp: '^(\s*)(daily|weekly|monthly|yearly)$' + replace: "\\1{{ rhel9cis_logrotate }}" + with_items: + - "{{ log_rotates.files }}" + - { path: "/etc/logrotate.conf" } + when: + - rhel9cis_rule_4_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_4.3 diff --git a/tasks/section_4/main.yml b/tasks/section_4/main.yml new file mode 100644 index 00000000..aecac9f5 --- /dev/null +++ b/tasks/section_4/main.yml @@ -0,0 +1,23 @@ +--- + +- name: "SECTION | 4.1| Configure System Accounting (auditd)" + include: cis_4.1.1.x.yml + +- name: "SECTION | 4.1.2.x| Configure Data Retention" + include: cis_4.1.2.x.yml + +- name: "SECTION | 4.1.x| Auditd rules" + include: cis_4.1.x.yml + +- name: "SECTION | 4.2.x| Configure Logging" + include: cis_4.2.1.x.yml + when: rhel9cis_syslog == 'rsyslog' + +- name: "SECTION | 4.2.2.x| Configure journald" + include: cis_4.2.2.x.yml + +- name: "SECTION | 4.2.3 | Configure logile perms" + include: cis_4.2.3.yml + +- name: "SECTION | 4.3 | Configure logrotate" + include: cis_4.3.yml diff --git a/tasks/section_5/cis_5.1.x.yml b/tasks/section_5/cis_5.1.x.yml new file mode 100644 index 00000000..a0470c55 --- /dev/null +++ b/tasks/section_5/cis_5.1.x.yml @@ -0,0 +1,147 @@ +--- + +- name: "5.1.1 | L1 | PATCH | Ensure cron daemon is enabled" + service: + name: crond + enabled: yes + when: + - rhel9cis_rule_5_1_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.1 + +- name: "5.1.2 | L1 | PATCH | Ensure permissions on /etc/crontab are configured" + file: + dest: /etc/crontab + owner: root + group: root + mode: 0600 + when: + - rhel9cis_rule_5_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.2 + +- name: "5.1.3 | L1 | PATCH | Ensure permissions on /etc/cron.hourly are configured" + file: + dest: /etc/cron.hourly + state: directory + owner: root + group: root + mode: 0700 + when: + - rhel9cis_rule_5_1_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.3 + +- name: "5.1.4 | L1 | PATCH | Ensure permissions on /etc/cron.daily are configured" + file: + dest: /etc/cron.daily + state: directory + owner: root + group: root + mode: 0700 + when: + - rhel9cis_rule_5_1_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.4 + +- name: "5.1.5 | L1 | PATCH | Ensure permissions on /etc/cron.weekly are configured" + file: + dest: /etc/cron.weekly + state: directory + owner: root + group: root + mode: 0700 + when: + - rhel9cis_rule_5_1_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.5 + +- name: "5.1.6 | L1 | PATCH | Ensure permissions on /etc/cron.monthly are configured" + file: + dest: /etc/cron.monthly + state: directory + owner: root + group: root + mode: 0700 + when: + - rhel9cis_rule_5_1_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.6 + +- name: "5.1.7 | L1 | PATCH | Ensure permissions on /etc/cron.d are configured" + file: + dest: /etc/cron.d + state: directory + owner: root + group: root + mode: 0700 + when: + - rhel9cis_rule_5_1_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.7 + +- name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users" + block: + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Remove at.deny" + file: + dest: /etc/at.deny + state: absent + + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Check if at.allow exists" + stat: + path: "/etc/at.allow" + register: p + + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Ensure at.allow is restricted to authorized users" + file: + dest: /etc/at.allow + state: '{{ "file" if p.stat.exists else "touch" }}' + owner: root + group: root + mode: 0600 + + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Remove cron.deny" + file: + dest: /etc/cron.deny + state: absent + + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Check if cron.allow exists" + stat: + path: "/etc/cron.allow" + register: p + + - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Ensure cron.allow is restricted to authorized users" + file: + dest: /etc/cron.allow + state: '{{ "file" if p.stat.exists else "touch" }}' + owner: root + group: root + mode: 0600 + when: + - rhel9cis_rule_5_1_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.1.8 diff --git a/tasks/section_5/cis_5.2.x.yml b/tasks/section_5/cis_5.2.x.yml new file mode 100644 index 00000000..cce02269 --- /dev/null +++ b/tasks/section_5/cis_5.2.x.yml @@ -0,0 +1,345 @@ +--- + +- name: "5.2.1 | L1 | PATCH | Ensure permissions on /etc/ssh/sshd_config are configured" + file: + dest: /etc/ssh/sshd_config + state: file + owner: root + group: root + mode: 0600 + when: + - rhel9cis_rule_5_2_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.1 + +- name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited" + block: + - name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowusers" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^AllowUsers" + line: AllowUsers {{ rhel9cis_sshd['allowusers'] }} + notify: restart sshd + when: "rhel9cis_sshd['allowusers']|default('') | length > 0" + + - name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowgroups" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^AllowGroups" + line: AllowGroups {{ rhel9cis_sshd['allowgroups'] }} + notify: restart sshd + when: "rhel9cis_sshd['allowgroups']|default('') | length > 0" + + - name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denyusers" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^DenyUsers" + line: DenyUsers {{ rhel9cis_sshd['denyusers'] }} + notify: restart sshd + when: "rhel9cis_sshd['denyusers']|default('') | length > 0" + + - name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denygroups" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^DenyGroups" + line: DenyGroups {{ rhel9cis_sshd['denygroups'] }} + notify: restart sshd + when: "rhel9cis_sshd['denygroups']|default('') | length > 0" + when: + - rhel9cis_rule_5_2_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.2 + +- name: "5.2.3 | L1 | PATCH | Ensure permissions on SSH private host key files are configured" + block: + - name: "5.2.3 | L1 | AUDIT | Ensure permissions on SSH private host key files are configured | Find the SSH private host keys" + find: + paths: /etc/ssh + patterns: 'ssh_host_*_key' + recurse: true + file_type: any + register: rhel9cis_5_2_3_ssh_private_host_key + + - name: "5.2.3 | L1 | PATCH | Ensure permissions on SSH private host key files are configured | Set permissions on SSH private host keys" + file: + path: "{{ item.path }}" + owner: root + group: root + mode: 0600 + with_items: + - "{{ rhel9cis_5_2_3_ssh_private_host_key.files }}" + when: + - rhel9cis_rule_5_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.3 + +- name: "5.2.4 | L1 | PATCH | Ensure permissions on SSH public host key files are configured" + block: + - name: "5.2.4 | L1 | AUDIT | Ensure permissions on SSH public host key files are configured | Find the SSH public host keys" + find: + paths: /etc/ssh + patterns: 'ssh_host_*_key.pub' + recurse: true + file_type: any + register: rhel9cis_5_2_4_ssh_public_host_key + + - name: "5.2.4 | L1 | PATCH | Ensure permissions on SSH public host key files are configured | Set permissions on SSH public host keys" + file: + path: "{{ item.path }}" + owner: root + group: root + mode: 0644 + with_items: + - "{{ rhel9cis_5_2_4_ssh_public_host_key.files }}" + when: + - rhel9cis_rule_5_2_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.4 + +- name: "5.2.5 | L1 | PATCH | Ensure SSH LogLevel is appropriate" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#LogLevel|^LogLevel" + line: 'LogLevel {{ rhel9cis_ssh_loglevel }}' + when: + - rhel9cis_rule_5_2_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.5 + +- name: "5.2.6 | L2 | PATCH | Ensure SSH X11 forwarding is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#X11Forwarding|^X11Forwarding" + line: 'X11Forwarding no' + when: + - rhel9cis_rule_5_2_6 + tags: + - level2-server + - level1-workstation + - patch + - rule_5.2.6 + +- name: "5.2.7 | L1 | PATCH | Ensure SSH MaxAuthTries is set to 4 or less" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: '^(#)?MaxAuthTries \d' + line: 'MaxAuthTries 4' + when: + - rhel9cis_rule_5_2_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.7 + +- name: "5.2.8 | L1 | PATCH | Ensure SSH IgnoreRhosts is enabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#IgnoreRhosts|^IgnoreRhosts" + line: 'IgnoreRhosts yes' + when: + - rhel9cis_rule_5_2_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.8 + +- name: "5.2.9 | L1 | PATCH | Ensure SSH HostbasedAuthentication is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: ^#HostbasedAuthentication|^HostbasedAuthentication" + line: 'HostbasedAuthentication no' + when: + - rhel9cis_rule_5_2_9 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.9 + +- name: "5.2.10 | L1 | PATCH | Ensure SSH root login is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#PermitRootLogin|^PermitRootLogin" + line: 'PermitRootLogin no' + when: + - rhel9cis_rule_5_2_10 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.10 + +- name: "5.2.11 | L1 | PATCH | Ensure SSH PermitEmptyPasswords is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#PermitEmptyPasswords|^PermitEmptyPasswords" + line: 'PermitEmptyPasswords no' + when: + - rhel9cis_rule_5_2_11 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.11 + +- name: "5.2.12 | L1 | PATCH | Ensure SSH PermitUserEnvironment is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#PermitUserEnvironment|^PermitUserEnvironment" + line: 'PermitUserEnvironment no' + when: + - rhel9cis_rule_5_2_12 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.12 + +- name: "5.2.13 | L1 | PATCH | Ensure SSH Idle Timeout Interval is configured" + block: + - name: "5.2.13 | L1 | PATCH | Ensure SSH Idle Timeout Interval is configured | Add line in sshd_config for ClientAliveInterval" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: '^ClientAliveInterval' + line: "ClientAliveInterval {{ rhel9cis_sshd['clientaliveinterval'] }}" + + - name: "5.2.13 | L1 | PATCH | Ensure SSH Idle Timeout Interval is configured | Ensure SSH ClientAliveCountMax set to <= 3" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: '^ClientAliveCountMax' + line: "ClientAliveCountMax {{ rhel9cis_sshd['clientalivecountmax'] }}" + when: + - rhel9cis_rule_5_2_13 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.13 + +- name: "5.2.14 | L1 | PATCH | Ensure SSH LoginGraceTime is set to one minute or less" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#LoginGraceTime|^LoginGraceTime" + line: "LoginGraceTime {{ rhel9cis_sshd['logingracetime'] }}" + when: + - rhel9cis_rule_5_2_14 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.14 + +- name: "5.2.15 | L1 | PATCH | Ensure SSH warning banner is configured" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: '^Banner' + line: 'Banner /etc/issue.net' + when: + - rhel9cis_rule_5_2_15 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.15 + +- name: "5.2.16 | L1 | PATCH | Ensure SSH PAM is enabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#UsePAM|^UsePAM" + line: 'UsePAM yes' + when: + - rhel9cis_rule_5_2_16 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.16 + +- name: "5.2.17 | L2 | PATCH | Ensure SSH AllowTcpForwarding is disabled" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#AllowTcpForwarding|^AllowTcpForwarding" + line: 'AllowTcpForwarding no' + when: + - rhel9cis_rule_5_2_17 + tags: + - level2-server + - level2-workstation + - patch + - rule_5.2.17 + +- name: "5.2.18 | L1 | PATCH | Ensure SSH MaxStartups is configured" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#MaxStartups|^MaxStartups" + line: 'MaxStartups 10:30:60' + when: + - rhel9cis_rule_5_2_18 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.18 + +- name: "5.2.19 | L1 | PATCH | Ensure SSH MaxSessions is set to 4 or less" + lineinfile: + state: present + dest: /etc/ssh/sshd_config + regexp: "^#MaxSessions|^MaxSessions" + line: 'MaxSessions {{ rhel9cis_ssh_maxsessions }}' + when: + - rhel9cis_rule_5_2_19 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.19 + +- name: "5.2.20 | L1 | PATCH | Ensure system-wide crypto policy is not over-ridden" + shell: sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd + args: + warn: no + notify: restart sshd + when: + - rhel9cis_rule_5_2_20 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.2.20 diff --git a/tasks/section_5/cis_5.3.x.yml b/tasks/section_5/cis_5.3.x.yml new file mode 100644 index 00000000..c643b0e5 --- /dev/null +++ b/tasks/section_5/cis_5.3.x.yml @@ -0,0 +1,90 @@ +--- + +- name: "5.3.1 | L1 | PATCH | Create custom authselect profile" + block: + - name: "5.3.1 | L1 | PATCH | Create custom authselect profile | Gather profiles" + shell: 'authselect current | grep "Profile ID: custom/"' + failed_when: false + changed_when: false + check_mode: no + register: rhel9cis_5_3_1_profiles + + - name: "5.3.1 | L1 | AUDIT | Create custom authselect profile | Show profiles" + debug: + msg: + - "Below are the current custom profiles" + - "{{ rhel9cis_5_3_1_profiles.stdout_lines }}" + + - name: "5.3.1 | L1 | PATCH | Create custom authselect profile | Create custom profiles" + shell: authselect create-profile {{ rhel9cis_authselect['custom_profile_name'] }} -b {{ rhel9cis_authselect['default_file_to_copy'] }} + args: + warn: no + when: rhel9cis_authselect_custom_profile_create + when: + - rhel9cis_rule_5_3_1 + tags: + - level1-server + - level1-workstation + - patch + - authselect + - rule_5.3.1 + +- name: "5.3.2 | L1 | PATCH | Select authselect profile" + block: + - name: "5.3.2 | L1 | AUDIT | Select authselect profile | Gather profiles and enabled features" + shell: "authselect current" + args: + warn: no + failed_when: false + changed_when: false + check_mode: no + register: rhel9cis_5_3_2_profiles + + - name: "5.3.2 | L1 | AUDIT | Select authselect profile | Show profiles" + debug: + msg: + - "Below are the current custom profiles" + - "{{ rhel9cis_5_3_2_profiles.stdout_lines }}" + + - name: "5.3.2 | L1 | PATCH | Select authselect profile | Create custom profiles" + shell: "authselect select custom/{{ rhel9cis_authselect['custom_profile_name'] }} {{ rhel9cis_authselect['options'] }}" + args: + warn: no + when: rhel9cis_authselect_custom_profile_select + when: + - rhel9cis_rule_5_3_2 + tags: + - level1-server + - level1-workstation + - patch + - authselect + - rule_5.3.2 + +- name: "5.3.3 | L1 | PATCH | Ensure authselect includes with-faillock" + block: + - name: "5.3.3 | L1 | AUDIT | Ensure authselect includes with-faillock | Gather profiles and enabled features" + shell: "authselect current | grep with-faillock" + failed_when: false + changed_when: false + check_mode: no + register: rhel9cis_5_3_3_profiles_faillock + + - name: "5.3.3 | L1 | AUDIT | Ensure authselect includes with-faillock| Show profiles" + debug: + msg: + - "Below are the current custom profiles" + - "{{ rhel9cis_5_3_3_profiles_faillock.stdout_lines }}" + + - name: "5.3.3 | L1 | PATCH | Ensure authselect includes with-faillock | Create custom profiles" + shell: "authselect select custom/{{ rhel9cis_authselect['custom_profile_name'] }} with-faillock" + args: + warn: no + when: rhel9cis_authselect_custom_profile_select + when: + - rhel9cis_rule_5_3_3 + tags: + - level1-server + - level1-workstation + - patch + - authselect + - rule_5.3.3 diff --git a/tasks/section_5/cis_5.4.x.yml b/tasks/section_5/cis_5.4.x.yml new file mode 100644 index 00000000..3870b8fb --- /dev/null +++ b/tasks/section_5/cis_5.4.x.yml @@ -0,0 +1,131 @@ +--- + +- name: | + "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured + 5.4.2 | L1 | PATCH | Ensure lockout for failed password attempts is configured + 5.4.3 | L1 | PATCH | Ensure password reuse is limited + 5.4.4 | L1 | PATCH | Ensure password hashing algorithm is SHA-512" + block: + - name: "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured | Set pwquality config settings" + lineinfile: + state: present + dest: /etc/security/pwquality.conf + regexp: ^{{ item.name }} + line: "{{ item.name }} = {{ item.value }}" + with_items: + - { name: minlen, value: "{{ rhel9cis_pam_password.minlen }}" } + - { name: minclass, value: "{{ rhel9cis_pam_password.minclass }}" } + when: rhel9cis_rule_5_4_1 + + - name: | + "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings + 5.4.3| L1 | PATCH | Ensure password reuse is limited | Set system-auth remember settings" + lineinfile: + dest: /etc/pam.d/system-auth + state: present + regexp: '^password requisite pam_pwquality.so' + line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce-for-root retry=3 remember={{ rhel9cis_pam_faillock.remember }}" + insertbefore: '^#?password ?' + when: + - rhel9cis_rule_5_4_1 or + rhel9cis_rule_5_4_3 + + - name: "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings" + lineinfile: + dest: /etc/pam.d/password-auth + state: present + regexp: '^password requisite pam_pwquality.so' + line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce-for-root retry=3" + insertbefore: '^#?password ?' + when: rhel9cis_rule_5_4_1 + + - name: "5.4.2 | L1 | PATCH | Ensure lockout for failed password attempts is configured | Add deny count and unlock time for preauth" + lineinfile: + dest: /etc/pam.d/{{ item }} + state: present + regexp: '^auth required pam_faillock.so preauth' + line: "auth required pam_faillock.so preauth silent deny={{ rhel9cis_pam_faillock.attempts }}{{ (rhel9cis_pam_faillock.fail_for_root) | ternary(' even_deny_root ',' ') }}unlock_time={{ rhel9cis_pam_faillock.unlock_time }}" + insertafter: '^#?auth ?' + with_items: + - "system-auth" + - "password-auth" + when: rhel9cis_rule_5_4_2 + + - name: "5.4.2 | L1 | PATCH | Ensure lockout for failed password attempts is configured | Add deny count and unlock times for authfail" + lineinfile: + dest: /etc/pam.d/{{ item }} + state: present + regexp: '^auth required pam_faillock.so authfail' + line: "auth required pam_faillock.so authfail deny={{ rhel9cis_pam_faillock.attempts }}{{ (rhel9cis_pam_faillock.fail_for_root) | ternary(' even_deny_root ',' ') }}unlock_time={{ rhel9cis_pam_faillock.unlock_time }}" + insertafter: '^#?auth ?' + with_items: + - "system-auth" + - "password-auth" + when: rhel9cis_rule_5_4_2 + + - name: | + "5.4.3 | L1 | PATCH | Ensure password reuse is limited | Set system-auth remember remember settings + 5.4.4 | L1 | PATCH | Ensure password hashing algorithm is SHA-512 | Set system-auth pwhash settings" + lineinfile: + dest: /etc/pam.d/system-auth + state: present + regexp: '^password sufficient pam_unix.so' + line: "password sufficient pam_unix.so {{ rhel9cis_pam_faillock.pwhash }} shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}" + insertafter: '^#?password ?' + when: + - rhel9cis_rule_5_4_3 or + rhel9cis_rule_5_4_4 + + - name: "5.4.4 | L1 | PATCH | Ensure password hashing algorithm is SHA-512 | Set system-auth pwhash settings" + lineinfile: + dest: /etc/pam.d/password-auth + state: present + regexp: '^password sufficient pam_unix.so' + line: "password sufficient pam_unix.so {{ rhel9cis_pam_faillock.pwhash }} shadow try_first_pass use_authtok" + insertafter: '^#?password ?' + when: rhel9cis_rule_5_4_4 + + # The two steps below were added to keep authconfig from overwritting the above configs. This follows steps from here: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/chap-hardening_your_system_with_tools_and_services + # With the steps below you will score five (5) points lower due to false positive results + - name: | + "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured + 5.4.2 | L1 | PATCH | Ensure lockout for failed password attempts is configured + 5.4.3 | L1 | PATCH | Ensure password reuse is limited + 5.4.4 | L1 | PATCH | Ensure password hashing algorithm is SHA-512" + copy: + src: /etc/pam.d/{{ item }} + dest: /etc/pam.d/{{ item }}-local + remote_src: yes + owner: root + group: root + mode: '0644' + with_items: + - "system-auth" + - "password-auth" + + - name: | + "5.4.1 | L1 | PATCH | Ensure password creation requirements are configured + 5.4.2 | L1 | PATCH | Ensure lockout for failed password attempts is configured + 5.4.3 | L1 | PATCH | Ensure password reuse is limited + 5.4.4 | L1 | PATCH | Ensure password hashing algorithm is SHA-512" + file: + src: /etc/pam.d/{{ item }}-local + dest: /etc/pam.d/{{ item }} + state: link + force: yes + with_items: + - "system-auth" + - "password-auth" + when: + - rhel9cis_rule_5_4_1 or + rhel9cis_rule_5_4_2 or + rhel9cis_rule_5_4_3 or + rhel9cis_rule_5_4_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.4.1 + - rule_5.4.2 + - rule_5.4.3 + - rule_5.4.4 diff --git a/tasks/section_5/cis_5.5.1.x.yml b/tasks/section_5/cis_5.5.1.x.yml new file mode 100644 index 00000000..453f31b7 --- /dev/null +++ b/tasks/section_5/cis_5.5.1.x.yml @@ -0,0 +1,117 @@ +--- + +- name: "5.5.1.1 | L1 | PATCH | Ensure password expiration is 365 days or less" + lineinfile: + state: present + dest: /etc/login.defs + regexp: '^PASS_MAX_DAYS' + line: "PASS_MAX_DAYS {{ rhel9cis_pass['max_days'] }}" + when: + - rhel9cis_rule_5_5_1_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.1 + +- name: "5.5.1.2 | L1 | PATCH | Ensure minimum days between password changes is 7 or more" + lineinfile: + state: present + dest: /etc/login.defs + regexp: '^PASS_MIN_DAYS' + line: "PASS_MIN_DAYS {{ rhel9cis_pass['min_days'] }}" + when: + - rhel9cis_rule_5_5_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.2 + +- name: "5.5.1.3 | L1 | PATCH | Ensure password expiration warning days is 7 or more" + lineinfile: + state: present + dest: /etc/login.defs + regexp: '^PASS_WARN_AGE' + line: "PASS_WARN_AGE {{ rhel9cis_pass['warn_age'] }}" + when: + - rhel9cis_rule_5_5_1_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.3 + +- name: "5.5.1.4 | L1 | PATCH | Ensure inactive password lock is 30 days or less" + block: + - name: "5.5.1.4 | L1 | 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= + changed_when: false + failed_when: false + check_mode: no + register: rhel9cis_5_5_1_4_inactive_settings + + - name: "5.5.1.4 | L1 | PATCH | Ensure inactive password lock is 30 days or less | Set default inactive setting" + command: useradd -D -f {{ rhel9cis_inactivelock.lock_days }} + when: rhel9cis_5_5_1_4_inactive_settings.stdout | length == 0 + + - name: "5.5.1.4 | L1 | AUDIT | Ensure inactive password lock is 30 days or less | Getting user list" + shell: 'egrep ^[^:]+:[^\!*] /etc/shadow | cut -d: -f1' + check_mode: no + register: rhel_09_5_5_1_4_audit + changed_when: false + + - name: "5.5.1.4 | L1 | 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_09_5_5_1_4_audit.stdout_lines }}" + when: + - rhel9cis_rule_5_5_1_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.4 + +- name: "5.5.1.5 | L1 | PATCH | Ensure all users last password change date is in the past" + block: + - name: "5.5.1.5 | L1 | 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)) + failed_when: false + changed_when: false + check_mode: no + register: rhel9cis_5_5_1_5_currentut + + - name: "5.5.1.5 | L1 | 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_5_1_5_currentut.stdout }})print$1}'" + changed_when: false + failed_when: false + check_mode: no + register: rhel9cis_5_5_1_5_user_list + + - name: "5.5.1.5 | L1 | 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_5_1_5_user_list.stdout | length == 0 + + - name: "5.5.1.5 | L1 | AUDIT | Ensure all users last password change date is in the past | Alert on accounts with pw change in the future" + debug: + msg: "Warning! The following accounts have the last PW change date in the future: {{ rhel9cis_5_5_1_5_user_list.stdout_lines }}" + when: + - rhel9cis_5_5_1_5_user_list.stdout | length > 0 + - not rhel9cis_futurepwchgdate_autofix + + - name: "5.5.1.5 | L1 | PATCH | Ensure all users last password change date is in the past | Fix accounts with pw change in the future" + command: passwd --expire {{ item }} + when: + - rhel9cis_5_5_1_5_user_list | length > 0 + - rhel9cis_futurepwchgdate_autofix + with_items: + - "{{ rhel9cis_5_5_1_5_user_list.stdout_lines }}" + when: + - rhel9cis_rule_5_5_1_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.5 diff --git a/tasks/section_5/cis_5.5.x.yml b/tasks/section_5/cis_5.5.x.yml new file mode 100644 index 00000000..856e6b44 --- /dev/null +++ b/tasks/section_5/cis_5.5.x.yml @@ -0,0 +1,96 @@ +--- + +- name: "5.5.2 | L1 | PATCH | Ensure system accounts are secured" + block: + - name: "5.5.2 | L1 | Ensure system accounts are secured | Set nologin" + user: + name: "{{ item.id }}" + shell: /usr/sbin/nologin + with_items: + - "{{ rhel9cis_passwd }}" + when: + - item.id != "root" + - item.id != "sync" + - item.id != "shutdown" + - item.id != "halt" + - item.gid < rhel9cis_int_gid + - item.shell != " /bin/false" + - item.shell != " /usr/sbin/nologin" + + - name: "5.5.2 | L1 | PATCH | Ensure system accounts are secured | Lock accounts" + user: + name: "{{ item.id }}" + password_lock: true + with_items: + - "{{ rhel9cis_passwd }}" + when: + - item.id != "halt" + - item.id != "shutdown" + - item.id != "sync" + - item.id != "root" + - item.gid < rhel9cis_int_gid + - item.shell != " /bin/false" + - item.shell != " /usr/sbin/nologin" + when: + - rhel9cis_rule_5_5_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.2 + +- name: "5.5.3 | L1 | PATCH | Ensure default user shell timeout is 900 seconds or less" + blockinfile: + create: yes + mode: 0644 + dest: "{{ item.dest }}" + state: "{{ item.state }}" + marker: "# {mark} ANSIBLE MANAGED" + block: | + # Set session timeout - CIS ID RHEL-09-5.4.5 + 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') }}" } + when: + - rhel9cis_rule_5_5_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.3 + +- name: "5.5.4 | L1 | PATCH | Ensure default group for the root account is GID 0" + command: usermod -g 0 root + changed_when: false + failed_when: false + when: + - rhel9cis_rule_5_5_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.4 + +- name: "5.5.5 | L1 | PATCH | Ensure default user umask is 027 or more restrictive" + block: + - name: "5.5.5 | L1 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/bashrc" + replace: + path: /etc/bashrc + regexp: '(^\s+umask) 0[012][0-6]' + replace: '\1 027' + + - name: "5.5.5 | L1 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/profile" + replace: + path: /etc/profile + regexp: '(^\s+umask) 0[012][0-6]' + replace: '\1 027' + when: + - rhel9cis_rule_5_5_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.5 diff --git a/tasks/section_5/cis_5.6.yml b/tasks/section_5/cis_5.6.yml new file mode 100644 index 00000000..58eb7e54 --- /dev/null +++ b/tasks/section_5/cis_5.6.yml @@ -0,0 +1,35 @@ +--- + +# this will just display the list of consoles. The site will need to confirm the allowed consoles are correct and change manually if needed. +- name: "5.6 | L1 | AUDIT | Ensure root login is restricted to system console" + block: + - name: "5.6 | L1 | AUDIT | Ensure root login is restricted to system console | Check if securetty file exists" + stat: + path: /etc/securetty + register: rhel9cis_securetty_check + + - name: "5.6 | L1 | AUDIT | Ensure root login is restricted to system console | Capture consoles" + command: cat /etc/securetty + changed_when: false + register: rhel_09_5_6_audit + when: rhel9cis_securetty_check.stat.exists + + - name: "5.6 | L1 | AUDIT |Ensure root login is restricted to system console | Display Console" + debug: + msg: + - "These are the consoles with root login access, please review:" + - "{{ rhel_09_5_6_audit.stdout_lines }}" + when: rhel9cis_securetty_check.stat.exists + + - name: "5.6 | L1 | AUDIT | Ensure root login is restricted to system console | Display that no securetty file exists" + debug: + msg: + - "There is no /etc/securetty file, this has been removed by default in RHEL9" + when: not rhel9cis_securetty_check.stat.exists + when: + - rhel9cis_rule_5_6 + tags: + - level1-server + - level1-workstation + - audit + - rule_5.6 diff --git a/tasks/section_5/cis_5.7.yml b/tasks/section_5/cis_5.7.yml new file mode 100644 index 00000000..9e7bbec8 --- /dev/null +++ b/tasks/section_5/cis_5.7.yml @@ -0,0 +1,22 @@ +--- + +- name: "5.7 | L1 | PATCH | Ensure access to the su command is restricted" + block: + - name: "5.7 | L1 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" + lineinfile: + state: present + dest: /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.7 | L1 | PATCH | Ensure access to the su command is restricted | wheel group contains root" + user: + name: "{{ rhel9cis_sugroup_users }}" + groups: "{{ rhel9cis_sugroup | default('wheel') }}" + when: + - rhel9cis_rule_5_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.7 diff --git a/tasks/section_5/main.yml b/tasks/section_5/main.yml new file mode 100644 index 00000000..f290165e --- /dev/null +++ b/tasks/section_5/main.yml @@ -0,0 +1,27 @@ +--- + +- name: "SECTION | 5.1 | Configure time-based job schedulers" + include: cis_5.1.x.yml + +- name: "SECTION | 5.2 | Configure SSH Server" + include: cis_5.2.x.yml + +- name: "SECTION | 5.3 | Configure Profiles" + include: cis_5.3.x.yml + when: + - rhel9cis_use_authconfig + +- name: "SECTION | 5.4 | Configure PAM " + include: cis_5.4.x.yml + +- name: "SECTION | 5.5.1.x | Passwords and Accounts" + include: cis_5.5.1.x.yml + +- name: "SECTION | 5.5.x | System Accounts and User Settings" + include: cis_5.5.x.yml + +- name: "SECTION | 5.6 | Root Login" + include: cis_5.6.yml + +- name: Section | 5.7 | su Command Restriction + include: cis_5.7.yml diff --git a/tasks/section_6/cis_6.1.x.yml b/tasks/section_6/cis_6.1.x.yml new file mode 100644 index 00000000..ad162e34 --- /dev/null +++ b/tasks/section_6/cis_6.1.x.yml @@ -0,0 +1,294 @@ +--- + +- name: "6.1.1 | L2 | AUDIT | Audit system file permissions" + block: + - name: "6.1.1 | L2 | AUDIT | Audit system file permissions | Audit the packages" + shell: rpm -Va --nomtime --nosize --nomd5 --nolinkto + args: + warn: no + changed_when: false + failed_when: false + register: rhel9cis_6_1_1_packages_rpm + + - name: "6.1.1 | L2 | AUDIT | Audit system file permissions | Create list and warning" + block: + - name: "6.1.1 | L2 | Audit system file permissions | Add file discrepancy list to system" + copy: + dest: "{{ rhel9cis_rpm_audit_file }}" + content: "{{ rhel9cis_6_1_1_packages_rpm.stdout }}" + + - name: "6.1.1 | L2 | 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 }}" + when: rhel9cis_6_1_1_packages_rpm.stdout|length > 0 + + - name: "6.1.1 | L2 | 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 + when: + - rhel9cis_rule_6_1_1 + tags: + - level2-server + - level2-workstation + - audit + - rule_6.1.1 + +- name: "6.1.2 | L1 | PATCH | Ensure permissions on /etc/passwd are configured" + file: + dest: /etc/passwd + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_6_1_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.2 + +- name: "6.1.3 | L1 | PATCH | Ensure permissions on /etc/passwd- are configured" + file: + dest: /etc/passwd- + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_6_1_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.3 + +- name: "6.1.4 | L1 | PATCH | Ensure permissions on /etc/shadow are configured" + file: + dest: /etc/shadow + owner: root + group: root + mode: 0000 + when: + - rhel9cis_rule_6_1_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.4 + +- name: "6.1.5 | L1 | PATCH | Ensure permissions on /etc/shadow- are configured" + file: + dest: /etc/shadow- + owner: root + group: root + mode: 0000 + when: + - rhel9cis_rule_6_1_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.5 + +- name: "6.1.6 | L1 | PATCH | Ensure permissions on /etc/gshadow are configured" + file: + dest: /etc/gshadow + owner: root + group: root + mode: 0000 + when: + - rhel9cis_rule_6_1_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.6 + +- name: "6.1.7 | L1 | PATCH | Ensure permissions on /etc/gshadow- are configured" + file: + dest: /etc/gshadow- + owner: root + group: root + mode: 0000 + when: + - rhel9cis_rule_6_1_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.7 + +- name: "6.1.8 | L1 | PATCH | Ensure permissions on /etc/group are configured" + file: + dest: /etc/group- + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_6_1_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.8 + +- name: "6.1.9 | L1 | PATCH | Ensure permissions on /etc/group- are configured" + file: + dest: /etc/group- + owner: root + group: root + mode: 0644 + when: + - rhel9cis_rule_6_1_9 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.9 + +- name: "6.1.10 | L1 | PATCH | Ensure no world writable files exist" + block: + - name: "6.1.10 | L1 | 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 + failed_when: false + changed_when: false + register: rhel_09_6_1_10_perms_results + + - name: "6.1.10 | L1 | 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" + when: + - rhel_09_6_1_10_perms_results.stdout is not defined + + - name: "6.1.10 | L1 | 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_09_6_1_10_perms_results.stdout_lines }}" + when: + - rhel_09_6_1_10_perms_results.stdout_lines is defined + - rhel9cis_no_world_write_adjust + when: + - rhel9cis_rule_6_1_10 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.10 + +- name: "6.1.11 | L1 | AUDIT | Ensure no unowned files or directories exist" + block: + - name: "6.1.11 | L1 | AUDIT | Ensure no unowned files or directories exist | Finding all unowned files or directories" + command: find "{{ item.mount }}" -xdev -nouser + check_mode: false + failed_when: false + changed_when: false + with_items: "{{ ansible_mounts }}" + register: rhel_09_6_1_11_audit + when: item['device'].startswith('/dev') and not 'bind' in item['options'] + + - name: "6.1.11 | L1 | 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_09_6_1_11_audit.results }}" + when: + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + when: + - rhel9cis_rule_6_1_11 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.1.11 + +- name: "6.1.12 | L1 | AUDIT | Ensure no ungrouped files or directories exist" + block: + - name: "6.1.12 | L1 | AUDIT | Ensure no ungrouped files or directories exist | Finding all ungrouped files or directories" + command: find "{{ item.mount }}" -xdev -nogroup + check_mode: false + failed_when: false + changed_when: false + register: rhel_09_6_1_12_audit + with_items: "{{ ansible_mounts }}" + when: item['device'].startswith('/dev') and not 'bind' in item['options'] + + - name: "6.1.12 | L1 | 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_09_6_1_12_audit.results }}" + when: + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + when: + - rhel9cis_rule_6_1_12 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.12 + +- name: "6.1.13 | L1 | AUDIT | Audit SUID executables" + block: + - name: "6.1.13 | L1 | 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 + failed_when: false + changed_when: false + register: rhel_09_6_1_13_perms_results + with_items: "{{ ansible_mounts }}" + + - name: "6.1.13 | L1 | 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 + when: + - rhel_09_6_1_13_perms_results.stdout is not defined + + - name: "6.1.13 | L1 | 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_09_6_1_13_perms_results.stdout_lines }}" + when: + - rhel_09_6_1_13_perms_results.stdout is defined + when: + - rhel9cis_rule_6_1_13 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.1.13 + +- name: "6.1.14 | L1 | AUDIT | Audit SGID executables" + block: + - name: "6.1.14 | L1 | 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 + changed_when: false + register: rhel_09_6_1_14_perms_results + with_items: "{{ ansible_mounts }}" + + - name: "6.1.14 | L1 | 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_09_6_1_14_perms_results.stdout is not defined + + - name: "6.1.14 | L1 | 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_09_6_1_14_perms_results.stdout_lines }}" + when: + - rhel_09_6_1_14_perms_results.stdout is defined + when: + - rhel9cis_rule_6_1_14 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.1.14 diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml new file mode 100644 index 00000000..1edab263 --- /dev/null +++ b/tasks/section_6/cis_6.2.x.yml @@ -0,0 +1,519 @@ +--- + +- name: "6.2.1 | L1 | AUDIT | Ensure password fields are not empty" + command: passwd -l {{ item }} + changed_when: false + failed_when: false + with_items: "{{ empty_password_accounts.stdout_lines }}" + when: + - empty_password_accounts.rc + - rhel9cis_rule_6_2_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.1 + +- name: "6.2.2 | L1 | PATCH | Ensure no legacy '+' entries exist in /etc/passwd" + command: sed -i '/^+/ d' /etc/passwd + changed_when: false + failed_when: false + when: + - rhel9cis_rule_6_2_2 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.2 + - skip_ansible_lint + +- name: "6.2.3 | L1 | PATCH | Ensure root PATH Integrity" + block: + - name: "6.2.3 | L1 | AUDIT | Ensure root PATH Integrity | Determine empty value" + shell: 'echo $PATH | grep ::' + check_mode: no + register: path_colon + changed_when: False + failed_when: path_colon.rc == 0 + + - name: "6.2.3 | L1 | AUDIT | Ensure root PATH Integrity | Determin colon end" + shell: 'echo $PATH | grep :$' + check_mode: no + register: path_colon_end + changed_when: False + failed_when: path_colon_end.rc == 0 + + - name: "6.2.3 | L1 | 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'" + check_mode: no + register: dot_in_path + changed_when: False + failed_when: '"." in dot_in_path.stdout_lines' + + - name: "6.2.3 | L1 | AUDIT | Ensure root PATH Integrity | Alert on empty value, colon end, and dot in path" + debug: + msg: + - "The following paths have an empty value: {{ path_colon.stdout_lines }}" + - "The following paths have colon end: {{ path_colon_end.stdout_lines }}" + - "The following paths have a dot in the path: {{ dot_in_path.stdout_lines }}" + + - name: "6.2.3 | L1 | PATCH | Ensure root PATH Integrity (Scored) | Determine rights and owner" + file: > + path='{{ item }}' + follow=yes + state=directory + owner=root + mode='o-w,g-w' + with_items: "{{ dot_in_path.stdout_lines }}" + when: + - rhel9cis_rule_6_2_3 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.3 + +- name: "6.2.4 | L1 | PATCH | Ensure no legacy '+' entries exist in /etc/shadow" + command: sed -i '/^+/ d' /etc/shadow + changed_when: false + failed_when: false + when: + - rhel9cis_rule_6_2_4 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.4 + - skip_ansible_lint + +- name: "6.2.5 | L1 | PATCH | Ensure no legacy '+' entries exist in /etc/group" + command: sed -i '/^+/ d' /etc/group + changed_when: false + failed_when: false + when: + - rhel9cis_rule_6_2_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.5 + - skip_ansible_lint + +- name: "6.2.6 | L1 | PATCH | Ensure root is the only UID 0 account" + command: passwd -l {{ item }} + changed_when: false + failed_when: false + with_items: "{{ uid_zero_accounts_except_root.stdout_lines }}" + when: + - uid_zero_accounts_except_root.rc + - rhel9cis_rule_6_2_6 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.6 + +- name: "6.2.7 | L1 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + block: + - name: "6.2.7 | L1 | AUDIT | Ensure users' home directories permissions are 750 or more restrictive" + stat: + path: "{{ item }}" + with_items: "{{ rhel9cis_passwd | selectattr('uid', '>=', rhel9cis_int_gid) | selectattr('uid', '!=', 65534) | map(attribute='dir') | list }}" + register: rhel_09_6_2_7_audit + + - name: "6.2.7 | L1 | 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_09_6_2_7_patch_audit.stdout | length > 0 + register: rhel_09_6_2_7_patch_audit + when: + - ansible_check_mode + - item.1.exists + with_together: + - "{{ rhel_09_6_2_7_audit.results | map(attribute='item') | list }}" + - "{{ rhel_09_6_2_7_audit.results | map(attribute='stat') | list }}" + loop_control: + label: "{{ item.0 }}" + + - name: "6.2.7 | L1 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + file: + path: "{{ item.0 }}" + recurse: yes + mode: a-st,g-w,o-rwx + register: rhel_09_6_2_7_patch + when: + - not ansible_check_mode + - item.1.exists + with_together: + - "{{ rhel_09_6_2_7_audit.results | map(attribute='item') | list }}" + - "{{ rhel_09_6_2_7_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.7 | L1 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" + acl: + path: "{{ item.0 }}" + default: yes + state: present + recursive: yes + etype: "{{ item.1.etype }}" + permissions: "{{ item.1.mode }}" + when: not rhel9cis_system_is_container + with_nested: + - "{{ (ansible_check_mode | ternary(rhel_09_6_2_7_patch_audit, rhel_09_6_2_7_patch)).results | + rejectattr('skipped', 'defined') | map(attribute='item') | map('first') | list }}" + - + - etype: group + mode: rx + - etype: other + mode: '0' + when: + - rhel9cis_rule_6_2_7 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.7 + +- name: "6.2.8 | L1 | 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 }}" + when: + - item.uid >= rhel9cis_int_gid + - rhel9cis_rule_6_2_8 + tags: + - skip_ansible_lint # settings found on 6_2_7 + - level1-server + - level1-workstation + - patch + - rule_6.2.8 + +- name: "6.2.9 | L1 | PATCH | Ensure users' dot files are not group or world-writable" + block: + - name: "6.2.9 | L1 | AUDIT | Ensure users' dot files are not group or world-writable | Check for files" + shell: find /home/ -name "\.*" -perm /g+w,o+w + changed_when: false + failed_when: false + register: rhel9cis_6_2_9_audit + + - name: "6.2.9 | L1 | 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" + when: + - rhel9cis_6_2_9_audit.stdout is not defined + + - name: "6.2.9 | L1 | 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_9_audit.stdout_lines }}" + when: + - rhel9cis_6_2_9_audit.stdout is defined + - rhel9cis_dotperm_ansiblemanaged + when: + - rhel9cis_rule_6_2_9 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.9 + +- name: "6.2.10 | L1 | PATCH | Ensure no users have .forward files" + file: + state: absent + dest: "~{{ item }}/.forward" + with_items: "{{ users.stdout_lines }}" + when: + - rhel9cis_rule_6_2_10 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.10 + +- name: "6.2.11 | L1 | PATCH | Ensure no users have .netrc files" + file: + state: absent + dest: "~{{ item }}/.netrc" + with_items: "{{ users.stdout_lines }}" + when: + - rhel9cis_rule_6_2_11 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.11 + +- name: "6.2.12 | L1 | PATCH | Ensure users' .netrc Files are not group or world accessible" + command: /bin/true + changed_when: false + failed_when: false + when: + - rhel9cis_rule_6_2_12 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.12 + +- name: "6.2.13 | L1 | PATCH | Ensure no users have .rhosts files" + file: + state: absent + dest: "~{{ item }}/.rhosts" + with_items: "{{ users.stdout_lines }}" + when: + - rhel9cis_rule_6_2_13 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.13 + +- name: "6.2.14 | L1 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group" + block: + - name: "6.2.14 | L1 | 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: passwd_gid_check + + - name: "6.2.14 | L1 | 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: passwd_gid_check.stdout is not defined + + - name: "6.2.14 | L1 | 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): {{ passwd_gid_check.stdout_lines | join (', ') }}" + when: passwd_gid_check.stdout is defined + when: + - rhel9cis_rule_6_2_14 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.2.14 + +- name: "6.2.15 | L1 | AUDIT Ensure no duplicate UIDs exist" + block: + - name: "6.2.15 | L1 | 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" + changed_when: false + failed_when: false + register: user_uid_check + + - name: "6.2.15 | L1 | 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: user_uid_check.stdout is not defined + + - name: "6.2.15 | L1 | AUDIT| Ensure no duplicate UIDs exist | Print warning about users with duplicate UIDs" + debug: + msg: "Warning: The following users have UIDs that are duplicates: {{ user_uid_check.stdout_lines }}" + when: user_uid_check.stdout is defined + when: + - rhel9cis_rule_6_2_15 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.15 + +- name: "6.2.16 | L1 | AUDIT | Ensure no duplicate GIDs exist" + block: + - name: "6.2.16 | L1 | 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" + changed_when: false + failed_when: false + register: user_user_check + + - name: "6.2.16 | L1 | 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: user_user_check.stdout is not defined + + - name: "6.2.16 | L1 | AUDIT | Ensure no duplicate GIDs exist | Print warning about users with duplicate GIDs" + debug: + msg: "Warning: The following groups have duplicate GIDs: {{ user_user_check.stdout_lines }}" + when: user_user_check.stdout is defined + when: + - rhel9cis_rule_6_2_16 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.2.16 + +- name: "6.2.17 | L1 | AUDIT | Ensure no duplicate user names exist" + block: + - name: "6.2.17 | L1 | 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" + changed_when: false + failed_when: false + register: user_username_check + + - name: "6.2.17 | L1 | 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: user_username_check.stdout is not defined + + - name: "6.2.17 | L1 | AUDIT | Ensure no duplicate user names exist | Print warning about users with duplicate User Names" + debug: + msg: "Warning: The following user names are duplicates: {{ user_username_check.stdout_lines }}" + when: user_username_check.stdout is defined + when: + - rhel9cis_rule_6_2_17 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.2.17 + +- name: "6.2.18 | L1 | AUDIT |Ensure no duplicate group names exist" + block: + - name: "6.2.18 | L1 | AUDIT | Ensure no duplicate group names exist | Check for duplicate group names" + shell: 'getent passwd | cut -d: -f1 | sort -n | uniq -d' + changed_when: false + failed_when: false + check_mode: no + register: group_group_check + + - name: "6.2.18 | L1 | 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: group_group_check.stdout is defined + + - name: "6.2.18 | L1 | AUDIT | Ensure no duplicate group names exist | Print warning about users with duplicate group names" + debug: + msg: "Warning: The following group names are duplicates: {{ group_group_check.stdout_lines }}" + when: group_group_check.stdout is not defined + when: + - rhel9cis_rule_6_2_18 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.2.18 + +- name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty" + block: + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Check for shadow group and pull group id" + shell: "getent group shadow | cut -d: -f3" + changed_when: false + failed_when: false + check_mode: no + register: rhel9cis_shadow_gid + + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Check /etc/group for empty shadow group" + shell: grep ^shadow:[^:]*:[^:]*:[^:]+ /etc/group + changed_when: false + failed_when: false + check_mode: no + register: rhel9cis_empty_shadow + + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Check for users assigned to shadow" + shell: "getent passwd | awk -F: '$4 == '{{ rhel9cis_shadow_gid.stdout }}' {print $1}'" + changed_when: false + failed_when: false + check_mode: no + register: rhel9cis_shadow_passwd + + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Alert shadow group is empty and no users assigned" + debug: + msg: + - " Good News! The shadow group is empty and there are no users assigned to shadow" + when: + - rhel9cis_empty_shadow.stdout | length == 0 + - rhel9cis_shadow_passwd.stdout | length == 0 + + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Alert shadow group is not empty" + debug: + msg: + - "Alert! The shadow group is not empty" + when: + - rhel9cis_empty_shadow.stdout | length > 0 + + - name: "6.2.19 | L1 | AUDIT | Ensure shadow group is empty | Alert users are using shadow group" + debug: + msg: + - "Alert! The following users are assigned to the shadow group, please assing them to the appropriate group" + - "{{ rhel9cis_shadow_passwd.stdout_lines }}" + when: + - rhel9cis_shadow_passwd.stdout | length > 0 + when: + - rhel9cis_rule_6_2_19 + tags: + - level1-server + - level1-workstation + - audit + - rule_6.2.19 + +- name: "6.2.20 | L1 | PATCH | Ensure all users' home directories exist" + block: + - name: "6.2.20 | L1 | AUDIT | Ensure all users' home directories exist" + stat: + path: "{{ item }}" + register: rhel_09_6_2_20_audit + with_items: "{{ rhel9cis_passwd | selectattr('uid', '>=', rhel9cis_int_gid) | selectattr('uid', '!=', 65534) | map(attribute='dir') | list }}" + + - name: "6.2.20 | L1 | AUDIT | Ensure all users' home directories exist" + command: find -H {{ item.0 | quote }} -not -type l -perm /027 + check_mode: false + changed_when: rhel_09_6_2_20_patch_audit.stdout | length > 0 + register: rhel_09_6_2_20_patch_audit + when: + - ansible_check_mode + - item.1.exists + with_together: + - "{{ rhel_09_6_2_20_audit.results | map(attribute='item') | list }}" + - "{{ rhel_09_6_2_20_audit.results | map(attribute='stat') | list }}" + loop_control: + label: "{{ item.0 }}" + + - name: "6.2.20 | L1 | PATCH | Ensure all users' home directories exist" + file: + path: "{{ item.0 }}" + recurse: yes + mode: a-st,g-w,o-rwx + register: rhel_09_6_2_20_patch + when: + - not ansible_check_mode + - item.1.exists + with_together: + - "{{ rhel_09_6_2_20_audit.results | map(attribute='item') | list }}" + - "{{ rhel_09_6_2_20_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.20 | L1 | PATCH | Ensure all users' home directories exist" + acl: + path: "{{ item.0 }}" + default: yes + state: present + recursive: yes + etype: "{{ item.1.etype }}" + permissions: "{{ item.1.mode }}" + when: not rhel9cis_system_is_container + with_nested: + - "{{ (ansible_check_mode | ternary(rhel_09_6_2_20_patch_audit, rhel_09_6_2_20_patch)).results | + rejectattr('skipped', 'defined') | map(attribute='item') | map('first') | list }}" + - + - etype: group + mode: rx + - etype: other + mode: '0' + when: + - rhel9cis_rule_6_2_20 + tags: + - level1-server + - level1-workstation + - patch + - rule_6.2.20 diff --git a/tasks/section_6/main.yml b/tasks/section_6/main.yml new file mode 100644 index 00000000..bf6943af --- /dev/null +++ b/tasks/section_6/main.yml @@ -0,0 +1,7 @@ +--- + +- name: "SECTION | 6.1 | System File Permissions" + include: cis_6.1.x.yml + +- name: "SECTION | 6.2 | User and Group Settings" + include: cis_6.2.x.yml diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 new file mode 100644 index 00000000..ec9dac64 --- /dev/null +++ b/templates/ansible_vars_goss.yml.j2 @@ -0,0 +1,474 @@ +## metadata for Audit benchmark +benchmark_version: '1.0.1' + +# Set if genuine RHEL (subscription manager check) not for derivatives e.g. CentOS +is_redhat_os: {% if ansible_distribution == "RedHat" %}true{% else %}false{% endif %} + +rhel9cis_os_distribution: {{ ansible_distribution | lower }} + +# timeout for each command to run where set - default = 10seconds/10000ms +timeout_ms: {{ audit_cmd_timeout }} + +# Taken from LE rhel9-cis +rhel9cis_notauto: {{ rhel9cis_notauto }} +rhel9cis_section1: {{ rhel9cis_section1 }} +rhel9cis_section2: {{ rhel9cis_section2 }} +rhel9cis_section3: {{ rhel9cis_section3 }} +rhel9cis_section4: {{ rhel9cis_section4 }} +rhel9cis_section5: {{ rhel9cis_section5 }} +rhel9cis_section6: {{ rhel9cis_section6 }} + +rhel9cis_level_1: {{ rhel9cis_level_1 }} +rhel9cis_level_2: {{ rhel9cis_level_2 }} + +rhel9cis_selinux_disable: {{ rhel9cis_selinux_disable }} + + + +# to enable rules that may have IO impact on a system e.g. full filesystem scans or CPU heavy +run_heavy_tests: true +{% if rhel9cis_legacy_boot is defined %} +rhel9cis_legacy_boot: {{ rhel9cis_legacy_boot }} +{% endif %} + + +rhel9cis_set_boot_pass: {{ rhel9cis_set_boot_pass }} +# These variables correspond with the CIS rule IDs or paragraph numbers defined in +# the CIS benchmark documents. +# PLEASE NOTE: These work in coordination with the section # group variables and tags. +# You must enable an entire section in order for the variables below to take effect. +# Section 1 rules +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 }} +rhel9cis_rule_1_1_1_4: {{ rhel9cis_rule_1_1_1_4 }} +rhel9cis_rule_1_1_2: {{ rhel9cis_rule_1_1_2 }} +rhel9cis_rule_1_1_3: {{ rhel9cis_rule_1_1_3 }} +rhel9cis_rule_1_1_4: {{ rhel9cis_rule_1_1_4 }} +rhel9cis_rule_1_1_5: {{ rhel9cis_rule_1_1_5 }} +rhel9cis_rule_1_1_6: {{ rhel9cis_rule_1_1_6 }} +rhel9cis_rule_1_1_7: {{ rhel9cis_rule_1_1_7 }} +rhel9cis_rule_1_1_8: {{ rhel9cis_rule_1_1_8 }} +rhel9cis_rule_1_1_9: {{ rhel9cis_rule_1_1_9 }} +rhel9cis_rule_1_1_10: {{ rhel9cis_rule_1_1_10 }} +rhel9cis_rule_1_1_11: {{ rhel9cis_rule_1_1_11 }} +rhel9cis_rule_1_1_12: {{ rhel9cis_rule_1_1_12 }} +rhel9cis_rule_1_1_13: {{ rhel9cis_rule_1_1_13 }} +rhel9cis_rule_1_1_14: {{ rhel9cis_rule_1_1_14 }} +rhel9cis_rule_1_1_15: {{ rhel9cis_rule_1_1_15 }} +rhel9cis_rule_1_1_16: {{ rhel9cis_rule_1_1_16 }} +rhel9cis_rule_1_1_17: {{ rhel9cis_rule_1_1_17 }} +rhel9cis_rule_1_1_18: {{ rhel9cis_rule_1_1_18 }} +rhel9cis_rule_1_1_19: {{ rhel9cis_rule_1_1_19 }} +rhel9cis_rule_1_1_20: {{ rhel9cis_rule_1_1_20 }} +rhel9cis_rule_1_1_21: {{ rhel9cis_rule_1_1_21 }} +rhel9cis_rule_1_1_22: {{ rhel9cis_rule_1_1_22 }} +rhel9cis_rule_1_1_23: {{ rhel9cis_rule_1_1_23 }} +rhel9cis_rule_1_2_1: {% if ansible_distribution == "RedHat" %}True{% else %}False{% endif %} # Only run if Redhat and Subscribed +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 }} +rhel9cis_rule_1_2_5: {{ rhel9cis_rule_1_2_5 }} +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 }} +rhel9cis_rule_1_4_1: {{ rhel9cis_rule_1_4_1 }} +rhel9cis_rule_1_4_2: {{ rhel9cis_rule_1_4_2 }} +rhel9cis_rule_1_5_1: {{ rhel9cis_rule_1_5_1 }} +rhel9cis_rule_1_5_2: {{ rhel9cis_rule_1_5_2 }} +rhel9cis_rule_1_5_3: {{ rhel9cis_rule_1_5_3 }} +rhel9cis_rule_1_6_1: {{ rhel9cis_rule_1_6_1 }} +rhel9cis_rule_1_6_2: {{ rhel9cis_rule_1_6_2 }} +rhel9cis_rule_1_7_1_1: {{ rhel9cis_rule_1_7_1_1 }} +rhel9cis_rule_1_7_1_2: {{ rhel9cis_rule_1_7_1_2 }} +rhel9cis_rule_1_7_1_3: {{ rhel9cis_rule_1_7_1_3 }} +rhel9cis_rule_1_7_1_4: {{ rhel9cis_rule_1_7_1_4 }} +rhel9cis_rule_1_7_1_5: {{ rhel9cis_rule_1_7_1_5 }} +rhel9cis_rule_1_7_1_6: {{ rhel9cis_rule_1_7_1_6 }} +rhel9cis_rule_1_7_1_7: {{ rhel9cis_rule_1_7_1_7 }} +rhel9cis_rule_1_8_1_1: {{ rhel9cis_rule_1_8_1_1 }} +rhel9cis_rule_1_8_1_2: {{ rhel9cis_rule_1_8_1_2 }} +rhel9cis_rule_1_8_1_3: {{ rhel9cis_rule_1_8_1_3 }} +rhel9cis_rule_1_8_1_4: {{ rhel9cis_rule_1_8_1_4 }} +rhel9cis_rule_1_8_1_5: {{ rhel9cis_rule_1_8_1_5 }} +rhel9cis_rule_1_8_1_6: {{ rhel9cis_rule_1_8_1_6 }} +rhel9cis_rule_1_8_2: {{ rhel9cis_rule_1_8_2 }} +rhel9cis_rule_1_9: {{ rhel9cis_rule_1_9 }} +rhel9cis_rule_1_10: {{ rhel9cis_rule_1_10 }} +rhel9cis_rule_1_11: {{ rhel9cis_rule_1_11 }} + +# section 2 rules +rhel9cis_rule_2_1_1: {{ rhel9cis_rule_2_1_1 }} +rhel9cis_rule_2_2_1_1: {{ rhel9cis_rule_2_2_1_1 }} +rhel9cis_rule_2_2_1_2: {{ rhel9cis_rule_2_2_1_2 }} +rhel9cis_rule_2_2_2: {{ rhel9cis_rule_2_2_2 }} +rhel9cis_rule_2_2_3: {{ rhel9cis_rule_2_2_3 }} +rhel9cis_rule_2_2_4: {{ rhel9cis_rule_2_2_4 }} +rhel9cis_rule_2_2_5: {{ rhel9cis_rule_2_2_5 }} +rhel9cis_rule_2_2_6: {{ rhel9cis_rule_2_2_6 }} +rhel9cis_rule_2_2_7: {{ rhel9cis_rule_2_2_7 }} +rhel9cis_rule_2_2_8: {{ rhel9cis_rule_2_2_8 }} +rhel9cis_rule_2_2_9: {{ rhel9cis_rule_2_2_9 }} +rhel9cis_rule_2_2_10: {{ rhel9cis_rule_2_2_10 }} +rhel9cis_rule_2_2_11: {{ rhel9cis_rule_2_2_11 }} +rhel9cis_rule_2_2_12: {{ rhel9cis_rule_2_2_12 }} +rhel9cis_rule_2_2_13: {{ rhel9cis_rule_2_2_13 }} +rhel9cis_rule_2_2_14: {{ rhel9cis_rule_2_2_14 }} +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_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 }} + + +# Section 3 rules +rhel9cis_rule_3_1_1: {{ rhel9cis_rule_3_1_1 }} +rhel9cis_rule_3_1_2: {{ rhel9cis_rule_3_1_2 }} +rhel9cis_rule_3_2_1: {{ rhel9cis_rule_3_2_1 }} +rhel9cis_rule_3_2_2: {{ rhel9cis_rule_3_2_2 }} +rhel9cis_rule_3_2_3: {{ rhel9cis_rule_3_2_3 }} +rhel9cis_rule_3_2_4: {{ rhel9cis_rule_3_2_4 }} +rhel9cis_rule_3_2_5: {{ rhel9cis_rule_3_2_5 }} +rhel9cis_rule_3_2_6: {{ rhel9cis_rule_3_2_6 }} +rhel9cis_rule_3_2_7: {{ rhel9cis_rule_3_2_7 }} +rhel9cis_rule_3_2_8: {{ rhel9cis_rule_3_2_8 }} +rhel9cis_rule_3_2_9: {{ rhel9cis_rule_3_2_9 }} +rhel9cis_rule_3_3_1: {{ rhel9cis_rule_3_3_1 }} +rhel9cis_rule_3_3_2: {{ rhel9cis_rule_3_3_2 }} +rhel9cis_rule_3_3_3: {{ rhel9cis_rule_3_3_3 }} +rhel9cis_rule_3_3_4: {{ rhel9cis_rule_3_3_4 }} +rhel9cis_rule_3_4_1_1: {{ rhel9cis_rule_3_4_1_1 }} +rhel9cis_rule_3_4_2_1: {{ rhel9cis_rule_3_4_2_1 }} +rhel9cis_rule_3_4_2_2: {{ rhel9cis_rule_3_4_2_2 }} +rhel9cis_rule_3_4_2_3: {{ rhel9cis_rule_3_4_2_3 }} +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_5: {{ rhel9cis_rule_3_5 }} +rhel9cis_rule_3_6: {{ rhel9cis_rule_3_6 }} + + +# Section 4 rules +rhel9cis_rule_4_1_1_1: {{ rhel9cis_rule_4_1_1_1 }} +rhel9cis_rule_4_1_1_2: {{ rhel9cis_rule_4_1_1_2 }} +rhel9cis_rule_4_1_1_3: {{ rhel9cis_rule_4_1_1_3 }} +rhel9cis_rule_4_1_1_4: {{ rhel9cis_rule_4_1_1_4 }} +rhel9cis_rule_4_1_2_1: {{ rhel9cis_rule_4_1_2_1 }} +rhel9cis_rule_4_1_2_2: {{ rhel9cis_rule_4_1_2_2 }} +rhel9cis_rule_4_1_2_3: {{ rhel9cis_rule_4_1_2_3 }} +rhel9cis_rule_4_1_3: {{ rhel9cis_rule_4_1_3 }} +rhel9cis_rule_4_1_4: {{ rhel9cis_rule_4_1_4 }} +rhel9cis_rule_4_1_5: {{ rhel9cis_rule_4_1_5 }} +rhel9cis_rule_4_1_6: {{ rhel9cis_rule_4_1_6 }} +rhel9cis_rule_4_1_7: {{ rhel9cis_rule_4_1_7 }} +rhel9cis_rule_4_1_8: {{ rhel9cis_rule_4_1_8 }} +rhel9cis_rule_4_1_9: {{ rhel9cis_rule_4_1_9 }} +rhel9cis_rule_4_1_10: {{ rhel9cis_rule_4_1_10 }} +rhel9cis_rule_4_1_11: {{ rhel9cis_rule_4_1_11 }} +rhel9cis_rule_4_1_12: {{ rhel9cis_rule_4_1_12 }} +rhel9cis_rule_4_1_13: {{ rhel9cis_rule_4_1_13 }} +rhel9cis_rule_4_1_14: {{ rhel9cis_rule_4_1_14 }} +rhel9cis_rule_4_1_15: {{ rhel9cis_rule_4_1_15 }} +rhel9cis_rule_4_1_16: {{ rhel9cis_rule_4_1_16 }} +rhel9cis_rule_4_1_17: {{ rhel9cis_rule_4_1_17 }} +rhel9cis_rule_4_2_1_1: {{ rhel9cis_rule_4_2_1_1 }} +rhel9cis_rule_4_2_1_2: {{ rhel9cis_rule_4_2_1_2 }} +rhel9cis_rule_4_2_1_3: {{ rhel9cis_rule_4_2_1_3 }} +rhel9cis_rule_4_2_1_4: {{ rhel9cis_rule_4_2_1_4 }} +rhel9cis_rule_4_2_1_5: {{ rhel9cis_rule_4_2_1_5 }} +rhel9cis_rule_4_2_1_6: {{ rhel9cis_rule_4_2_1_6 }} +rhel9cis_rule_4_2_2_1: {{ rhel9cis_rule_4_2_2_1 }} +rhel9cis_rule_4_2_2_2: {{ rhel9cis_rule_4_2_2_2 }} +rhel9cis_rule_4_2_2_3: {{ rhel9cis_rule_4_2_2_3 }} +rhel9cis_rule_4_2_3: {{ rhel9cis_rule_4_2_3 }} +rhel9cis_rule_4_3: {{ rhel9cis_rule_4_3 }} + +# Section 5 +rhel9cis_rule_5_1_1: {{ rhel9cis_rule_5_1_1 }} +rhel9cis_rule_5_1_2: {{ rhel9cis_rule_5_1_2 }} +rhel9cis_rule_5_1_3: {{ rhel9cis_rule_5_1_3 }} +rhel9cis_rule_5_1_4: {{ rhel9cis_rule_5_1_4 }} +rhel9cis_rule_5_1_5: {{ rhel9cis_rule_5_1_5 }} +rhel9cis_rule_5_1_6: {{ rhel9cis_rule_5_1_6 }} +rhel9cis_rule_5_1_7: {{ rhel9cis_rule_5_1_7 }} +rhel9cis_rule_5_1_8: {{ rhel9cis_rule_5_1_8 }} + +rhel9cis_rule_5_2_1: {{ rhel9cis_rule_5_2_1 }} +rhel9cis_rule_5_2_2: {{ rhel9cis_rule_5_2_2 }} +rhel9cis_rule_5_2_3: {{ rhel9cis_rule_5_2_3 }} +rhel9cis_rule_5_2_4: {{ rhel9cis_rule_5_2_4 }} +rhel9cis_rule_5_2_5: {{ rhel9cis_rule_5_2_5 }} +rhel9cis_rule_5_2_6: {{ rhel9cis_rule_5_2_6 }} +rhel9cis_rule_5_2_7: {{ rhel9cis_rule_5_2_7 }} +rhel9cis_rule_5_2_8: {{ rhel9cis_rule_5_2_8 }} +rhel9cis_rule_5_2_9: {{ rhel9cis_rule_5_2_9 }} +rhel9cis_rule_5_2_10: {{ rhel9cis_rule_5_2_10 }} +rhel9cis_rule_5_2_11: {{ rhel9cis_rule_5_2_11 }} +rhel9cis_rule_5_2_12: {{ rhel9cis_rule_5_2_12 }} +rhel9cis_rule_5_2_13: {{ rhel9cis_rule_5_2_13 }} +rhel9cis_rule_5_2_14: {{ rhel9cis_rule_5_2_14 }} +rhel9cis_rule_5_2_15: {{ rhel9cis_rule_5_2_15 }} +rhel9cis_rule_5_2_16: {{ rhel9cis_rule_5_2_16 }} +rhel9cis_rule_5_2_17: {{ rhel9cis_rule_5_2_17 }} +rhel9cis_rule_5_2_18: {{ rhel9cis_rule_5_2_18 }} +rhel9cis_rule_5_2_19: {{ rhel9cis_rule_5_2_19 }} +rhel9cis_rule_5_2_20: {{ rhel9cis_rule_5_2_20 }} + +rhel9cis_rule_5_3_1: {{ rhel9cis_rule_5_3_1 }} +rhel9cis_rule_5_3_2: {{ rhel9cis_rule_5_3_2 }} +rhel9cis_rule_5_3_3: {{ rhel9cis_rule_5_3_3 }} + +rhel9cis_rule_5_4_1: {{ rhel9cis_rule_5_4_1 }} +rhel9cis_rule_5_4_2: {{ rhel9cis_rule_5_4_2 }} +rhel9cis_rule_5_4_3: {{ rhel9cis_rule_5_4_3 }} +rhel9cis_rule_5_4_4: {{ rhel9cis_rule_5_4_4 }} + +rhel9cis_rule_5_5_1_1: {{ rhel9cis_rule_5_5_1_1 }} +rhel9cis_rule_5_5_1_2: {{ rhel9cis_rule_5_5_1_2 }} +rhel9cis_rule_5_5_1_3: {{ rhel9cis_rule_5_5_1_3 }} +rhel9cis_rule_5_5_1_4: {{ rhel9cis_rule_5_5_1_4 }} +rhel9cis_rule_5_5_1_5: {{ rhel9cis_rule_5_5_1_5 }} + +rhel9cis_rule_5_5_2: {{ rhel9cis_rule_5_5_2 }} +rhel9cis_rule_5_5_3: {{ rhel9cis_rule_5_5_3 }} +rhel9cis_rule_5_5_4: {{ rhel9cis_rule_5_5_4 }} +rhel9cis_rule_5_5_5: {{ rhel9cis_rule_5_5_5 }} + +rhel9cis_rule_5_6: {{ rhel9cis_rule_5_6 }} +rhel9cis_rule_5_7: {{ rhel9cis_rule_5_7 }} + +# Section 6 +rhel9cis_rule_6_1_1: {{ rhel9cis_rule_6_1_1 }} +rhel9cis_rule_6_1_2: {{ rhel9cis_rule_6_1_2 }} +rhel9cis_rule_6_1_3: {{ rhel9cis_rule_6_1_3 }} +rhel9cis_rule_6_1_4: {{ rhel9cis_rule_6_1_4 }} +rhel9cis_rule_6_1_5: {{ rhel9cis_rule_6_1_5 }} +rhel9cis_rule_6_1_6: {{ rhel9cis_rule_6_1_6 }} +rhel9cis_rule_6_1_7: {{ rhel9cis_rule_6_1_7 }} +rhel9cis_rule_6_1_8: {{ rhel9cis_rule_6_1_8 }} +rhel9cis_rule_6_1_9: {{ rhel9cis_rule_6_1_9 }} +rhel9cis_rule_6_1_10: {{ rhel9cis_rule_6_1_10 }} +rhel9cis_rule_6_1_11: {{ rhel9cis_rule_6_1_11 }} +rhel9cis_rule_6_1_12: {{ rhel9cis_rule_6_1_12 }} +rhel9cis_rule_6_1_13: {{ rhel9cis_rule_6_1_13 }} +rhel9cis_rule_6_1_14: {{ rhel9cis_rule_6_1_14 }} + +rhel9cis_rule_6_2_1: {{ rhel9cis_rule_6_2_1 }} +rhel9cis_rule_6_2_2: {{ rhel9cis_rule_6_2_2 }} +rhel9cis_rule_6_2_3: {{ rhel9cis_rule_6_2_3 }} +rhel9cis_rule_6_2_4: {{ rhel9cis_rule_6_2_4 }} +rhel9cis_rule_6_2_5: {{ rhel9cis_rule_6_2_5 }} +rhel9cis_rule_6_2_6: {{ rhel9cis_rule_6_2_6 }} +rhel9cis_rule_6_2_7: {{ rhel9cis_rule_6_2_7 }} +rhel9cis_rule_6_2_8: {{ rhel9cis_rule_6_2_8 }} +rhel9cis_rule_6_2_9: {{ rhel9cis_rule_6_2_9 }} +rhel9cis_rule_6_2_10: {{ rhel9cis_rule_6_2_10 }} +rhel9cis_rule_6_2_11: {{ rhel9cis_rule_6_2_11 }} +rhel9cis_rule_6_2_12: {{ rhel9cis_rule_6_2_12 }} +rhel9cis_rule_6_2_13: {{ rhel9cis_rule_6_2_13 }} +rhel9cis_rule_6_2_14: {{ rhel9cis_rule_6_2_14 }} +rhel9cis_rule_6_2_15: {{ rhel9cis_rule_6_2_15 }} +rhel9cis_rule_6_2_16: {{ rhel9cis_rule_6_2_16 }} +rhel9cis_rule_6_2_17: {{ rhel9cis_rule_6_2_17 }} +rhel9cis_rule_6_2_18: {{ rhel9cis_rule_6_2_18 }} +rhel9cis_rule_6_2_19: {{ rhel9cis_rule_6_2_19 }} +rhel9cis_rule_6_2_20: {{ rhel9cis_rule_6_2_20 }} + + +# Service configuration booleans set true to keep service +rhel9cis_avahi_server: {{ rhel9cis_avahi_server }} +rhel9cis_cups_server: {{ rhel9cis_cups_server }} +rhel9cis_dhcp_server: {{ rhel9cis_dhcp_server }} +rhel9cis_ldap_server: {{ rhel9cis_ldap_server }} +rhel9cis_telnet_server: {{ rhel9cis_telnet_server }} +rhel9cis_nfs_server: {{ rhel9cis_nfs_server }} +rhel9cis_rpc_server: {{ rhel9cis_rpc_server }} +rhel9cis_ntalk_server: {{ rhel9cis_ntalk_server }} +rhel9cis_rsyncd_server: {{ rhel9cis_rsyncd_server }} +rhel9cis_tftp_server: {{ rhel9cis_tftp_server }} +rhel9cis_rsh_server: {{ rhel9cis_rsh_server }} +rhel9cis_nis_server: {{ rhel9cis_nis_server }} +rhel9cis_snmp_server: {{ rhel9cis_snmp_server }} +rhel9cis_squid_server: {{ rhel9cis_squid_server }} +rhel9cis_smb_server: {{ rhel9cis_smb_server }} +rhel9cis_dovecot_server: {{ rhel9cis_dovecot_server }} +rhel9cis_httpd_server: {{ rhel9cis_httpd_server }} +rhel9cis_vsftpd_server: {{ rhel9cis_vsftpd_server }} +rhel9cis_named_server: {{ rhel9cis_named_server }} +rhel9cis_nfs_rpc_server: {{ rhel9cis_nfs_rpc_server }} +rhel9cis_is_mail_server: {{ rhel9cis_is_mail_server }} +rhel9cis_bind: {{ rhel9cis_bind }} +rhel9cis_vsftpd: {{ rhel9cis_vsftpd }} +rhel9cis_httpd: {{ rhel9cis_httpd }} +rhel9cis_dovecot: {{ rhel9cis_dovecot }} +rhel9cis_samba: {{ rhel9cis_samba }} +rhel9cis_squid: {{ rhel9cis_squid }} +rhel9cis_net_snmp: {{ rhel9cis_net_snmp}} +rhel9cis_allow_autofs: {{ rhel9cis_allow_autofs }} + +# client services +rhel9cis_openldap_clients_required: {{ rhel9cis_openldap_clients_required }} +rhel9cis_telnet_required: {{ rhel9cis_telnet_required }} +rhel9cis_talk_required: {{ rhel9cis_talk_required }} +rhel9cis_rsh_required: {{ rhel9cis_rsh_required }} +rhel9cis_ypbind_required: {{ rhel9cis_ypbind_required }} + +# AIDE +rhel9cis_config_aide: {{ rhel9cis_config_aide }} + +# aide setup via - cron, timer +rhel9_aide_scan: cron + +# AIDE cron settings +rhel9cis_aide_cron: + cron_user: {{ rhel9cis_aide_cron.cron_user }} + cron_file: '{{ rhel9cis_aide_cron.cron_file }}' + aide_job: ' {{ rhel9cis_aide_cron.aide_job }}' + aide_minute: '{{ rhel9cis_aide_cron.aide_minute }}' + aide_hour: '{{ rhel9cis_aide_cron.aide_hour }}' + aide_day: '{{ rhel9cis_aide_cron.aide_day }}' + aide_month: '{{ rhel9cis_aide_cron.aide_month }}' + aide_weekday: '{{ rhel9cis_aide_cron.aide_weekday }}' + +# 1.5.1 Bootloader password +rhel9cis_bootloader_password: {{ rhel9cis_bootloader_password_hash }} +rhel9cis_set_boot_pass: {{ rhel9cis_set_boot_pass }} + +# 1.10 crypto +rhel9cis_crypto_policy: {{ rhel9cis_crypto_policy }} + +# Warning Banner Content (issue, issue.net, motd) +rhel9cis_warning_banner: {{ rhel9cis_warning_banner }} +# End Banner + +# Set to 'true' if X Windows is needed in your environment +rhel9cis_xwindows_required: {{ rhel9cis_xwindows_required }} + +# Whether or not to run tasks related to auditing/patching the desktop environment +rhel9cis_gui: {{ rhel9cis_gui }} + +# xinetd required +rhel9cis_xinetd_required: {{ rhel9cis_xinetd_required }} + +# IPv6 required +rhel9cis_ipv6_required: {{ rhel9cis_ipv6_required }} + +# System network parameters (host only OR host and router) +rhel9cis_is_router: {{ rhel9cis_is_router }} + +# Time Synchronization +rhel9cis_time_synchronization: {{ rhel9cis_time_synchronization }} + +rhel9cis_varlog_location: {{ rhel9cis_varlog_location }} + +rhel9cis_firewall: {{ rhel9cis_firewall }} +#rhel9cis_firewall: iptables +rhel9cis_default_firewall_zone: {{ rhel9cis_default_zone }} +rhel9cis_firewall_interface: +- enp0s3 +- enp0s8 + +rhel9cis_firewall_services: {{ rhel9cis_firewall_services }} + + + +### Section 4 +## auditd settings +rhel9cis_auditd: + space_left_action: {{ rhel9cis_auditd.space_left_action}} + action_mail_acct: {{ rhel9cis_auditd.action_mail_acct }} + admin_space_left_action: {{ rhel9cis_auditd.admin_space_left_action }} + max_log_file_action: {{ rhel9cis_auditd.max_log_file_action }} + auditd_backlog_limit: {{ rhel9cis_audit_back_log_limit }} + +## syslog +rhel9_cis_rsyslog: true + +### Section 5 +rhel9cis_sshd_limited: false +#Note the following to understand precedence and layout +rhel9cis_sshd_access: + AllowUser: + AllowGroup: + DenyUser: + DenyGroup: + +rhel9cis_ssh_strong_ciphers: Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128- gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr +rhel9cis_ssh_weak_ciphers: + 3des-cbc + aes128-cbc + aes192-cbc + aes256-cbc + arcfour + arcfour128 + arcfour256 + blowfish-cbc + cast128-cbc + rijndael-cbc@lysator.liu.se + +rhel9cis_ssh_strong_macs: MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2- 512,hmac-sha2-256 +rhel9cis_ssh_weak_macs: + hmac-md5 + hmac-md5-96 + hmac-ripemd160 + hmac-sha1 + hmac-sha1-96 + umac-64@openssh.com + umac-128@openssh.com + hmac-md5-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-etm@openssh.com + hmac-sha1-96-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + +rhel9cis_ssh_strong_kex: KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman- group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 +rhel9cis_ssh_weak_kex: + diffie-hellman-group1-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group-exchange-sha1 + +rhel9cis_ssh_aliveinterval: "300" +rhel9cis_ssh_countmax: "3" + +## PAM +rhel9cis_pam_password: + minlen: {{ rhel9cis_pam_password.minlen }} + minclass: {{ rhel9cis_pam_password.minclass }} +rhel9cis_pam_passwd_retry: "3" +# faillock or tally2 +rhel9cis_accountlock: faillock + +## note this is to skip tests +skip_rhel9cis_pam_passwd_auth: true +skip_rhel9cis_pam_system_auth: true + +# choose one of below +rhel9cis_pwhistory_so: "14" +rhel9cis_unix_so: false +rhel9cis_passwd_remember: "5" + +# logins.def password settings +rhel9cis_pass: + max_days: {{ rhel9cis_pass.max_days }} + min_days: {{ rhel9cis_pass.min_days }} + warn_age: {{ rhel9cis_pass.warn_age }} + +# 5.3.1/5.3.2 Custon authselect profile settings. Settings in place now will fail, they are place holders from the control example +rhel9cis_authselect: + custom_profile_name: {{ rhel9cis_authselect['custom_profile_name'] }} + default_file_to_copy: {{ rhel9cis_authselect.default_file_to_copy }} + options: {{ rhel9cis_authselect.options }} + +# 5.3.1 Enable automation to creat custom profile settings, using the setings above +rhel9cis_authselect_custom_profile_create: {{ rhel9cis_authselect_custom_profile_create }} + +# 5.3.2 Enable automation to select custom profile options, using the settings above +rhel9cis_authselect_custom_profile_select: {{ rhel9cis_authselect_custom_profile_select }} + +# 5.7 +rhel9cis_sugroup: {{ rhel9cis_sugroup| default('wheel') }} +rhel9cis_sugroup_users: {{ rhel9cis_sugroup_users }} diff --git a/templates/audit/99_auditd.rules.j2 b/templates/audit/99_auditd.rules.j2 new file mode 100644 index 00000000..da5664ba --- /dev/null +++ b/templates/audit/99_auditd.rules.j2 @@ -0,0 +1,79 @@ +# File created initially via RHEL9 CIS ansible-lockdown remdiation role +{% if rhel9cis_rule_4_1_3 %} +-w /etc/sudoers -p wa -k scope +-w /etc/sudoers.d/ -p wa -k scope +{% endif %} +{% if rhel9cis_rule_4_1_4 %} +-w /var/log/faillog -p wa -k logins +-w /var/log/lastlog -p wa -k logins +{% endif %} +{% if rhel9cis_rule_4_1_5 %} +-w /var/run/utmp -p wa -k session +-w /var/log/wtmp -p wa -k logins +-w /var/log/btmp -p wa -k logins +{% endif %} +{% if rhel9cis_rule_4_1_6 %} +-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change +-a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change +-a always,exit -F arch=b64 -S clock_settime -k time-change +-a always,exit -F arch=b32 -S clock_settime -k time-change +-w /etc/localtime -p wa -k time-change +{% endif %} +{% if rhel9cis_rule_4_1_7 %} +-w /etc/selinux/ -p wa -k MAC-policy +-w /usr/share/selinux/ -p wa -k MAC-policy +{% endif %} +{% if rhel9cis_rule_4_1_8 %} +-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale +-a always,exit -F arch=b32 -S sethostname -S setdomainname -k system-locale +-w /etc/issue -p wa -k system-locale +-w /etc/issue.net -p wa -k system-locale +-w /etc/hosts -p wa -k system-locale +-w /etc/sysconfig/network -p wa -k system-locale +{% endif %} +{% if rhel9cis_rule_4_1_9 %} +-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k perm_mod +{% endif %} +{% if rhel9cis_rule_4_1_10 %} +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=access +-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=access +-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=access +{% endif %} +{% if rhel9cis_rule_4_1_11 %} +-w /etc/group -p wa -k identity +-w /etc/passwd -p wa -k identity +-w /etc/gshadow -p wa -k identity +-w /etc/shadow -p wa -k identity +-w /etc/security/opasswd -p wa -k identity +{% endif %} +{% if rhel9cis_rule_4_1_12 %} +-a always,exit -F arch=b32 -S mount -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k mounts +-a always,exit -F arch=b64 -S mount -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k mounts +{% endif %} +{% if rhel9cis_rule_4_1_13 %} +{% for proc in priv_procs.stdout_lines -%} +-a always,exit -F path={{ proc }} -F perm=x -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -k privileged +{% endfor %} +{% endif %} +{% if rhel9cis_rule_4_1_14 %} +-a always,exit -F arch=b32 -S rmdir,unlink,unlinkat,rename -S renameat -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=delete +-a always,exit -F arch=b64 -S rmdir,unlink,unlinkat,rename -S renameat -F auid>={{ rhel9cis_int_gid }} -F auid!=4294967295 -F key=delete +{% endif %} +{% if rhel9cis_rule_4_1_15 %} +-w /usr/sbin/insmod -p x -k modules +-w /usr/sbin/rmmod -p x -k modules +-w /usr/sbin/modprobe -p x -k modules +-a always,exit -F arch=b64 -S init_module -S delete_module -k modules +{% endif %} +{% if rhel9cis_rule_4_1_16 %} +-w /var/log/sudo.log -p wa -k actions +{% endif %} +{% if rhel9cis_rule_4_1_17 %} +-e 2 +{% endif %} diff --git a/templates/chrony.conf.j2 b/templates/chrony.conf.j2 new file mode 100644 index 00000000..6513faac --- /dev/null +++ b/templates/chrony.conf.j2 @@ -0,0 +1,93 @@ +# This the default chrony.conf file for the Debian chrony package. After +# editing this file use the command 'invoke-rc.d chrony restart' to make +# your changes take effect. John Hasler 1998-2008 + +# See www.pool.ntp.org for an explanation of these servers. Please +# consider joining the project if possible. If you can't or don't want to +# use these servers I suggest that you try your ISP's nameservers. We mark +# the servers 'offline' so that chronyd won't try to connect when the link +# is down. Scripts in /etc/ppp/ip-up.d and /etc/ppp/ip-down.d use chronyc +# commands to switch it on when a dialup link comes up and off when it goes +# down. Code in /etc/init.d/chrony attempts to determine whether or not +# the link is up at boot time and set the online status accordingly. If +# you have an always-on connection such as cable omit the 'offline' +# directive and chronyd will default to online. +# +# Note that if Chrony tries to go "online" and dns lookup of the servers +# fails they will be discarded. Thus under some circumstances it is +# better to use IP numbers than host names. + +{% for server in rhel9cis_time_synchronization_servers -%} +server {{ server }} {{ rhel9cis_chrony_server_options }} +{% endfor %} + +# Look here for the admin password needed for chronyc. The initial +# password is generated by a random process at install time. You may +# change it if you wish. + +keyfile /etc/chrony/chrony.keys + +# Set runtime command key. Note that if you change the key (not the +# password) to anything other than 1 you will need to edit +# /etc/ppp/ip-up.d/chrony, /etc/ppp/ip-down.d/chrony, /etc/init.d/chrony +# and /etc/cron.weekly/chrony as these scripts use it to get the password. + +commandkey 1 + +# I moved the driftfile to /var/lib/chrony to comply with the Debian +# filesystem standard. + +driftfile /var/lib/chrony/chrony.drift + +# Comment this line out to turn off logging. + +log tracking measurements statistics +logdir /var/log/chrony + +# Stop bad estimates upsetting machine clock. + +maxupdateskew 100.0 + +# Dump measurements when daemon exits. + +dumponexit + +# Specify directory for dumping measurements. + +dumpdir /var/lib/chrony + +# Let computer be a server when it is unsynchronised. + +local stratum 10 + +# Allow computers on the unrouted nets to use the server. + +#allow 10/8 +#allow 192.168/16 +#allow 172.16/12 + +# This directive forces `chronyd' to send a message to syslog if it +# makes a system clock adjustment larger than a threshold value in seconds. + +logchange 0.5 + +# This directive defines an email address to which mail should be sent +# if chronyd applies a correction exceeding a particular threshold to the +# system clock. + +# mailonchange root@localhost 0.5 + +# This directive tells chrony to regulate the real-time clock and tells it +# Where to store related data. It may not work on some newer motherboards +# that use the HPET real-time clock. It requires enhanced real-time +# support in the kernel. I've commented it out because with certain +# combinations of motherboard and kernel it is reported to cause lockups. + +# rtcfile /var/lib/chrony/chrony.rtc + +# If the last line of this file reads 'rtconutc' chrony will assume that +# the CMOS clock is on UTC (GMT). If it reads '# rtconutc' or is absent +# chrony will assume local time. The line (if any) was written by the +# chrony postinst based on what it found in /etc/default/rcS. You may +# change it if necessary. +rtconutc diff --git a/templates/etc/99-sysctl.conf.j2 b/templates/etc/99-sysctl.conf.j2 new file mode 100644 index 00000000..61f4dfa4 --- /dev/null +++ b/templates/etc/99-sysctl.conf.j2 @@ -0,0 +1,75 @@ +# Setting added via ansible CIS remediation playbook + +{% if rhel9cis_rule_1_6_1 %} +# Filesystem sysctl +# CIS 1.6.1 +fs.suid_dumpable = 0 +{% endif %} +{% if rhel9cis_rule_1_6_2 %} +# Kernel sysctl +# CIS 1.6.2 +kernel.randomize_va_space = 2 +{% endif %} + +# Network sysctl +{% if rhel9cis_rule_3_1_1 %} +# CIS 3.1.1 +net.ipv4.ip_forward = 0 +{% if rhel9cis_rule_3_1_1 and rhel9cis_ipv6_required %} +net.ipv6.conf.all.forwarding = 0 +{% endif %} +{% endif %} +{% if rhel9cis_rule_3_1_2 %} +# CIS 3.1.2 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_2_1 %} +# CIS 3.2.1 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +{% if rhel9cis_rule_3_2_1 and rhel9cis_ipv6_required %} +net.ipv6.conf.all.accept_source_route = 0 +net.ipv6.conf.default.accept_source_route = 0 +{% endif %} +{% endif %} +{% if rhel9cis_rule_3_2_2 %} +# CIS 3.2.2 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +{% if rhel9cis_rule_3_2_2 and rhel9cis_ipv6_required %} +net.ipv6.conf.all.accept_redirects = 0 +net.ipv6.conf.default.accept_redirects = 0 +{% endif %} +{% endif %} +{% if rhel9cis_rule_3_2_3 %} +# CIS 3.2.3 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.default.secure_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_2_4 %} +# CIS 3.2.4 +net.ipv4.conf.all.log_martians = 1 +net.ipv4.conf.default.log_martians = 1 +{% endif %} +{% if rhel9cis_rule_3_2_5 %} +# CIS 3.2.5 +net.ipv4.icmp_echo_ignore_broadcasts = 1 +{% endif %} +{% if rhel9cis_rule_3_2_6 %} +# CIS 3.2.6 +net.ipv4.icmp_ignore_bogus_error_responses = 1 +{% endif %} +{% if rhel9cis_rule_3_2_7 %} +# CIS 3.2.7 +net.ipv4.conf.default.rp_filter = 1 +{% endif %} +{% if rhel9cis_rule_3_2_8 %} +# CIS 3.2.8 +net.ipv4.tcp_syncookies = 1 +{% endif %} +{% if rhel9cis_rule_3_2_9 %} +# CIS 3.2.9 +net.ipv6.conf.all.accept_ra = 0 +net.ipv6.conf.default.accept_ra = 0 +{% endif %} \ No newline at end of file diff --git a/templates/etc/issue.j2 b/templates/etc/issue.j2 new file mode 100644 index 00000000..ed524ae9 --- /dev/null +++ b/templates/etc/issue.j2 @@ -0,0 +1 @@ +{{ rhel9cis_warning_banner }} diff --git a/templates/etc/issue.net.j2 b/templates/etc/issue.net.j2 new file mode 100644 index 00000000..ed524ae9 --- /dev/null +++ b/templates/etc/issue.net.j2 @@ -0,0 +1 @@ +{{ rhel9cis_warning_banner }} diff --git a/templates/etc/motd.j2 b/templates/etc/motd.j2 new file mode 100644 index 00000000..ed524ae9 --- /dev/null +++ b/templates/etc/motd.j2 @@ -0,0 +1 @@ +{{ rhel9cis_warning_banner }} diff --git a/templates/etc/systemd/system/tmp.mount.j2 b/templates/etc/systemd/system/tmp.mount.j2 new file mode 100644 index 00000000..2a97a56b --- /dev/null +++ b/templates/etc/systemd/system/tmp.mount.j2 @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Temporary Directory (/tmp) +Documentation=man:hier(7) +Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +ConditionPathIsSymbolicLink=!/tmp +DefaultDependencies=no +Conflicts=umount.target +Before=local-fs.target umount.target +After=swap.target + +[Mount] +What=tmpfs +Where=/tmp +Type=tmpfs +Options=mode=1777,strictatime,{% if rhel9cis_rule_1_1_3 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_4 %}nosuid,{% endif %}{% if rhel9cis_rule_1_1_5 %}noexec{% endif %} + +# Make 'systemctl enable tmp.mount' work: +[Install] +WantedBy=local-fs.target \ No newline at end of file diff --git a/templates/hosts.allow.j2 b/templates/hosts.allow.j2 new file mode 100644 index 00000000..4bab3d1f --- /dev/null +++ b/templates/hosts.allow.j2 @@ -0,0 +1,11 @@ +# +# hosts.allow This file contains access rules which are used to +# allow or deny connections to network services that +# either use the tcp_wrappers library or that have been +# started through a tcp_wrappers-enabled xinetd. +# +# See 'man 5 hosts_options' and 'man 5 hosts_access' +# for information on rule syntax. +# See 'man tcpd' for information on tcp_wrappers +# +ALL: {% for iprange in rhel9cis_host_allow -%}{{ iprange }}{% if not loop.last %}, {% endif %}{% endfor %} diff --git a/templates/ntp.conf.j2 b/templates/ntp.conf.j2 new file mode 100644 index 00000000..c745ab14 --- /dev/null +++ b/templates/ntp.conf.j2 @@ -0,0 +1,59 @@ +# For more information about this file, see the man pages +# ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5). + +driftfile /var/lib/ntp/drift + +# Permit time synchronization with our time source, but do not +# permit the source to query or modify the service on this system. +#restrict default nomodify notrap nopeer noquery +restrict -4 default kod nomodify notrap nopeer noquery +restrict -6 default kod nomodify notrap nopeer noquery + +# Permit all access over the loopback interface. This could +# be tightened as well, but to do so would effect some of +# the administrative functions. +restrict 127.0.0.1 +restrict ::1 + +# Hosts on local network are less restricted. +#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap + +# Use public servers from the pool.ntp.org project. +# Please consider joining the pool (http://www.pool.ntp.org/join.html). +{% for server in rhel9cis_time_synchronization_servers -%} +server {{ server }} {{ rhel9cis_ntp_server_options }} +{% endfor %} + +#broadcast 192.168.1.255 autokey # broadcast server +#broadcastclient # broadcast client +#broadcast 224.0.1.1 autokey # multicast server +#multicastclient 224.0.1.1 # multicast client +#manycastserver 239.255.254.254 # manycast server +#manycastclient 239.255.254.254 autokey # manycast client + +# Enable public key cryptography. +#crypto + +includefile /etc/ntp/crypto/pw + +# Key file containing the keys and key identifiers used when operating +# with symmetric key cryptography. +keys /etc/ntp/keys + +# Specify the key identifiers which are trusted. +#trustedkey 4 8 42 + +# Specify the key identifier to use with the ntpdc utility. +#requestkey 8 + +# Specify the key identifier to use with the ntpq utility. +#controlkey 8 + +# Enable writing of statistics records. +#statistics clockstats cryptostats loopstats peerstats + +# Disable the monitoring facility to prevent amplification attacks using ntpdc +# monlist command when default restrict does not include the noquery flag. See +# CVE-2013-5211 for more details. +# Note: Monitoring will not be disabled with the limited restriction flag. +disable monitor diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 00000000..83b0489f --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,5 @@ +--- +# vars file for RHEL9-CIS +rhel9cis_allowed_crypto_policies: + - 'FUTURE' + - 'FIPS'