diff --git a/.ansible-lint b/.ansible-lint index f2a7e7cc..964eb052 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -1,11 +1,23 @@ +--- + parseable: true quiet: true skip_list: - - '204' - - '305' - - '303' - - '403' - - '306' - - '602' + - 'schema' + - 'no-changed-when' + - 'var-spacing' + - 'fqcn-builtins' + - 'experimental' + - 'name[play]' + - 'name[casing]' + - 'name[template]' + - 'fqcn[action]' + - '204' + - '305' + - '303' + - '403' + - '306' + - '602' + - '208' use_default_rules: true verbosity: 0 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..d3828eaf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Report Issue +about: Create a bug issue ticket to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the Issue** +A clear and concise description of what the bug is. + +**Expected Behavior** +A clear and concise description of what you expected to happen. + +**Actual Behavior** +A clear and concise description of what's happening. + +**Control(s) Affected** +What controls are being affected by the issue + +**Environment (please complete the following information):** + +- branch being used: [e.g. devel] +- Ansible Version: [e.g. 2.10] +- Host Python Version: [e.g. Python 3.7.6] +- Ansible Server Python Version: [e.g. Python 3.7.6] +- Additional Details: + +**Additional Notes** +Anything additional goes here + +**Possible Solution** +Enter a suggested fix here diff --git a/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md b/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md new file mode 100644 index 00000000..3908075d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md @@ -0,0 +1,22 @@ +--- +name: Feature Request or Enhancement +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +## Feature Request or Enhancement + +- Feature [] +- Enhancement [] + +**Summary of Request** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Suggested Code** +Please provide any code you have in mind to fulfill the request diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 00000000..ad0629e3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,18 @@ +--- +name: Question +about: Ask away....... +title: '' +labels: question +assignees: '' + +--- + +**Question** +Pose question here. + +**Environment (please complete the following information):** + +- Ansible Version: [e.g. 2.10] +- Host Python Version: [e.g. Python 3.7.6] +- Ansible Server Python Version: [e.g. Python 3.7.6] +- Additional Details: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..05dadb6b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ +**Overall Review of Changes:** +A general description of the changes made that are being requested for merge + +**Issue Fixes:** +Please list (using linking) any open issues this PR addresses + +**Enhancements:** +Please list any enhancements/features that are not open issue tickets + +**How has this been tested?:** +Please give an overview of how these changes were tested. If they were not please use N/A + diff --git a/.github/workflows/OS.tfvars b/.github/workflows/OS.tfvars new file mode 100644 index 00000000..634512bc --- /dev/null +++ b/.github/workflows/OS.tfvars @@ -0,0 +1,9 @@ +#Ami Alma 9 +ami_id = "ami-0845395779540e3cb" +ami_os = "rhel9" +ami_username = "ec2-user" +ami_user_home = "/home/ec2-user" +instance_tags = { + Name = "RHEL9-CIS" + Environment = "lockdown_github_repo_workflow" +} diff --git a/.github/workflows/github_networks.tf b/.github/workflows/github_networks.tf new file mode 100644 index 00000000..ba777642 --- /dev/null +++ b/.github/workflows/github_networks.tf @@ -0,0 +1,53 @@ +resource "aws_vpc" "Main" { + cidr_block = var.main_vpc_cidr + instance_tenancy = "default" + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-VPC" + } +} + +resource "aws_internet_gateway" "IGW" { + vpc_id = aws_vpc.Main.id + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-IGW" + } +} + +resource "aws_subnet" "publicsubnets" { + vpc_id = aws_vpc.Main.id + cidr_block = var.public_subnets + availability_zone = var.availability_zone + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-pubsub" + } +} + +resource "aws_subnet" "Main" { + vpc_id = aws_vpc.Main.id + cidr_block = var.private_subnets + availability_zone = var.availability_zone + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-prvsub" + } +} + +resource "aws_route_table" "PublicRT" { + vpc_id = aws_vpc.Main.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.IGW.id + } + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-publicRT" + } +} + +resource "aws_route_table_association" "rt_associate_public" { + subnet_id = aws_subnet.Main.id + route_table_id = aws_route_table.PublicRT.id +} diff --git a/.github/workflows/github_vars.tfvars b/.github/workflows/github_vars.tfvars new file mode 100644 index 00000000..24daeca6 --- /dev/null +++ b/.github/workflows/github_vars.tfvars @@ -0,0 +1,14 @@ +// github_actions variables +// Resourced in github_networks.tf +// Declared in variables.tf +// + +namespace = "github_actions" +environment = "lockdown_github_repo_workflow" + +// Matching pair name found in AWS for keypairs PEM key +ami_key_pair_name = "github_actions" +private_key = ".ssh/github_actions.pem" +main_vpc_cidr = "172.22.0.0/24" +public_subnets = "172.22.0.128/26" +private_subnets = "172.22.0.192/26" diff --git a/.github/workflows/linux_benchmark_testing.yml b/.github/workflows/linux_benchmark_testing.yml new file mode 100644 index 00000000..8d26a35c --- /dev/null +++ b/.github/workflows/linux_benchmark_testing.yml @@ -0,0 +1,111 @@ +# This is a basic workflow to help you get started with Actions + +name: linux_benchmark_pipeline + +# Controls when the action will run. +# Triggers the workflow on push or pull request +# events but only for the devel branch +on: + pull_request_target: + types: [opened, reopened, synchronize] + branches: + - devel + - main + paths: + - '**.yml' + - '**.sh' + - '**.j2' + - '**.ps1' + - '**.cfg' + +# A workflow run is made up of one or more jobs +# that can run sequentially or in parallel +jobs: + # This will create messages for first time contributers and direct them to the Discord server + welcome: + runs-on: ubuntu-latest + + steps: + - uses: actions/first-interaction@main + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + pr-message: |- + Congrats on opening your first pull request and thank you for taking the time to help improve Ansible-Lockdown! + Please join in the conversation happening on the [Discord Server](https://discord.io/ansible-lockdown) as well. + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + env: + ENABLE_DEBUG: false + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, + # so your job can access it + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Add_ssh_key + working-directory: .github/workflows + env: + SSH_AUTH_SOCK: /tmp/ssh_agent.sock + PRIVATE_KEY: "${{ secrets.SSH_PRV_KEY }}" + run: | + mkdir .ssh + chmod 700 .ssh + echo $PRIVATE_KEY > .ssh/github_actions.pem + chmod 600 .ssh/github_actions.pem + +### Build out the server + - name: Terraform_Init + working-directory: .github/workflows + run: terraform init + + - name: Terraform_Validate + working-directory: .github/workflows + run: terraform validate + + - name: Terraform_Apply + working-directory: .github/workflows + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: terraform apply -var-file "github_vars.tfvars" -var-file "OS.tfvars" --auto-approve -input=false + +## Debug Section + - name: DEBUG - Show Ansible hostfile + if: env.ENABLE_DEBUG == 'true' + working-directory: .github/workflows + run: cat hosts.yml + +# Aws deployments taking a while to come up insert sleep or playbook fails + + - name: Sleep for 60 seconds + run: sleep 60s + shell: bash + +# Run the ansible playbook + - name: Run_Ansible_Playbook + uses: arillso/action.playbook@master + with: + playbook: site.yml + inventory: .github/workflows/hosts.yml + galaxy_file: collections/requirements.yml + private_key: ${{ secrets.SSH_PRV_KEY }} +# verbose: 3 + env: + ANSIBLE_HOST_KEY_CHECKING: "false" + ANSIBLE_DEPRECATION_WARNINGS: "false" + +# Remove test system - User secrets to keep if necessary + + - name: Terraform_Destroy + working-directory: .github/workflows + if: always() && env.ENABLE_DEBUG == 'false' + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: terraform destroy -var-file "github_vars.tfvars" -var-file "OS.tfvars" --auto-approve -input=false diff --git a/.github/workflows/main.tf b/.github/workflows/main.tf new file mode 100644 index 00000000..516d5cc1 --- /dev/null +++ b/.github/workflows/main.tf @@ -0,0 +1,84 @@ +provider "aws" { + profile = "" + region = var.aws_region +} + +// Create a security group with access to port 22 and port 80 open to serve HTTP traffic + + +resource "random_id" "server" { + keepers = { + # Generate a new id each time we switch to a new AMI id + ami_id = "${var.ami_id}" + } + + byte_length = 8 +} + +resource "aws_security_group" "github_actions" { + name = "${var.namespace}-${random_id.server.hex}-SG" + vpc_id = aws_vpc.Main.id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + tags = { + Environment = "${var.environment}" + Name = "${var.namespace}-SG" + } +} + +// instance setup + +resource "aws_instance" "testing_vm" { + ami = var.ami_id + availability_zone = var.availability_zone + associate_public_ip_address = true + key_name = var.ami_key_pair_name # This is the key as known in the ec2 key_pairs + instance_type = var.instance_type + tags = var.instance_tags + vpc_security_group_ids = [aws_security_group.github_actions.id] + subnet_id = aws_subnet.Main.id + root_block_device { + delete_on_termination = true + } +} + +// generate inventory file +resource "local_file" "inventory" { + filename = "./hosts.yml" + directory_permission = "0755" + file_permission = "0644" + content = < { - "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. +### Known Issues -- 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 +CIS 1.2.4 - repo_gpgcheck is not carried out for RedHat hosts as the default repos do not have this function. This also affect EPEL(not covered by var). + - Rocky and Alma not affected. +Variable used to unset. +rhel9cis_rhel_default_repo: true # to be set to false if using repo that does have this ability diff --git a/ansible.cfg b/ansible.cfg index 831f01d9..3bc6e078 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -5,7 +5,13 @@ system_warnings=False command_warnings=False nocows=1 retry_files_save_path=/dev/null -library=~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules:./library +pipelining=true + +# Use the YAML callback plugin. +stdout_callback = yaml +# Use the stdout_callback when running ad-hoc commands. +bin_ansible_callbacks = True + [privilege_escalation] @@ -14,6 +20,7 @@ record_host_keys=False [ssh_connection] transfer_method=scp +ssh_args = -o ControlMaster=auto -o ControlPersist=60s [accelerate] diff --git a/collections/requirements.yml b/collections/requirements.yml new file mode 100644 index 00000000..d35b7e97 --- /dev/null +++ b/collections/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: +- name: community.general +- name: community.crypto +- name: ansible.posix diff --git a/defaults/main.yml b/defaults/main.yml index e9c7a4b3..7ea583d0 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,13 +1,15 @@ --- # defaults file for rhel9-cis -rhel9cis_skip_for_travis: false -rhel9cis_system_is_container: false +system_is_container: false +container_vars_file: is_container.yml # 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 +# Run the OS validation check +os_check: true + rhel9cis_section1: true rhel9cis_section2: true rhel9cis_section3: true @@ -15,6 +17,10 @@ rhel9cis_section4: true rhel9cis_section5: true rhel9cis_section6: true +# This is used for audit purposes to run only specifc level use the tags +# e.g. +# - level1-server +# - level2-workstation rhel9cis_level_1: true rhel9cis_level_2: true @@ -27,10 +33,16 @@ python2_bin: /bin/python2.7 ## Benchmark name used by audting control role # The audit variable found at the base +## metadata for Audit benchmark +benchmark_version: 'v1.0.0' + benchmark: RHEL9-CIS # Whether to skip the reboot -rhel9cis_skip_reboot: true +skip_reboot: true + +# default value will change to true but wont reboot if not enabled but will error +change_requires_reboot: false #### Basic external goss audit enablement settings #### #### Precise details - per setting can be found at the bottom of this file #### @@ -50,7 +62,7 @@ audit_content: git run_audit: false # Timeout for those cmds that take longer to run where timeout set -audit_cmd_timeout: 30000 +audit_cmd_timeout: 60000 ### End Goss enablements #### #### Detailed settings found at the end of this document #### @@ -62,36 +74,41 @@ audit_cmd_timeout: 30000 # 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_2_1: true +rhel9cis_rule_1_1_2_2: true +rhel9cis_rule_1_1_2_3: true +rhel9cis_rule_1_1_2_4: true +rhel9cis_rule_1_1_3_1: true +rhel9cis_rule_1_1_3_2: true +rhel9cis_rule_1_1_3_3: true +rhel9cis_rule_1_1_4_1: true +rhel9cis_rule_1_1_4_2: true +rhel9cis_rule_1_1_4_3: true +rhel9cis_rule_1_1_4_4: true +rhel9cis_rule_1_1_5_1: true +rhel9cis_rule_1_1_5_2: true +rhel9cis_rule_1_1_5_3: true +rhel9cis_rule_1_1_5_4: true +rhel9cis_rule_1_1_6_1: true +rhel9cis_rule_1_1_6_2: true +rhel9cis_rule_1_1_6_3: true +rhel9cis_rule_1_1_6_4: true +rhel9cis_rule_1_1_7_1: true +rhel9cis_rule_1_1_7_2: true +rhel9cis_rule_1_1_7_3: true +rhel9cis_rule_1_1_8_1: true +rhel9cis_rule_1_1_8_2: true +rhel9cis_rule_1_1_8_3: true +rhel9cis_rule_1_1_8_4: true rhel9cis_rule_1_1_18: true rhel9cis_rule_1_1_19: true rhel9cis_rule_1_1_20: true rhel9cis_rule_1_1_21: true -rhel9cis_rule_1_1_22: true -rhel9cis_rule_1_1_23: true +rhel9cis_rule_1_1_9: 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 @@ -100,37 +117,37 @@ 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_6_1_1: true +rhel9cis_rule_1_6_1_2: true +rhel9cis_rule_1_6_1_3: true +rhel9cis_rule_1_6_1_4: true +rhel9cis_rule_1_6_1_5: true +rhel9cis_rule_1_6_1_6: true +rhel9cis_rule_1_6_1_7: true +rhel9cis_rule_1_6_1_8: true +rhel9cis_rule_1_7_1: true +rhel9cis_rule_1_7_2: true +rhel9cis_rule_1_7_3: true +rhel9cis_rule_1_7_4: true +rhel9cis_rule_1_7_5: true +rhel9cis_rule_1_7_6: true +rhel9cis_rule_1_8_1: true rhel9cis_rule_1_8_2: true +rhel9cis_rule_1_8_3: true +rhel9cis_rule_1_8_4: true +rhel9cis_rule_1_8_5: true +rhel9cis_rule_1_8_6: true +rhel9cis_rule_1_8_7: true +rhel9cis_rule_1_8_8: true +rhel9cis_rule_1_8_9: true +rhel9cis_rule_1_8_10: true rhel9cis_rule_1_9: true rhel9cis_rule_1_10: true -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_1: true rhel9cis_rule_2_2_2: true rhel9cis_rule_2_2_3: true rhel9cis_rule_2_2_4: true @@ -151,50 +168,33 @@ rhel9cis_rule_2_2_18: true rhel9cis_rule_2_3_1: true rhel9cis_rule_2_3_2: true rhel9cis_rule_2_3_3: true +rhel9cis_rule_2_3_4: true +rhel9cis_rule_2_4: true # Section 3 rules rhel9cis_rule_3_1_1: true rhel9cis_rule_3_1_2: true +rhel9cis_rule_3_1_3: 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_3_5: true +rhel9cis_rule_3_3_6: true +rhel9cis_rule_3_3_7: true +rhel9cis_rule_3_3_8: true +rhel9cis_rule_3_3_9: true rhel9cis_rule_3_4_1_1: true +rhel9cis_rule_3_4_1_2: true rhel9cis_rule_3_4_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 +rhel9cis_rule_3_4_2_7: true # Section 4 rules rhel9cis_rule_4_1_1_1: true @@ -204,30 +204,54 @@ 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_1_3_1: true +rhel9cis_rule_4_1_3_2: true +rhel9cis_rule_4_1_3_3: true +rhel9cis_rule_4_1_3_4: true +rhel9cis_rule_4_1_3_5: true +rhel9cis_rule_4_1_3_6: true +rhel9cis_rule_4_1_3_7: true +rhel9cis_rule_4_1_3_8: true +rhel9cis_rule_4_1_3_9: true +rhel9cis_rule_4_1_3_10: true +rhel9cis_rule_4_1_3_11: true +rhel9cis_rule_4_1_3_12: true +rhel9cis_rule_4_1_3_13: true +rhel9cis_rule_4_1_3_14: true +rhel9cis_rule_4_1_3_15: true +rhel9cis_rule_4_1_3_16: true +rhel9cis_rule_4_1_3_17: true +rhel9cis_rule_4_1_3_18: true +rhel9cis_rule_4_1_3_19: true +rhel9cis_rule_4_1_3_20: true +rhel9cis_rule_4_1_3_21: true +rhel9cis_rule_4_1_4_1: true +rhel9cis_rule_4_1_4_2: true +rhel9cis_rule_4_1_4_3: true +rhel9cis_rule_4_1_4_4: true +rhel9cis_rule_4_1_4_5: true +rhel9cis_rule_4_1_4_6: true +rhel9cis_rule_4_1_4_7: true +rhel9cis_rule_4_1_4_8: true +rhel9cis_rule_4_1_4_9: true +rhel9cis_rule_4_1_4_10: true rhel9cis_rule_4_2_1_1: true rhel9cis_rule_4_2_1_2: true rhel9cis_rule_4_2_1_3: true 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_1_7: true +rhel9cis_rule_4_2_2_1_1: true +rhel9cis_rule_4_2_2_1_2: true +rhel9cis_rule_4_2_2_1_3: true +rhel9cis_rule_4_2_2_1_4: true rhel9cis_rule_4_2_2_2: true rhel9cis_rule_4_2_2_3: true +rhel9cis_rule_4_2_2_4: true +rhel9cis_rule_4_2_2_5: true +rhel9cis_rule_4_2_2_6: true +rhel9cis_rule_4_2_2_7: true rhel9cis_rule_4_2_3: true rhel9cis_rule_4_3: true @@ -240,6 +264,7 @@ 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_1_9: true rhel9cis_rule_5_2_1: true rhel9cis_rule_5_2_2: true rhel9cis_rule_5_2_3: true @@ -263,21 +288,27 @@ 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_3_4: true +rhel9cis_rule_5_3_5: true +rhel9cis_rule_5_3_6: true +rhel9cis_rule_5_3_7: 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_1: 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 +rhel9cis_rule_5_6_1_1: true +rhel9cis_rule_5_6_1_2: true +rhel9cis_rule_5_6_1_3: true +rhel9cis_rule_5_6_1_4: true +rhel9cis_rule_5_6_1_5: true +rhel9cis_rule_5_6_2: true +rhel9cis_rule_5_6_3: true +rhel9cis_rule_5_6_4: true +rhel9cis_rule_5_6_5: true +rhel9cis_rule_5_6_6: true # Section 6 rules rhel9cis_rule_6_1_1: true @@ -294,6 +325,7 @@ 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_1_15: true rhel9cis_rule_6_2_1: true rhel9cis_rule_6_2_2: true rhel9cis_rule_6_2_3: true @@ -301,7 +333,7 @@ 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_8: true rhel9cis_rule_6_2_9: true rhel9cis_rule_6_2_10: true rhel9cis_rule_6_2_11: true @@ -310,51 +342,19 @@ 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 +#### 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.1.9 +rhel9cis_allow_autofs: false + # 1.2.1 # This is the login information for your RedHat Subscription # DO NOT USE PLAIN TEXT PASSWORDS!!!!! @@ -367,20 +367,22 @@ rhel9cis_rh_sub_password: password # 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.2.4 repo_gpgcheck +rhel9cis_rhel_default_repo: true # 1.4.2 Bootloader password -rhel9cis_bootloader_password_hash: 'grub.pbkdf2.sha512.changethispassword' +rhel9cis_bootloader_password_hash: 'grub.pbkdf2.sha512.10000.9306A36764A7BEA3BF492D1784396B27F52A71812E9955A58709F94EE70697F9BD5366F36E07DEC41B52279A056E2862A93E42069D7BBB08F5DFC2679CD43812.6C32ADA5449303AD5E67A4C150558592A05381331DE6B33463469A236871FA8E70738C6F9066091D877EF88A213C86825E093117F30E9E1BF158D0DB75E7581B' rhel9cis_bootloader_password: random -rhel9cis_set_boot_pass: false +rhel9cis_set_boot_pass: true + +# 1.8 Gnome Desktop +rhel9cis_dconf_db_name: local +rhel9cis_screensaver_idle_delay: 900 # Set max value for idle-delay in seconds (between 1 and 900) +rhel9cis_screensaver_lock_delay: 5 # Set max value for lock-delay in seconds (between 0 and 5) -# 1.10/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" +# 1.10 Set crypto policy DEFAULT +# Control 1.10 states not to use LEGACY +rhel9cis_crypto_policy: "DEFAULT" # System network parameters (host only OR host and router) rhel9cis_is_router: false @@ -393,7 +395,7 @@ rhel9cis_config_aide: true # AIDE cron settings rhel9cis_aide_cron: cron_user: root - cron_file: /etc/crontab + cron_file: /etc/cron.d/aide_cron aide_job: '/usr/sbin/aide --check' aide_minute: 0 aide_hour: 5 @@ -403,107 +405,170 @@ rhel9cis_aide_cron: # SELinux policy rhel9cis_selinux_pol: targeted +# chose onf or enfocing or permissive +rhel9cis_selinux_enforce: enforcing # Whether or not to run tasks related to auditing/patching the desktop environment -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. Services -# 2.2.1.2 Time Synchronization servers - used in template file chrony.conf.j2 +### 2.1 Time Synchronization +#### 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" +### 2.2 Special Purposes +##### Service configuration booleans set true to keep service +rhel9cis_gui: false +rhel9cis_avahi_server: false +rhel9cis_cups_server: false +rhel9cis_dhcp_server: false +rhel9cis_dns_server: false +rhel9cis_dnsmasq_server: false +rhel9cis_vsftpd_server: false +rhel9cis_tftp_server: false +rhel9cis_httpd_server: false +rhel9cis_nginx_server: false +rhel9cis_dovecot_server: false +rhel9cis_imap_server: false +rhel9cis_samba_server: false +rhel9cis_squid_server: false +rhel9cis_snmp_server: false +rhel9cis_telnet_server: false +rhel9cis_is_mail_server: false +# Note the options +# Packages are used for client services and Server- only remove if you dont use the client service +# -# Firewall Service - either firewalld, iptables, or nftables +rhel9cis_use_nfs_server: false +rhel9cis_use_nfs_service: false + +rhel9cis_use_rpc_server: false +rhel9cis_use_rpc_service: false + +rhel9cis_use_rsync_server: false +rhel9cis_use_rsync_service: false + +#### 2.3 Service clients +rhel9cis_telnet_required: false +rhel9cis_openldap_clients_required: false +rhel9cis_tftp_client: false +rhel9cis_ftp_client: false + +## Section3 vars +## Sysctl +rhel9cis_sysctl_update: false +rhel9cis_flush_ipv4_route: false +rhel9cis_flush_ipv6_route: false + +### Firewall Service - either firewalld, iptables, or nftables +#### Some control allow for services to be removed or masked +#### The options are under each heading +#### absent = remove the package +#### masked = leave package if installed and mask the service rhel9cis_firewall: firewalld -# 3.4.2.4 Default zone setting +##### firewalld 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 +# These are added to demonstrate how this can be done +rhel9cis_firewalld_ports: + - number: 80 + protocol: tcp -# 3.4.3.2 Set nftables new table create +#### nftables 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. +rhel9cis_warning_banner: Authorized uses only. All activity may be monitored and reported. # End Banner ## Section4 vars - +### 4.1 Configure System Accounting +#### 4.1.2 Configure Data Retention 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 +### 4.1.3.x audit template +update_audit_template: false + +## Advanced option found in auditd post +rhel9cis_allow_auditd_uid_user_exclusions: false + +# This can be used to configure other keys in auditd.conf +rhel9cis_auditd_extra_conf: {} +# Example: +# rhel9cis_auditd_extra_conf: +# admin_space_left: '10%' + +## Preferred method of logging +## Whether rsyslog or journald preferred method for local logging +## Affects rsyslog cis 4.2.1.3 and journald cis 4.2.2.5 +rhel9cis_syslog: rsyslog +rhel9cis_rsyslog_ansiblemanaged: true + +#### 4.2.1.6 remote and destation log server name +rhel9cis_remote_log_server: false +rhel9cis_remote_log_host: logagg.example.com +rhel9cis_remote_log_port: 514 +rhel9cis_remote_log_protocol: tcp +rhel9cis_remote_log_retrycount: 100 +rhel9cis_remote_log_queuesize: 1000 -# RHEL-09-4.2.1.5 +#### 4.2.1.7 rhel9cis_system_is_log_server: false +# 4.2.2.1.2 +# rhel9cis_journal_upload_url is the ip address to upload the journal entries to +rhel9cis_journal_upload_url: 192.168.50.42 +# The paths below have the default paths/files, but allow user to create custom paths/filenames +rhel9cis_journal_upload_serverkeyfile: "/etc/ssl/private/journal-upload.pem" +rhel9cis_journal_servercertificatefile: "/etc/ssl/certs/journal-upload.pem" +rhel9cis_journal_trustedcertificatefile: "/etc/ssl/ca/trusted.pem" + +# 4.2.2.1 +# The variables below related to journald, please set these to your site specific values +# rhel9cis_journald_systemmaxuse is the max amount of disk space the logs will use +rhel9cis_journald_systemmaxuse: 10M +# rhel9cis_journald_systemkeepfree is the amount of disk space to keep free +rhel9cis_journald_systemkeepfree: 100G +rhel9cis_journald_runtimemaxuse: 10M +rhel9cis_journald_runtimekeepfree: 100G +# rhel9cis_journald_MaxFileSec is how long in time to keep log files. Values are Xm, Xh, Xday, Xweek, Xmonth, Xyear, for example 2week is two weeks +rhel9cis_journald_maxfilesec: 1month + +#### 4.3 +rhel9cis_logrotate: "daily" + ## Section5 vars +# This will allow use of drop in files when CIS adopts them. +rhel9_cis_sshd_config_file: /etc/ssh/sshd_config + 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 @@ -513,9 +578,9 @@ rhel9cis_ssh_maxsessions: 4 rhel9cis_inactivelock: lock_days: 30 +rhel9cis_use_authconfig: false # 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" @@ -531,22 +596,37 @@ 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 +# 5.5.1 ## PAM rhel9cis_pam_password: - minlen: "14" - minclass: "4" + minlen: 14 + minclass: 4 -# Starting GID for interactive users -rhel9cis_int_gid: 1000 +rhel9cis_pam_faillock: + unlock_time: 900 + deny: 5 + remember: 5 + +# UID settings for interactive users +# These are discovered via logins.def if set true +discover_int_uid: false +min_int_uid: 1000 +max_int_uid: 65533 + +# 5.3.3 var log location variable +rhel9cis_sudolog_location: "/var/log/sudo.log" + +#### 5.3.6 +rhel9cis_sudo_timestamp_timeout: 15 + +### 5.4.2 authselect and faillock +## This option is used at your own risk it will enable faillock for users +## Only to be used on a new clean system if not using authselect +## THIS CAN BREAK ACCESS EVEN FOR ROOT - UNDERSTAND RISKS ## +rhel9cis_add_faillock_without_authselect: false +# This needs to be set to ACCEPT +rhel9cis_5_4_2_risks: NEVER # RHEL-09-5.4.5 # Session timeout setting file (TMOUT setting can be set in multiple files) @@ -557,11 +637,8 @@ rhel9cis_shell_session_timeout: # 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" +# 5.3.7 +rhel9cis_sugroup: nosugroup ## Section6 vars @@ -571,20 +648,28 @@ 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 + +# 6.2.16 +## Dont follow symlinks for changes to user home directory thanks to @dulin-gnet and comminty for rhel8-cis reedbacj +rhel_09_6_2_16_home_follow_symlinks: false + #### Goss Configuration Settings #### +# Set correct env for the run_audit.sh script from https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" +audit_run_script_environment: + AUDIT_BIN: "{{ audit_bin }}" + AUDIT_FILE: 'goss.yml' + AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" ### Goss binary settings ### goss_version: - release: v0.3.16 - checksum: 'sha256:827e354b48f93bce933f5efcd1f00dc82569c42a179cf2d384b040d8a80bfbfb' + release: v0.3.21 + checksum: 'sha256:9a9200779603acf0353d2c0e85ae46e083596c10838eaf4ee050c924678e4fe3' audit_bin_path: /usr/local/bin/ audit_bin: "{{ audit_bin_path }}goss" audit_format: json # if get_goss_file == download change accordingly -goss_url: "https://github.com/aelsabbahy/goss/releases/download/{{ goss_version.release }}/goss-linux-amd64" +goss_url: "https://github.com/goss-org/goss/releases/download/{{ goss_version.release }}/goss-linux-amd64" ## if get_goss_file - copy the following needs to be updated for your environment ## it is expected that it will be copied from somewhere accessible to the control node @@ -595,7 +680,7 @@ copy_goss_from_path: /some/accessible/path ## managed by the control audit_content # git audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" -audit_git_version: main +audit_git_version: "benchmark_{{ benchmark_version }}" # copy: audit_local_copy: "some path to copy from" @@ -603,15 +688,12 @@ 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_out_dir: '/opt' 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 }}" +pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_pre_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" +post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_post_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" ## The following should not need changing goss_file: "{{ audit_conf_dir }}goss.yml" diff --git a/group_vars/docker b/group_vars/docker deleted file mode 100644 index 5b6e3b29..00000000 --- a/group_vars/docker +++ /dev/null @@ -1,28 +0,0 @@ ---- -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 deleted file mode 100644 index 1c0fb37f..00000000 --- a/group_vars/vagrant +++ /dev/null @@ -1,28 +0,0 @@ ---- -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 index fdd93548..c4b27e76 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,134 +1,105 @@ --- # handlers file for RHEL9-CIS -- name: sysctl flush ipv4 route table - become: yes - sysctl: +- name: Reload sysctl + ansible.builtin.shell: sysctl --system + when: + - sysctl_updated.changed + +- name: Sysctl flush ipv4 route table + ansible.posix.sysctl: name: net.ipv4.route.flush value: '1' - sysctl_set: yes - ignore_errors: yes - when: ansible_virtualization_type != "docker" - tags: - - skip_ansible_lint + sysctl_set: true + ignore_errors: true # noqa ignore-errors + when: + - rhel9cis_flush_ipv4_route + - not system_is_container -- name: sysctl flush ipv6 route table - become: yes - sysctl: +- name: Sysctl flush ipv6 route table + ansible.posix.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 + sysctl_set: true + when: + - rhel9cis_flush_ipv6_route + - not system_is_container -- name: systemd restart var-tmp.mount - become: yes - systemd: - name: var-tmp.mount - daemon_reload: yes - enabled: yes - masked: no +- name: Systemd restart tmp.mount + ansible.builtin.systemd: + name: tmp.mount + daemon_reload: true + enabled: true + masked: false state: reloaded -- name: remount tmp - command: mount -o remount /tmp - args: - warn: false +- name: Remount tmp + ansible.posix.mount: + path: /tmp + state: remounted -- name: restart firewalld - become: yes - service: +- name: Restart firewalld + ansible.builtin.systemd: name: firewalld state: restarted -- name: restart xinetd - become: yes - service: - name: xinetd - state: restarted - -- name: restart sshd - become: yes - service: +- name: Restart sshd + ansible.builtin.systemd: name: sshd state: restarted -- name: restart postfix - become: yes - service: +- name: Restart postfix + ansible.builtin.systemd: 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: Reload dconf + ansible.builtin.shell: dconf update -- name: grub2cfg - command: "grub2-mkconfig -o {{ grub_cfg.stat.lnk_source }}" - ignore_errors: True +- name: Grub2cfg + ansible.builtin.shell: "grub2-mkconfig -o /boot/grub2/grub.cfg" + ignore_errors: true # noqa ignore-errors tags: - skip_ansible_lint -- name: restart rsyslog - become: yes - service: +- name: Restart rsyslog + ansible.builtin.systemd: name: rsyslog state: restarted -- name: restart syslog-ng - become: yes - service: - name: syslog-ng +- name: Restart journald + ansible.builtin.systemd: + name: systemd-journald + state: restarted + +- name: Restart systemd_journal_upload + ansible.builtin.systemd: + name: systemd-journal-upload state: restarted -- name: systemd_daemon_reload - systemd: - daemon-reload: yes +- name: Systemd daemon reload + ansible.builtin.systemd: + daemon-reload: true + +## Auditd tasks note order for handlers to run + +- name: Auditd immutable check + ansible.builtin.shell: grep -c "^-e 2" /etc/audit/rules.d/99_auditd.rules + changed_when: false + register: auditd_immutable_check + +- name: Audit immutable fact + ansible.builtin.debug: + msg: "Reboot required for auditd to apply new rules as immutable set" + notify: Change_requires_reboot + when: + - auditd_immutable_check.stdout == '1' + +- name: Restart auditd + ansible.builtin.shell: service auditd restart + tags: + - skip_ansible_lint + +- name: Change_requires_reboot + ansible.builtin.set_fact: + change_requires_reboot: true diff --git a/local.yml b/local.yml index 2c649b2f..18c2f438 100644 --- a/local.yml +++ b/local.yml @@ -3,10 +3,6 @@ - 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 index cf060a31..c60c6a73 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -1,18 +1,32 @@ --- galaxy_info: author: "Sam Doran, Josh Springer, Daniel Shepherd, Bas Meijeri, James Cassell, Mike Renfro, DFed, George Nalen, Mark Bolwell" - description: "Apply the RHEL 8 CIS" + description: "Apply the RHEL 9 CIS" company: "MindPoint Group" license: MIT role_name: rhel9_cis - min_ansible_version: 2.9.0 + namespace: mindpointgroup + min_ansible_version: 2.10.0 platforms: - name: EL versions: - - 9 + - "9" galaxy_tags: - system - security - - cis + - stig - hardening + - benchmark + - compliance + - redhat + - complianceascode + - disa + - rhel9 + - cis + - rocky + - alma +collections: + - community.general + - community.crypto + - ansible.posix dependencies: [] diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 00000000..d558e806 --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,27 @@ +--- +# This is a playbook to test the tasks. +- name: Converge + hosts: all + gather_facts: true + + vars: + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + ansible_user: root + system_is_container: true + rhel9cis_selinux_disable: true + rhel9cis_rule_5_3_4: false + rhel9cis_rule_1_1_10: false + rhel9cis_firewall: "none" + rhel9cis_rule_4_1_1_1: false + rhel9cis_rule_4_1_1_2: false + rhel9cis_rule_4_1_1_3: false + rhel9cis_rule_4_1_1_4: false + rhel9cis_rule_4_2_1_2: false + rhel9cis_rule_4_2_1_4: false + rhel9cis_rule_5_1_1: false + + pre_tasks: + tasks: + - name: "Include tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 00000000..55a62745 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,34 @@ +--- +# Molecule configuration +# https://molecule.readthedocs.io/en/latest/ + +driver: + name: docker + +platforms: + - name: ubi9 + image: registry.access.redhat.com/ubi9/ubi-init + pre_build_image: true + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: true + command: "/usr/sbin/init" + capabilities: + - SYS_ADMIN + +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + callbacks_enabled: profile_tasks, timer + +lint: | + set -e + yamllint . + ansible-lint + flake8 + +verifier: + name: ansible + diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml new file mode 100644 index 00000000..5c57ab4c --- /dev/null +++ b/molecule/default/verify.yml @@ -0,0 +1,13 @@ +--- +- name: Verify + hosts: all + gather_facts: false + + vars: + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + + tasks: + - name: "Include verify tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" + tasks_from: verify diff --git a/molecule/localhost/converge.yml b/molecule/localhost/converge.yml new file mode 100644 index 00000000..6dadcfcd --- /dev/null +++ b/molecule/localhost/converge.yml @@ -0,0 +1,18 @@ +--- +# This is a playbook to test the tasks. +- name: Converge + hosts: all + become: true + gather_facts: true + + vars: + ansible_user: "{{ lookup('env', 'USER') }}" + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + rhel9cis_rule_5_3_4: false + + pre_tasks: + tasks: + - name: "Include tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" + diff --git a/molecule/localhost/molecule.yml b/molecule/localhost/molecule.yml new file mode 100644 index 00000000..94547051 --- /dev/null +++ b/molecule/localhost/molecule.yml @@ -0,0 +1,30 @@ +--- +# Molecule configuration +# https://molecule.readthedocs.io/en/latest/ + +driver: + name: delegated + options: + managed: false + ansible_connection_options: + ansible_connection: local +platforms: + - name: localhost + +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + stdout_callback: yaml + callbacks_enabled: profile_tasks, timer + +lint: | + set -e + yamllint . + ansible-lint + flake8 + +verifier: + name: ansible + diff --git a/molecule/localhost/verify.yml b/molecule/localhost/verify.yml new file mode 100644 index 00000000..58afa467 --- /dev/null +++ b/molecule/localhost/verify.yml @@ -0,0 +1,14 @@ +--- +- name: Verify + hosts: all + gather_facts: false + become: true + + vars: + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + + tasks: + - name: "Include verify tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" + tasks_from: verify diff --git a/molecule/wsl/converge.yml b/molecule/wsl/converge.yml new file mode 100644 index 00000000..0f5f3e62 --- /dev/null +++ b/molecule/wsl/converge.yml @@ -0,0 +1,27 @@ +--- +# This is a playbook to test the tasks. +- name: Converge + hosts: all + become: true + gather_facts: true + + vars: + ansible_user: "{{ lookup('env', 'USER') }}" + system_is_container: true + rhel8cis_selinux_disable: true + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + rhel8cis_rule_5_3_4: false + rhel8cis_rule_1_1_10: false + rhel8cis_rsyslog_ansiblemanaged: false + rhel8cis_rule_3_4_1_3: false + rhel8cis_rule_3_4_1_4: false + rhel8cis_rule_4_2_1_2: false + rhel8cis_rule_4_2_1_4: false + rhel8cis_rule_5_1_1: false + + pre_tasks: + tasks: + - name: "Include tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" + diff --git a/molecule/wsl/molecule.yml b/molecule/wsl/molecule.yml new file mode 100644 index 00000000..9360997d --- /dev/null +++ b/molecule/wsl/molecule.yml @@ -0,0 +1,29 @@ +--- +# Molecule configuration +# https://molecule.readthedocs.io/en/latest/ + +driver: + name: delegated + options: + managed: false + ansible_connection_options: + ansible_connection: local +platforms: + - name: localhost + +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + callbacks_enabled: profile_tasks, timer + +lint: | + set -e + yamllint . + ansible-lint + flake8 + +verifier: + name: ansible + diff --git a/molecule/wsl/verify.yml b/molecule/wsl/verify.yml new file mode 100644 index 00000000..5c57ab4c --- /dev/null +++ b/molecule/wsl/verify.yml @@ -0,0 +1,13 @@ +--- +- name: Verify + hosts: all + gather_facts: false + + vars: + role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + + tasks: + - name: "Include verify tasks" + ansible.builtin.include_role: + name: "{{ role_name }}" + tasks_from: verify diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..52cb84d2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +passlib +lxml +xmltodict +jmespath +yamllint diff --git a/site.yml b/site.yml index 2763e43b..148ca0b7 100644 --- a/site.yml +++ b/site.yml @@ -1,11 +1,7 @@ --- + - 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 index 61a4cdf1..bc929aea 100644 --- a/tasks/LE_audit_setup.yml +++ b/tasks/LE_audit_setup.yml @@ -1,7 +1,7 @@ --- - name: Download audit binary - get_url: + ansible.builtin.get_url: url: "{{ goss_url }}" dest: "{{ audit_bin }}" owner: root @@ -11,8 +11,8 @@ when: - get_goss_file == 'download' -- name: copy audit binary - copy: +- name: Copy audit binary + ansible.builtin.copy: src: dest: "{{ audit_bin }}" mode: 0555 @@ -20,3 +20,11 @@ group: root when: - get_goss_file == 'copy' + +- name: Install git if not present + ansible.builtin.package: + name: git + state: present + register: git_installed + when: + - '"git" not in ansible_facts.packages' diff --git a/tasks/audit_homedirperms.yml b/tasks/audit_homedirperms.yml deleted file mode 100644 index 596fed5a..00000000 --- a/tasks/audit_homedirperms.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -- 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/auditd.yml b/tasks/auditd.yml new file mode 100644 index 00000000..486ef315 --- /dev/null +++ b/tasks/auditd.yml @@ -0,0 +1,46 @@ +--- + +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file + ansible.builtin.stat: + path: /etc/audit/rules.d/99_auditd.rules + register: rhel9cis_auditd_file + +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | setup file + ansible.builtin.template: + src: audit/99_auditd.rules.j2 + dest: /etc/audit/rules.d/99_auditd.rules + owner: root + group: root + mode: 0640 + diff: "{{ rhel9cis_auditd_file.stat.exists }}" # Only run diff if not a new file + register: rhel9cis_auditd_template_updated + notify: + - Auditd immutable check + - Audit immutable fact + - Restart auditd + +- name: POST | AUDITD | Add Warning count for changes to template file | Warn Count # noqa: no-handler + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: 'Auditd template updated, see diff output for details' + when: + - rhel9cis_auditd_template_updated.changed + - rhel9cis_auditd_file.stat.exists + +- name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file + ansible.builtin.stat: + path: /etc/audit/rules.d/98_auditd_exceptions.rules + register: rhel9cis_auditd_exception_file + +- name: POST | Set up auditd user logging exceptions | setup file + ansible.builtin.template: + src: audit/98_auditd_exception.rules.j2 + dest: /etc/audit/rules.d/98_auditd_exceptions.rules + owner: root + group: root + mode: 0640 + diff: "{{ rhel9cis_auditd_exception_file.stat.exists }}" + notify: Restart auditd + when: + - rhel9cis_allow_auditd_uid_user_exclusions + - rhel9cis_auditd_uid_exclude | length > 0 diff --git a/tasks/check_prereqs.yml b/tasks/check_prereqs.yml index 5ce4ab44..dcfee571 100644 --- a/tasks/check_prereqs.yml +++ b/tasks/check_prereqs.yml @@ -1,36 +1,8 @@ --- -- 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 +- name: "PREREQ | If required install libselinux package to manage file changes." + ansible.builtin.package: + name: libselinux-python3 + state: present when: - - ( ansible_python.version.major == 3 and ansible_python.version.minor == 6 ) - vars: - ansible_python_interpreter: "{{ python2_bin }}" + - '"libselinux-python3" not in ansible_facts.packages' diff --git a/tasks/main.yml b/tasks/main.yml index ef1cef5c..2bb0f3f5 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,111 +1,190 @@ --- # 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." + ansible.builtin.assert: + that: (ansible_distribution != 'CentOS' and ansible_os_family == 'RedHat' or ansible_os_family == "Rocky") and ansible_distribution_major_version is version_compare('9', '==') + fail_msg: "This role can only be run against Supported OSs. {{ ansible_distribution }} {{ ansible_distribution_major_version }} is not supported." + success_msg: "This role is running against a supported OS {{ ansible_distribution }} {{ ansible_distribution_major_version }}" when: - - ansible_os_family == 'RedHat' - - ansible_distribution_major_version is version_compare('9', '!=') + - os_check + - not system_is_ec2 tags: - - always + - 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', '>=') + ansible.builtin.assert: + that: ansible_version.full is version_compare(min_ansible_version, '>=') + fail_msg: "You must use Ansible {{ min_ansible_version }} or greater" + success_msg: "This role is running a supported version of ansible {{ ansible_version.full }} >= {{ min_ansible_version }}" tags: - - always - -- name: Check crypto-policy input - assert: - that: rhel9cis_crypto_policy in rhel9cis_allowed_crypto_policies + - always -- 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" +- name: "Check password set for {{ ansible_user }}" + block: + - name: Capture current password state of "{{ ansible_user }}" + ansible.builtin.shell: "grep {{ ansible_user }} /etc/shadow | awk -F: '{print $2}'" + changed_when: false + failed_when: false + check_mode: false + register: ansible_user_password_set + + - name: "Assert that password set for {{ ansible_user }} and account not locked" + ansible.builtin.assert: + that: ansible_user_password_set.stdout | length != 0 and ansible_user_password_set.stdout != "!!" + fail_msg: "You have {{ sudo_password_rule }} enabled but the user = {{ ansible_user }} has no password set - It can break access" + success_msg: "You a password set for the {{ ansible_user }}" + vars: + sudo_password_rule: rhel9cis_rule_5_3_4 when: - - rhel9cis_set_boot_pass - - rhel9cis_rule_1_5_2 + - rhel9cis_rule_5_3_4 + - not system_is_ec2 + tags: + - user_passwd -- name: "check sugroup exists if used" +- name: "Ensure root password is set" 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" + - name: "Ensure root password is set" + ansible.builtin.shell: passwd -S root | grep "Password set, SHA512 crypt" + changed_when: false + register: root_passwd_set + + - name: "Ensure root password is set" + ansible.builtin.assert: + that: root_passwd_set.rc == 0 + fail_msg: "You have rule 5.6.6 enabled this requires that you have a root password set" + success_msg: "You have a root password set" when: - - rhel9cis_sugroup is defined - - rhel9cis_rule_5_7 + - rhel9cis_rule_5_6_6 tags: - - rule_5.7 - -- include: prelim.yml - become: yes + - level1-server + - level1-workstation + - patch + - accounts + - root + - rule_5.6.6 + +- name: Setup rules if container + block: + - name: Discover and set container variable if required + ansible.builtin.set_fact: + system_is_container: true + + - name: Load variable for container + ansible.builtin.include_vars: + file: "{{ container_vars_file }}" + + - name: Output if discovered is a container + ansible.builtin.debug: + msg: system has been discovered as a container + when: + - system_is_container + when: + - ansible_connection == 'docker' or + ansible_virtualization_type in ["docker", "lxc", "openvz", "podman", "container"] tags: - - prelim_tasks - - always + - container_discovery + - always + +- name: Check crypto-policy input + ansible.builtin.assert: + that: rhel9cis_crypto_policy in rhel9cis_allowed_crypto_policies + fail_msg: "Crypto policy is not a permitted version" + success_msg: "Crypto policy is a permitted version" -- import_tasks: pre_remediation_audit.yml +- name: Check rhel9cis_bootloader_password_hash variable has been changed + ansible.builtin.assert: + that: rhel9cis_bootloader_password_hash.find('grub.pbkdf2.sha512') != -1 and rhel9cis_bootloader_password_hash != 'grub.pbkdf2.sha512.changethispassword' + msg: "This role will not be able to run single user password commands as rhel9cis_bootloader_password_hash variable has not been set correctly" when: - - run_audit + - rhel9cis_set_boot_pass + - rhel9cis_rule_1_4_1 + tags: + - always - name: Gather the package facts - package_facts: + ansible.builtin.package_facts: manager: auto tags: - - always + - always -- include: parse_etc_password.yml - become: yes - when: rhel9cis_section6 +- name: Include OS specific variables + ansible.builtin.include_vars: "{{ ansible_distribution }}.yml" + tags: + - always + +- name: Include preliminary steps + ansible.builtin.import_tasks: prelim.yml + tags: + - prelim_tasks + - always + +- name: run pre_remediation audit + ansible.builtin.include_tasks: pre_remediation_audit.yml + when: + - run_audit -- include: section_1/main.yml - become: yes +- name: run Section 1 tasks + ansible.builtin.import_tasks: section_1/main.yml when: rhel9cis_section1 tags: - - rhel9cis_section1 + - rhel9cis_section1 -- include: section_2/main.yml - become: yes +- name: run Section 2 tasks + ansible.builtin.import_tasks: section_2/main.yml when: rhel9cis_section2 + tags: + - rhel9cis_section2 -- include: section_3/main.yml - become: yes +- name: run Section 3 tasks + ansible.builtin.import_tasks: section_3/main.yml when: rhel9cis_section3 + tags: + - rhel9cis_section3 -- include: section_4/main.yml - become: yes +- name: run Section 4 tasks + ansible.builtin.import_tasks: section_4/main.yml when: rhel9cis_section4 + tags: + - rhel9cis_section4 -- include: section_5/main.yml - become: yes +- name: run Section 5 tasks + ansible.builtin.import_tasks: section_5/main.yml when: rhel9cis_section5 + tags: + - rhel9cis_section5 -- include: section_6/main.yml - become: yes +- name: run Section 6 tasks + ansible.builtin.import_tasks: section_6/main.yml when: rhel9cis_section6 + tags: + - rhel9cis_section6 + +- name: run auditd logic + ansible.builtin.import_tasks: auditd.yml + when: update_audit_template + tags: + - always -- include: post.yml - become: yes +- name: run post remediation tasks + ansible.builtin.import_tasks: post.yml tags: - - post_tasks - - always + - post_tasks + - always -- import_tasks: post_remediation_audit.yml +- name: run post_remediation audit + ansible.builtin.import_tasks: post_remediation_audit.yml when: - - run_audit + - run_audit - name: Show Audit Summary - debug: + ansible.builtin.debug: msg: "{{ audit_results.split('\n') }}" - when: - - run_audit + when: run_audit + +- name: If Warnings found Output count and control IDs affected + ansible.builtin.debug: + msg: "You have {{ warn_count }} Warning(s) that require investigating that are related to the following benchmark ID(s) {{ warn_control_list }}" + when: warn_count != 0 + tags: + - always diff --git a/tasks/parse_etc_password.yml b/tasks/parse_etc_password.yml index de29ff12..8ff13fd3 100644 --- a/tasks/parse_etc_password.yml +++ b/tasks/parse_etc_password.yml @@ -3,15 +3,15 @@ - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" block: - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" - command: cat /etc/passwd - changed_when: no - check_mode: no + ansible.builtin.shell: cat /etc/passwd + changed_when: false + check_mode: false register: rhel9cis_passwd_file_audit - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" - set_fact: + ansible.builtin.set_fact: rhel9cis_passwd: "{{ rhel9cis_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" - with_items: "{{ rhel9cis_passwd_file_audit.stdout_lines }}" + loop: "{{ rhel9cis_passwd_file_audit.stdout_lines }}" vars: ld_passwd_regex: >- ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) diff --git a/tasks/post.yml b/tasks/post.yml index 35c3b79d..8e8fea72 100644 --- a/tasks/post.yml +++ b/tasks/post.yml @@ -1,56 +1,66 @@ --- # 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 +- name: POST | Gather the package facts after remediation + ansible.builtin.package_facts: + manager: auto tags: - - sysctl + - always -- name: trigger update auditd - command: /bin/true - notify: update auditd - changed_when: false - check_mode: false +- name: POST | Update sysctl + ansible.builtin.template: + src: "etc/sysctl.d/{{ item }}.j2" + dest: "/etc/sysctl.d/{{ item }}" + owner: root + group: root + mode: 0600 + register: sysctl_updated + notify: Reload sysctl + loop: + - 60-kernel_sysctl.conf + - 60-disable_ipv6.conf + - 60-netipv4_sysctl.conf + - 60-netipv6_sysctl.conf 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 + - rhel9cis_sysctl_update + - not system_is_container + - "'procps-ng' in ansible_facts.packages" -- name: flush handlers - meta: flush_handlers +- name: Flush handlers + ansible.builtin.meta: flush_handlers -- name: Reboot host - reboot: - when: - - not rhel9cis_skip_reboot +- name: POST | reboot system if changes require it and not skipped + block: + - name: POST | Reboot system if changes require it and not skipped + ansible.builtin.reboot: + when: + - change_requires_reboot + - not skip_reboot + + - name: POST | Warning a reboot required but skip option set + ansible.builtin.debug: + msg: "Warning!! changes have been made that require a reboot to be implemented but skip reboot was set - Can affect compliance check results" + changed_when: true + when: + - change_requires_reboot + - skip_reboot + + - name: "POST | Warning a reboot required but skip option set | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: + - change_requires_reboot + - skip_reboot + vars: + warn_control_id: Reboot_required + tags: + - grub + - level1-server + - level1-workstation + - level2-server + - level2-workstation + - rhel9cis_section1 + - rhel9cis_section2 + - rhel9cis_section3 + - rhel9cis_section4 + - rhel9cis_section5 + - rhel9cis_section6 diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index 17ef3f87..0eb7608a 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,12 +1,13 @@ --- - name: "Post Audit | Run post_remediation {{ benchmark }} audit" - shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" - vars: - warn: false + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" + environment: "{{ audit_run_script_environment | default({}) }}" + changed_when: audit_run_post_remediation.rc == 0 + register: audit_run_post_remediation - name: Post Audit | ensure audit files readable by users - file: + ansible.builtin.file: path: "{{ item }}" mode: 0644 state: file @@ -16,14 +17,14 @@ - name: Post Audit | Capture audit data if json format block: - - name: "capture data {{ post_audit_outfile }}" - command: "cat {{ post_audit_outfile }}" + - name: "Capture data {{ post_audit_outfile }}" + ansible.builtin.shell: "cat {{ post_audit_outfile }}" register: post_audit changed_when: false - name: Capture post-audit result - set_fact: - post_audit_summary: "{{ post_audit.stdout | from_json |json_query(summary) }}" + ansible.builtin.set_fact: + post_audit_summary: "{{ post_audit.stdout | from_json | json_query(summary) }}" vars: summary: 'summary."summary-line"' when: @@ -32,12 +33,12 @@ - name: Post Audit | Capture audit data if documentation format block: - name: "Post Audit | capture data {{ post_audit_outfile }}" - command: "tail -2 {{ post_audit_outfile }}" + ansible.builtin.shell: "tail -2 {{ post_audit_outfile }}" register: post_audit changed_when: false - name: Post Audit | Capture post-audit result - set_fact: + ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout_lines }}" when: - audit_format == "documentation" diff --git a/tasks/pre_remediation_audit.yml b/tasks/pre_remediation_audit.yml index 4e568dc2..2947e6ae 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -1,44 +1,32 @@ --- - name: Pre Audit | Setup the audit - include_tasks: LE_audit_setup.yml + ansible.builtin.include_tasks: LE_audit_setup.yml when: - setup_audit tags: - setup_audit - name: "Pre Audit | Ensure {{ audit_conf_dir }} exists" - file: + ansible.builtin.file: path: "{{ audit_conf_dir }}" state: directory mode: '0755' -- name: Pre Audit | 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 }}" +- name: Pre Audit | retrieve audit content files from git + ansible.builtin.git: + repo: "{{ audit_file_git }}" + dest: "{{ audit_conf_dir }}" + version: "{{ audit_git_version }}" when: - audit_content == 'git' +- name: Pre Audit | confirm audit branch vs benchmark version + ansible.builtin.debug: + msg: "Audit will run the branch {{ audit_git_version }} for this Benchmark {{ benchmark_version }}" + - name: Pre Audit | copy to audit content files to server - copy: + ansible.builtin.copy: src: "{{ audit_local_copy }}" dest: "{{ audit_conf_dir }}" mode: 0644 @@ -46,36 +34,38 @@ - audit_content == 'copy' - name: Pre Audit | get audit content from url - get_url: + ansible.builtin.get_url: url: "{{ audit_files_url }}" dest: "{{ audit_conf_dir }}" + owner: root + group: root + mode: 0755 when: - audit_content == 'get_url' - name: Pre Audit | Check Goss is available block: - name: Pre Audit | Check for goss file - stat: + ansible.builtin.stat: path: "{{ audit_bin }}" register: goss_available - - name: Pre Audit | 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 + - name: Pre Audit | Alert if goss not available + ansible.builtin.assert: + that: goss_available.stat.exists + fail_msg: "Audit binary file {{ audit_bin }} does not exist" when: - run_audit - name: "Pre Audit | Check whether machine is UEFI-based" - stat: + ansible.builtin.stat: path: /sys/firmware/efi register: rhel9_efi_boot tags: - goss_template - name: Pre Audit | Copy ansible default vars values to test audit - template: + ansible.builtin.template: src: ansible_vars_goss.yml.j2 dest: "{{ audit_vars_path }}" mode: 0600 @@ -85,20 +75,21 @@ - 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 + ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" + environment: "{{ audit_run_script_environment | default({}) }}" + changed_when: audit_run_pre_remediation.rc == 0 + register: audit_run_pre_remediation - name: Pre Audit | Capture audit data if json format block: - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - command: "cat {{ pre_audit_outfile }}" + ansible.builtin.shell: "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) }}" + ansible.builtin.set_fact: + pre_audit_summary: "{{ pre_audit.stdout | from_json | json_query(summary) }}" vars: summary: 'summary."summary-line"' when: @@ -107,12 +98,12 @@ - name: Pre Audit | Capture audit data if documentation format block: - name: "Pre Audit | capture data {{ pre_audit_outfile }}" - command: "tail -2 {{ pre_audit_outfile }}" + ansible.builtin.shell: "tail -2 {{ pre_audit_outfile }}" register: pre_audit changed_when: false - name: Pre Audit | Capture pre-audit result - set_fact: + ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout_lines }}" when: - audit_format == "documentation" diff --git a/tasks/prelim.yml b/tasks/prelim.yml index 5451c31d..f5553374 100644 --- a/tasks/prelim.yml +++ b/tasks/prelim.yml @@ -1,119 +1,263 @@ --- + # 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 + ansible.builtin.shell: "awk -F: '{print $1}' /etc/passwd" + changed_when: false + check_mode: false register: users + tags: + - level1-server + - level1-workstation + - users + +- name: "PRELIM | capture /etc/password variables" + ansible.builtin.include_tasks: parse_etc_password.yml + tags: + - rule_5.5.2 + - rule_5.6.2 + - rule_6.2.9 + - rule_6.2.10 + - rule_6.2.11 + - rhel9cis_section5 + - rhel9cis_section6 + - level1-server + +- name: "PRELIM | Interactive User accounts" + ansible.builtin.shell: 'cat /etc/passwd | grep -Ev "nologin|/sbin" | cut -d: -f6' + changed_when: false + register: interactive_users_home + tags: + - always - name: "PRELIM | Gather accounts with empty password fields" - shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" - args: - warn: no - changed_when: no - check_mode: no + ansible.builtin.shell: "cat /etc/shadow | awk -F: '($2 == \"\" ) {j++;print $1; } END {exit j}'" + changed_when: false + check_mode: false register: empty_password_accounts + tags: + - level1-server + - level1-workstation + - passwords - name: "PRELIM | Gather UID 0 accounts other than root" - shell: "cat /etc/passwd | awk -F: '($3 == 0 && $1 != \"root\") {i++;print $1 } END {exit i}'" - args: - warn: 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 + ansible.builtin.shell: "cat /etc/passwd | awk -F: '($3 == 0 && $1 != \"root\") {i++;print $1 } END {exit i}'" + changed_when: false + check_mode: false + register: rhel9cis_uid_zero_accounts_except_root + tags: + - rule_6.2.9 + - level1-server + - level1-workstation + - users + +- name: "PRELIM | Setup crypto-policy" + block: + - name: "PRELIM | Install crypto-policies" + ansible.builtin.package: + name: + - crypto-policies + - crypto-policies-scripts + state: present + + - name: "PRELIM | Gather system-wide crypto-policy" + ansible.builtin.shell: update-crypto-policies --show + changed_when: false + check_mode: false + register: system_wide_crypto_policy + when: + - rhel9cis_rule_1_10 + tags: + - level1-server + - level1-workstation + - rule_1.10 + - crypto - name: "PRELIM | if systemd coredump" - stat: + ansible.builtin.stat: path: /etc/systemd/coredump.conf register: systemd_coredump when: - - rhel9cis_rule_1_6_1 + - rhel9cis_rule_1_5_1 + tags: + - level1-server + - level1-workstation + - rule_1.5.1 + - systemd - name: "PRELIM | Section 1.1 | Create list of mount points" - set_fact: + ansible.builtin.set_fact: mount_names: "{{ ansible_mounts | map(attribute='mount') | list }}" + tags: + - level1-server + - level1-workstation + +- name: "PRELIM | Ensure python3-libselinux is installed" + ansible.builtin.package: + name: python3-libselinux + state: present + when: + - '"python3-libselinux" not in ansible_facts.packages' + +- name: "PRELIM | Set facts based on boot type" + block: + - name: "PRELIM | Check whether machine is UEFI-based" + ansible.builtin.stat: + path: /sys/firmware/efi + register: rhel_09_efi_boot + + - name: "PRELIM | set legacy boot and grub path | Bios" + ansible.builtin.set_fact: + rhel9cis_legacy_boot: true + grub2_path: /etc/grub2.cfg + when: not rhel_09_efi_boot.stat.exists + + - name: "PRELIM | set grub fact | UEFI" + ansible.builtin.set_fact: + grub2_path: /etc/grub2-efi.cfg + when: rhel_09_efi_boot.stat.exists + +- name: "PRELIM | Update to latest gpg keys" + ansible.builtin.package: + name: "{{ gpg_key_package }}" + state: latest + when: + - rhel9cis_rule_1_2_4 + - ansible_distribution != 'RedHat' + - ansible_distribution != 'OracleLinux' - name: "PRELIM | Section 4.1 | Configure System Accounting (auditd)" - package: + ansible.builtin.package: name: audit state: present - when: rhel9cis_level_2 + become: true + when: + - '"auditd" not in ansible_facts.packages' + - rhel9cis_rule_4_1_1_1 + tags: + - level2-server + - level2-workstation + - patch + - rule_4.1.1.1 + - auditd -- name: "PRELIM | 4.1.12 | Ensure successful file system mounts are collected" - shell: for i in $(df | grep '^/dev' | awk '{ print $NF }'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done - changed_when: false - failed_when: false - check_mode: no - register: priv_procs +- name: "PRELIM | 4.1.4.5 | Audit conf and rules files | list files" + ansible.builtin.find: + path: /etc/audit + file_type: file + recurse: true + patterns: '*.conf,*.rules' + register: auditd_conf_files + when: + - rhel9cis_rule_4_1_4_5 or + rhel9cis_rule_4_1_4_6 or + rhel9cis_rule_4_1_4_7 tags: - - always + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.5 + - rule_4.1.4.6 + - rule_4.1.4.7 - name: "PRELIM | Section 5.1 | Configure cron" - package: + ansible.builtin.package: name: cronie state: present + become: true + when: + - rhel9cis_rule_5_1_1 + - '"cronie" not in ansible_facts.packages' + tags: + - level1-server + - level1-workstation + - rule_5.1.1 + - cron + +# Added to ensure ssh drop in file exists if not default /etc/ssh/sshd_config +- name: "PRELIM | Section 5.2 | SSH" + ansible.builtin.file: + path: "{{ rhel9_cis_sshd_config_file }}" + owner: root + group: root + mode: 0600 + state: touch + when: + - rhel9_cis_sshd_config_file != '/etc/ssh/sshd_config' + - "'openssh-server' in ansible_facts.packages" + tags: + - ssh + - level1_server + - level1_workstation - name: "PRELIM | Install authconfig" - package: + ansible.builtin.package: name: authconfig state: present + become: true when: - rhel9cis_use_authconfig - rhel9cis_rule_5_3_1 or rhel9cis_rule_5_3_2 or - rhel9cis_rule_5_3_3 + rhel9cis_rule_5_3_3 or + '"authconfig" not in ansible_facts.packages or + "auditd-lib" not in ansible_facts.packages' + tags: + - level1-server + - level1-workstation + - rule_5.3.1 or + rule_5.3.2 or + rule_5.3.3 + - authconfig + - auditd -- 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 | 5.3.4 | Find all sudoers files." + ansible.builtin.shell: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_sudoers_files + when: + - rhel9cis_rule_5_3_4 or + rhel9cis_rule_5_3_5 + tags: + - rule_5.3.4 + - rule_5.3.5 - - 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 | Discover Interactive UID MIN and MIN from logins.def" + block: + - name: "PRELIM | Capture UID_MIN information from logins.def" + ansible.builtin.shell: grep -w "^UID_MIN" /etc/login.defs | awk '{print $NF}' + changed_when: false + register: uid_min_id - - name: "PRELIM | set grub fact | UEFI" - set_fact: - grub2_path: /etc/grub2-efi.cfg - when: rhel_09_efi_boot.stat.exists + - name: "PRELIM | Capture UID_MAX information from logins.def" + ansible.builtin.shell: grep -w "^UID_MAX" /etc/login.defs | awk '{print $NF}' + changed_when: false + register: uid_max_id -# - name: debug legacy boot var -# debug: -# msg: | -# legacy_boot={{ rhel9cis_legacy_boot }} -# grub2_path={{ grub2_path }} + - name: "PRELIM | Capture GID_MIN information from logins.def" + ansible.builtin.shell: grep -w "^GID_MIN" /etc/login.defs | awk '{print $NF}' + changed_when: false + register: gid_min_id -- name: "PRELIM | AUDIT | Ensure permissions on bootloader config are configured | Get grub config file stats" - stat: - path: "{{ grub2_path }}" - changed_when: false - register: grub_cfg + - name: "PRELIM | set_facts for interactive uid/gid" + ansible.builtin.set_fact: + min_int_uid: "{{ uid_min_id.stdout }}" + max_int_uid: "{{ uid_max_id.stdout }}" + min_int_gid: "{{ gid_min_id.stdout }}" -# - name: debug grub stat -# debug: -# var: grub_cfg.stat +- name: "PRELIM | Output of uid findings" + ansible.builtin.debug: + msg: "{{ min_int_uid }} {{ max_int_uid }}" -- 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 + - not discover_int_uid + +- name: "PRELIM | Gather the package facts after prelim" + ansible.builtin.package_facts: + manager: auto tags: - - skip_ansible_lint + - always diff --git a/tasks/section_1/cis_1.1.1.x.yml b/tasks/section_1/cis_1.1.1.x.yml index fa381ad4..7a88f6f2 100644 --- a/tasks/section_1/cis_1.1.1.x.yml +++ b/tasks/section_1/cis_1.1.1.x.yml @@ -1,102 +1,66 @@ --- -- name: "1.1.1.1 | L1 | PATCH | Ensure mounting of cramfs filesystems is disabled" +- name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled" block: - - name: "1.1.1.1 | 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 + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Edit modprobe config" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/CIS.conf + regexp: "^(#)?install squashfs(\\s|$)" + line: "install squashfs /bin/true" + create: true mode: 0600 - - name: "1.1.1.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 + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist squashfs(\\s|$)" + line: "blacklist squashfs" + create: true mode: 0600 - - name: "1.1.1.2 | L2 | PATCH | Ensure mounting of vFAT filesystems is limited | Disable vFAT" - modprobe: - name: vfat + - name: "1.1.1.1 | PATCH | Ensure mounting of squashfs filesystems is disabled | Disable squashfs" + community.general.modprobe: + name: squashfs state: absent - when: ansible_connection != 'docker' + when: not system_is_container + when: - - rhel9cis_rule_1_1_1_2 - - rhel9cis_legacy_boot + - rhel9cis_rule_1_1_1_1 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 + - rule_1.1.1.1 - squashfs -- name: "1.1.1.4 | L1 | PATCH | Ensure mounting of udf filesystems is disabled" +- name: "1.1.1.2 | 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 + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disable | Edit modprobe config" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/CIS.conf regexp: "^(#)?install udf(\\s|$)" line: "install udf /bin/true" - create: yes + create: true + mode: 0600 + + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist udf(\\s|$)" + line: "blacklist udf" + create: true mode: 0600 - - name: "1.1.1.4 | L1 | PATCH | Ensure mounting of udf filesystems is disable | Disable udf" - modprobe: + - name: "1.1.1.2 | PATCH | Ensure mounting of udf filesystems is disable | Disable udf" + community.general.modprobe: name: udf state: absent - when: ansible_connection != 'docker' + when: not system_is_container when: - - rhel9cis_rule_1_1_1_4 + - rhel9cis_rule_1_1_1_2 tags: - - level1-server - - level1-workstation - - scored + - level2-server + - level2-workstation - patch - - rule_1.1.1.4 + - rule_1.1.1.2 - udf diff --git a/tasks/section_1/cis_1.1.2.x.yml b/tasks/section_1/cis_1.1.2.x.yml new file mode 100644 index 00000000..b4e18889 --- /dev/null +++ b/tasks/section_1/cis_1.1.2.x.yml @@ -0,0 +1,81 @@ +--- + +- name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition" + block: + - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.2.1 | PATCH | Ensure /tmp is a separate partition | Present" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.2.1' + required_mount: '/tmp' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_2_1 + tags: + - level1-server + - level1-workstation + - audit + - mounts + - rule_1.1.2.1 + +# via fstab +- name: | + "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition" + "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition" + "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition" + ansible.builtin.mount: + name: /tmp + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_2_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_2_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_2_4 %}nosuid{% endif %} + notify: Remount tmp + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + when: + - item.mount == "/tmp" + - not rhel9cis_tmp_svc + - rhel9cis_rule_1_1_2_2 or + rhel9cis_rule_1_1_2_3 or + rhel9cis_rule_1_1_2_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - rule_1.1.2.2 + - rule_1.1.2.3 + - rule_1.1.2.4 + +# via systemd +- name: | + "1.1.2.1 | PATCH | Ensure /tmp is configured" + "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition" + "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition" + "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition" + ansible.builtin.template: + src: etc/systemd/system/tmp.mount.j2 + dest: /etc/systemd/system/tmp.mount + owner: root + group: root + mode: 0644 + notify: Systemd restart tmp.mount + when: + - rhel9cis_tmp_svc + - rhel9cis_rule_1_1_2_1 or + rhel9cis_rule_1_1_2_2 or + rhel9cis_rule_1_1_2_3 or + rhel9cis_rule_1_1_2_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - rule_1.1.2.1 + - rule_1.1.2.2 + - rule_1.1.2.3 + - rule_1.1.2.4 diff --git a/tasks/section_1/cis_1.1.3.x.yml b/tasks/section_1/cis_1.1.3.x.yml new file mode 100644 index 00000000..d873c516 --- /dev/null +++ b/tasks/section_1/cis_1.1.3.x.yml @@ -0,0 +1,49 @@ +--- + +- name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var" + block: + - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Present" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.3.1' + required_mount: '/var' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_3_1 + tags: + - level2-server + - level2-workstation + - patch + - mounts + - rule_1.1.3.1 + +# skips if mount is absent +- name: | + "1.1.3.2 | PATCH | Ensure nodev option set on /var partition" + "1.1.3.3 | PATCH | Ensure nosuid option set on /var partition" + ansible.builtin.mount: + name: /var + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_3_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_3_3 %}nosuid,{% endif %} + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + notify: Change_requires_reboot + when: + - item.mount == "/var" + - rhel9cis_rule_1_1_3_2 or + rhel9cis_rule_1_1_3_3 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - skip_ansible_lint + - rule_1.1.3.2 + - rule_1.1.3.3 diff --git a/tasks/section_1/cis_1.1.4.x.yml b/tasks/section_1/cis_1.1.4.x.yml new file mode 100644 index 00000000..f063fbdf --- /dev/null +++ b/tasks/section_1/cis_1.1.4.x.yml @@ -0,0 +1,53 @@ +--- + +# Skips if mount is absent +- name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp" + block: + - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Present" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.1.4.1' + required_mount: '/var/tmp' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_4_1 + tags: + - level2-server + - level2-workstation + - audit + - mounts + - rule_1.1.4.1 + +# skips if mount is absent +- name: | + "1.1.4.2 | PATCH | Ensure noexec option set on /var/tmp partition" + "1.1.4.3 | PATCH | Ensure nosuid option set on /var/tmp partition" + "1.1.4.4 | PATCH | Ensure nodev option set on /var/tmp partition" + ansible.builtin.mount: + name: /var/tmp + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_4_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_4_3 %}nosuid,{% endif %}{% if rhel9cis_rule_1_1_4_4 %}nodev{% endif %} + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + notify: Change_requires_reboot + when: + - item.mount == "/var/tmp" + - rhel9cis_rule_1_1_4_2 or + rhel9cis_rule_1_1_4_3 or + rhel9cis_rule_1_1_4_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - skip_ansible_lint + - rule_1.1.4.2 + - rule_1.1.4.3 + - rule_1.1.4.4 diff --git a/tasks/section_1/cis_1.1.5.x.yml b/tasks/section_1/cis_1.1.5.x.yml new file mode 100644 index 00000000..1707f308 --- /dev/null +++ b/tasks/section_1/cis_1.1.5.x.yml @@ -0,0 +1,53 @@ +--- + +- name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log" + block: + - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Present" + ansible.builtin.import_tasks: warning_facts.yml + + vars: + warn_control_id: '1.1.5.1' + required_mount: '/var/log' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_5_1 + tags: + - level2-server + - level2-workstation + - audit + - mounts + - rule_1.1.5.1 + - skip_ansible_lint + +- name: | + "1.1.5.2 | PATCH | Ensure nodev option set on /var/log partition" + "1.1.5.3 | PATCH | Ensure noexec option set on /var/log partition" + "1.1.5.4 | PATCH | Ensure nosuid option set on /var/log partition" + ansible.builtin.mount: + name: /var/log + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_5_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_5_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_5_4 %}nosuid{% endif %} + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + notify: Change_requires_reboot + when: + - item.mount == "/var/log" + - rhel9cis_rule_1_1_5_2 or + rhel9cis_rule_1_1_5_3 or + rhel9cis_rule_1_1_5_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - skip_ansible_lint + - rule_1.1.5.2 + - rule_1.1.5.3 + - rule_1.1.5.4 diff --git a/tasks/section_1/cis_1.1.6.x.yml b/tasks/section_1/cis_1.1.6.x.yml new file mode 100644 index 00000000..274f6683 --- /dev/null +++ b/tasks/section_1/cis_1.1.6.x.yml @@ -0,0 +1,52 @@ +--- + +- name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit" + block: + - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Present" + ansible.builtin.import_tasks: warning_facts.yml + + vars: + warn_control_id: '1.1.6.1' + required_mount: '/var/log/audit' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_6_1 + tags: + - level2-server + - level2-workstation + - audit + - mounts + - rule_1.1.6.1 + +- name: | + "1.1.6.2 | PATCH | Ensure noexec option set on /var/log/audit partition" + "1.1.6.3 | PATCH | Ensure nodev option set on /var/log/audit partition" + "1.1.6.4 | PATCH | Ensure nosuid option set on /var/log/audit partition" + ansible.builtin.mount: + name: /var/log/audit + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_6_2 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_6_3 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_6_4 %}nosuid{% endif %} + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + notify: Change_requires_reboot + when: + - item.mount == "/var/log/audit" + - rhel9cis_rule_1_1_6_2 or + rhel9cis_rule_1_1_6_3 or + rhel9cis_rule_1_1_6_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - skip_ansible_lint + - rule_1.1.6.2 + - rule_1.1.6.3 + - rule_1.1.6.4 diff --git a/tasks/section_1/cis_1.1.7.x.yml b/tasks/section_1/cis_1.1.7.x.yml new file mode 100644 index 00000000..7f166100 --- /dev/null +++ b/tasks/section_1/cis_1.1.7.x.yml @@ -0,0 +1,52 @@ +--- + +- name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home" + block: + - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Present" + ansible.builtin.import_tasks: warning_facts.yml + + vars: + warn_control_id: '1.1.7.1' + required_mount: '/home' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_7_1 + tags: + - level2-server + - level2-workstation + - audit + - mounts + - rule_1.1.7.1 + - skip_ansible_lint + +- name: | + "1.1.7.2 | PATCH | Ensure nodev option set on /home partition + 1.1.7.3 | PATCH | Ensure nosuid option set on /home partition" + ansible.builtin.mount: + name: /home + src: "{{ item.device }}" + fstype: "{{ item.fstype }}" + state: present + opts: defaults,{% if rhel9cis_rule_1_1_7_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_7_3 %}nosuid,{% endif %} + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.device }}" + notify: Change_requires_reboot + when: + - item.mount == "/home" + - rhel9cis_rule_1_1_7_1 + - rhel9cis_rule_1_1_7_2 or + rhel9cis_rule_1_1_7_3 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - rule_1.1.7.2 + - rule_1.1.7.3 + - rule_1.1.7.4 + - skip_ansible_lint diff --git a/tasks/section_1/cis_1.1.8.x.yml b/tasks/section_1/cis_1.1.8.x.yml new file mode 100644 index 00000000..3b85af37 --- /dev/null +++ b/tasks/section_1/cis_1.1.8.x.yml @@ -0,0 +1,49 @@ +--- + +# Skips if mount is absent +- name: "1.1.8.1 | AUDIT | Ensure /dev/shm is a separate partition" + block: + - name: "1.1.8.1 | AUDIT | Ensure /dev/shm is a separate partition | Absent" + ansible.builtin.debug: + msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" + + - name: "1.1.8.1 | AUDIT | Ensure separate partition exists for /home | Present" + ansible.builtin.import_tasks: warning_facts.yml + + vars: + warn_control_id: '1.1.8.1' + required_mount: '/dev/shm' + when: + - required_mount not in mount_names + - rhel9cis_rule_1_1_8_1 + tags: + - level1-server + - level1-workstation + - audit + - mounts + - rule_1.1.8.1 + - skip_ansible_lint + +- name: | + "1.1.8.2 | PATCH | Ensure nodev option set on /dev/shm partition | Set nodev option + 1.1.8.3 | PATCH | Ensure noexec option set on /dev/shm partition | Set nosuid option + 1.1.8.4 | PATCH | Ensure nosuid option set on /dev/shm partition | Set noexec option" + ansible.builtin.mount: + name: /dev/shm + src: tmpfs + fstype: tmpfs + state: mounted + opts: defaults,{% if rhel9cis_rule_1_1_8_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_8_3 %}noexec,{% endif %}{% if rhel9cis_rule_1_1_8_4 %}nosuid{% endif %} + notify: Change_requires_reboot + when: + - rhel9cis_rule_1_1_8_2 or + rhel9cis_rule_1_1_8_3 or + rhel9cis_rule_1_1_8_4 + tags: + - level1-server + - level1-workstation + - patch + - mounts + - rule_1.1.8.2 + - rule_1.1.8.3 + - rule_1.1.8.4 diff --git a/tasks/section_1/cis_1.1.x.yml b/tasks/section_1/cis_1.1.x.yml index 65e2260d..0496300b 100644 --- a/tasks/section_1/cis_1.1.x.yml +++ b/tasks/section_1/cis_1.1.x.yml @@ -1,363 +1,35 @@ --- -- 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" +- name: "1.1.9 | PATCH | Disable USB Storage" 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 + - name: "1.1.9 | PATCH | Disable USB Storage | Edit modprobe config" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/CIS.conf regexp: "^(#)?install usb-storage(\\s|$)" line: "install usb-storage /bin/true" - create: yes + create: true owner: root group: root mode: 0600 - - name: "1.1.23 | L1 | PATCH | Disable USB Storage | Edit modprobe config" - modprobe: + - name: "1.1.9 | PATCH | Disable USB Storage | Edit modprobe config" + ansible.builtin.modprobe: name: usb-storage state: absent + + - name: "1.1.9 | PATCH | Disable USB Storage | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist usb-storage(\\s|$)" + line: "blacklist usb-storage" + create: true + mode: 0600 when: - - rhel9cis_rule_1_1_23 + - rhel9cis_rule_1_1_9 tags: - level1-server - level2-workstation - patch - mounts - removable_storage - - rule_1.1.23 + - rule_1.1.9 diff --git a/tasks/section_1/cis_1.10.yml b/tasks/section_1/cis_1.10.yml index 91b8cade..c43e4454 100644 --- a/tasks/section_1/cis_1.10.yml +++ b/tasks/section_1/cis_1.10.yml @@ -1,9 +1,10 @@ --- -- name: "1.10 | L1 | PATCH | Ensure system-wide crypto policy is not legacy" - shell: | +- name: "1.10 | PATCH | Ensure system-wide crypto policy is not legacy" + ansible.builtin.shell: | update-crypto-policies --set "{{ rhel9cis_crypto_policy }}" update-crypto-policies + notify: Change_requires_reboot when: - rhel9cis_rule_1_10 - system_wide_crypto_policy['stdout'] == 'LEGACY' diff --git a/tasks/section_1/cis_1.11.yml b/tasks/section_1/cis_1.11.yml deleted file mode 100644 index 34245b18..00000000 --- a/tasks/section_1/cis_1.11.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- - -- 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 index 07a8285a..25017320 100644 --- a/tasks/section_1/cis_1.2.x.yml +++ b/tasks/section_1/cis_1.2.x.yml @@ -1,100 +1,121 @@ --- -- 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.1 | AUDIT | Ensure GPG keys are configured" + block: + - name: "1.2.1 | AUDIT | Ensure GPG keys are configured | list installed pubkey keys" + ansible.builtin.shell: "rpm -qa | grep {{ os_gpg_key_pubkey_name }}" + changed_when: false + failed_when: false + register: os_installed_pub_keys -- name: "1.2.2 | 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.1 | AUDIT | Ensure GPG keys are configured | Query found keys" + ansible.builtin.shell: 'rpm -q --queryformat "%{PACKAGER} %{VERSION}\\n" {{ os_gpg_key_pubkey_name }} | grep "{{ os_gpg_key_pubkey_content }}"' + changed_when: false + failed_when: false + register: os_gpg_key_check + when: os_installed_pub_keys.rc == 0 -- name: "1.2.3 | L1 | AUDIT | Ensure GPG keys are configured" - command: gpg --quiet --with-fingerprint /etc/pki/rpm-gpg/RPM-GPG-KEY-{{ ansible_distribution|lower }}-release + - name: "1.2.1 | AUDIT | Ensure GPG keys are configured | expected keys fail" + ansible.builtin.fail: + msg: Installed GPG Keys do not meet expected values or keys installed that are not expected + when: + - os_installed_pub_keys.rc == 1 or + os_gpg_key_check.rc == 1 when: - - rhel9cis_rule_1_2_3 - - ansible_distribution == "RedHat" + - rhel9cis_rule_1_2_1 + - ansible_distribution == "RedHat" or + ansible_distribution == "Rocky" or + ansible_distribution == "AlmaLinux" tags: - level1-server - level1-workstation - - notscored + - manual - patch - - rule_1.2.3 + - rule_1.2.1 -- name: "1.2.4 | L1 | PATCH | Ensure gpgcheck is globally activated" +- name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated" block: - - name: "1.2.4 | L1 | AUDIT | Ensure gpgcheck is globally activated | Find repos" - find: + - name: "1.2.2 | AUDIT | Ensure gpgcheck is globally activated | Find repos" + ansible.builtin.find: paths: /etc/yum.repos.d patterns: "*.repo" register: yum_repos - changed_when: false - - name: "1.2.4 | L1 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" - replace: + - name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" + ansible.builtin.replace: name: "{{ item.path }}" regexp: "^gpgcheck=0" replace: "gpgcheck=1" - with_items: - - "{{ yum_repos.files }}" + loop: "{{ yum_repos.files }}" + loop_control: + label: "{{ item.path }}" when: - - rhel9cis_rule_1_2_4 + - rhel9cis_rule_1_2_2 tags: - level1-server - level1-workstation - - scored - patch - - rule_1.2.4 + - rule_1.2.2 -- name: "1.2.5 | L1 | Ensure package manager repositories are configured" +- name: "1.2.3 | AUDIT | 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 + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Get repo list" + ansible.builtin.shell: dnf repolist changed_when: false failed_when: false register: dnf_configured - check_mode: no - args: - warn: false + check_mode: false - - name: "1.2.5 | L1 | AUDIT | Ensure package manager repositories are configured | Display repo list" - debug: + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Display repo list" + ansible.builtin.debug: msg: - - "Alert! Below are the configured repos. Please review and make sure all align with site policy" + - "Warning!! Below are the configured repos. Please review and make sure all align with site policy" - "{{ dnf_configured.stdout_lines }}" + + - name: "1.2.3 | AUDIT | Ensure package manager repositories are configured | Warn Count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '1.2.3' when: - - rhel9cis_rule_1_2_5 + - rhel9cis_rule_1_2_3 tags: - level1-server - level1-workstation - - notscored - - patch - - rule_1.2.5 + - manual + - audit + - rule_1.2.3 - skip_ansible_lint + +- name: "1.2.4 | AUDIT | Ensure repo_gpgcheck is globally activated" + block: + - name: "1.2.4 | PATCH | Ensure repo_gpgcheck is globally activated | dnf.conf" + ansible.builtin.lineinfile: + path: /etc/dnf/dnf.conf + regexp: '^repo_gpgcheck' + line: repo_gpgcheck=1 + + - name: "1.2.4 | AUDIT| Ensure repo_gpgcheck is globally activated | get repo files" + ansible.builtin.find: + paths: /etc/yum.repos.d + patterns: "*.repo" + register: repo_files + + - name: "1.2.4 | PATCH | Ensure repo_gpgcheck is globally activated | amend repo files" + ansible.builtin.replace: + path: "{{ item.path }}" + regexp: '^repo_gpgcheck( |)=( |)0' + replace: repo_gpgcheck=1 + loop: "{{ repo_files.files }}" + loop_control: + label: "{{ item.path }}" + + when: + - rhel9cis_rule_1_2_4 + - not rhel9cis_rhel_default_repo or ansible_distribution != 'RedHat' + - ansible_distribution != 'OracleLinux' + tags: + - level1-server + - level1-workstation + - manual + - audit + - rule_1.2.4 diff --git a/tasks/section_1/cis_1.3.x.yml b/tasks/section_1/cis_1.3.x.yml index 8456bc13..1275d865 100644 --- a/tasks/section_1/cis_1.3.x.yml +++ b/tasks/section_1/cis_1.3.x.yml @@ -1,44 +1,78 @@ --- -- name: "1.3.1 | L1 | PATCH | Ensure sudo is installed" - package: - name: sudo - state: present +- name: "1.3.1 | PATCH | Ensure AIDE is installed" + block: + - name: "1.3.1 | PATCH | Ensure AIDE is installed | Install AIDE" + ansible.builtin.package: + name: aide + state: present + + - name: "1.3.1 | PATCH | Ensure AIDE is installed | Build AIDE DB" + ansible.builtin.shell: /usr/sbin/aide --init + changed_when: false + failed_when: false + async: 45 + poll: 0 + args: + creates: /var/lib/aide/aide.db.new.gz + when: not ansible_check_mode + + - name: "1.3.1 | PATCH | Ensure AIDE is installed | copy AIDE DB" + ansible.builtin.copy: + src: /var/lib/aide/aide.db.new.gz + dest: /var/lib/aide/aide.db.gz + remote_src: true when: + - rhel9cis_config_aide - rhel9cis_rule_1_3_1 tags: - level1-server - level1-workstation - - scored - - sudo + - aide - 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 +- name: "1.3.2 | PATCH | Ensure filesystem integrity is regularly checked" + ansible.builtin.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_3_2 + - not system_is_ec2 tags: - level1-server - level1-workstation - - scored + - aide + - file_integrity - 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 +- name: "1.3.3 | Ensure cryptographic mechanisms are used to protect the integrity of audit tools" + ansible.builtin.blockinfile: + path: /etc/aide.conf + marker: "# {mark} Audit tools - CIS benchmark - Ansible-lockdown" + block: | + /sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 + /sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 + validate: aide -D --config %s when: - - rhel9cis_rule_1_3_3 + - rhel9cis_rule_1_3_2 + - not system_is_ec2 tags: - level1-server - level1-workstation - - scored + - aide + - file_integrity - patch - rule_1.3.3 diff --git a/tasks/section_1/cis_1.4.x.yml b/tasks/section_1/cis_1.4.x.yml index 5956ae6e..ec27fa64 100644 --- a/tasks/section_1/cis_1.4.x.yml +++ b/tasks/section_1/cis_1.4.x.yml @@ -1,50 +1,44 @@ --- -- 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 +- name: "1.4.1 | PATCH | Ensure bootloader password is set" + ansible.builtin.copy: + dest: /boot/grub2/user.cfg + content: "GRUB2_PASSWORD={{ rhel9cis_bootloader_password_hash }}" # noqa template-instead-of-copy + owner: root + group: root + mode: 0600 + notify: Grub2cfg when: - - rhel9cis_config_aide + - rhel9cis_set_boot_pass - rhel9cis_rule_1_4_1 tags: - level1-server - level1-workstation - - scored - - aide + - grub - 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'] }}" +- name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured" + block: + - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured" + ansible.builtin.file: + path: "/boot/grub2/{{ item.path }}" + owner: root + group: root + mode: "{{ item.mode }}" + state: touch + modification_time: preserve + access_time: preserve + loop: + - { path: 'grub.cfg', mode: '0700' } + - { path: 'grubenv', mode: '0600' } + - { path: 'user.cfg', mode: '0600' } + when: - rhel9cis_rule_1_4_2 tags: - level1-server - level1-workstation - - scored - - aide - - file_integrity + - grub - 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 index 5b169468..3f806471 100644 --- a/tasks/section_1/cis_1.5.x.yml +++ b/tasks/section_1/cis_1.5.x.yml @@ -1,76 +1,48 @@ --- -- 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" +- name: "1.5.1 | PATCH | Ensure core dump storage is disabled" + ansible.builtin.lineinfile: + path: /etc/systemd/coredump.conf + regexp: '^Storage\s*=\s*(?!none).*' + line: 'Storage=none' + notify: Systemd daemon reload when: - rhel9cis_rule_1_5_1 - - grub_cfg.stat.exists - - grub_cfg.stat.islnk + - systemd_coredump.stat.exists 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 +- name: "1.5.2 | PATCH | Ensure core dump backtraces are disabled" + ansible.builtin.lineinfile: + path: /etc/systemd/coredump.conf + regexp: '^ProcessSizeMax\s*=\s*.*[1-9]$' + line: 'ProcessSizeMax=0' 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 + - sysctl - rule_1.5.2 -- name: "1.5.3 | L1 | PATCH | Ensure authentication required for single user mode" +- name: "1.5.3 | PATCH | Ensure address space layout randomization (ASLR) is enabled" 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 | PATCH | Ensure address space layout randomization (ASLR) is enabled" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true - - 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' + - name: "1.5.3 | PATCH | Ensure address space layout randomization (ASLR) is enabled" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-kernel_sysctl.conf" when: - rhel9cis_rule_1_5_3 tags: - level1-server - level1-workstation - patch + - sysctl - rule_1.5.3 diff --git a/tasks/section_1/cis_1.6.1.x.yml b/tasks/section_1/cis_1.6.1.x.yml new file mode 100644 index 00000000..f05143c7 --- /dev/null +++ b/tasks/section_1/cis_1.6.1.x.yml @@ -0,0 +1,132 @@ +--- + +- name: "1.6.1.1 | PATCH | Ensure SELinux is installed" + ansible.builtin.package: + name: libselinux + state: present + when: + - rhel9cis_rule_1_6_1_1 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.6.1.1 + +- name: "1.6.1.2 | PATCH | Ensure SELinux is not disabled in bootloader configuration" + ansible.builtin.replace: + path: /etc/default/grub + regexp: '{{ item }}' + replace: '' + loop: + - selinux=0 + - enforcing=0 + register: selinux_grub_patch + ignore_errors: true # noqa ignore-errors + notify: Grub2cfg + when: + - rhel9cis_rule_1_6_1_2 + tags: + - level1-server + - level1-workstation + - scored + - patch + - rule_1.6.1.2 + +# State set to enforcing because control 1.6.1.5 requires enforcing to be set +- name: "1.6.1.3 | PATCH | Ensure SELinux policy is configured" + ansible.posix.selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: "{{ rhel9cis_selinux_enforce }}" + when: + - not rhel9cis_selinux_disable + - rhel9cis_rule_1_6_1_3 + tags: + - level1-server + - level1-workstation + - selinux + - patch + - rule_1.6.1.3 + +- name: "1.6.1.4 | PATCH | Ensure the SELinux state is not disabled" + ansible.posix.selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: "{{ rhel9cis_selinux_enforce }}" + when: + - not rhel9cis_selinux_disable + - rhel9cis_rule_1_6_1_4 + tags: + - level1-server + - level1-workstation + - selinux + - patch + - rule_1.6.1.4 + +- name: "1.6.1.5 | PATCH | Ensure the SELinux state is enforcing" + ansible.posix.selinux: + conf: /etc/selinux/config + policy: "{{ rhel9cis_selinux_pol }}" + state: enforcing + when: + - not rhel9cis_selinux_disable + - rhel9cis_selinux_enforce == 'enforcing' + - rhel9cis_rule_1_6_1_5 + tags: + - level2-server + - level2-workstation + - selinux + - patch + - rule_1.6.1.5 + +- name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist" + block: + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | Find the unconfined services" + ansible.builtin.shell: ps -eZ | grep unconfined_service_t | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }' + register: rhelcis_1_6_1_6_unconf_services + failed_when: false + changed_when: false + + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | Message on unconfined services" + ansible.builtin.debug: + msg: "Warning!! You have unconfined services: {{ rhelcis_1_6_1_6_unconf_services.stdout_lines }}" + when: rhelcis_1_6_1_6_unconf_services.stdout | length > 0 + + - name: "1.6.1.6 | AUDIT | Ensure no unconfined services exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: rhelcis_1_6_1_6_unconf_services.stdout | length > 0 + vars: + warn_control_id: '1.6.1.6' + when: + - rhel9cis_rule_1_6_1_6 + tags: + - level1-server + - level1-workstation + - audit + - services + - rule_1.6.1.6 + +- name: "1.6.1.7 | PATCH | Ensure SETroubleshoot is not installed" + ansible.builtin.package: + name: setroubleshoot + state: absent + when: + - rhel9cis_rule_1_6_1_7 + - "'setroubleshoot' in ansible_facts.packages" + tags: + - level1-server + - selinux + - patch + - rule_1.6.1.7 + +- name: "1.6.1.8 | PATCH | Ensure the MCS Translation Service (mcstrans) is not installed" + ansible.builtin.package: + name: mcstrans + state: absent + when: + - rhel9cis_rule_1_6_1_8 + tags: + - level1-server + - level1-workstation + - patch + - rule_1.6.1.8 diff --git a/tasks/section_1/cis_1.6.x.yml b/tasks/section_1/cis_1.6.x.yml deleted file mode 100644 index 1b37c0de..00000000 --- a/tasks/section_1/cis_1.6.x.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- - -- 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 deleted file mode 100644 index ea1f8056..00000000 --- a/tasks/section_1/cis_1.7.1.x.yml +++ /dev/null @@ -1,115 +0,0 @@ ---- - -- 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.7.x.yml similarity index 50% rename from tasks/section_1/cis_1.8.1.x.yml rename to tasks/section_1/cis_1.7.x.yml index d8cbec37..1c20dca4 100644 --- a/tasks/section_1/cis_1.8.1.x.yml +++ b/tasks/section_1/cis_1.7.x.yml @@ -1,96 +1,93 @@ --- -- name: "1.8.1.1 | L1 | PATCH | Ensure message of the day is configured properly" - template: +- name: "1.7.1 | PATCH | Ensure message of the day is configured properly" + ansible.builtin.template: src: etc/motd.j2 dest: /etc/motd owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_1 + - rhel9cis_rule_1_7_1 tags: - level1-server - level1-workstation - banner - patch - - rule_1.8.1.1 + - rule_1.7.1 -- name: "1.8.1.2 | L1 | PATCH | Ensure local login warning banner is configured properly" - template: +- name: "1.7.2 | PATCH | Ensure local login warning banner is configured properly" + ansible.builtin.template: src: etc/issue.j2 dest: /etc/issue owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_2 + - rhel9cis_rule_1_7_2 tags: - level1-server - level1-workstation - patch - - rule_1.8.1.2 + - rule_1.7.2 -- name: "1.8.1.3 | L1 | PATCH | Ensure remote login warning banner is configured properly" - template: +- name: "1.7.3 | PATCH | Ensure remote login warning banner is configured properly" + ansible.builtin.template: src: etc/issue.net.j2 dest: /etc/issue.net owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_3 + - rhel9cis_rule_1_7_3 tags: - level1-server - level1-workstation - banner - patch - - rule_1.8.1.3 + - rule_1.7.3 -- name: "1.8.1.4 | L1 | PATCH | Ensure permissions on /etc/motd are configured" - file: - dest: /etc/motd - state: file +- name: "1.7.4 | PATCH | Ensure permissions on /etc/motd are configured" + ansible.builtin.file: + path: /etc/motd owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_4 + - rhel9cis_rule_1_7_4 tags: - level1-server - level1-workstation - perms - patch - - rule_1.8.1.4 + - rule_1.7.4 -- name: "1.8.1.5 | L1 | PATCH | Ensure permissions on /etc/issue are configured" - file: - dest: /etc/issue - state: file +- name: "1.7.5 | PATCH | Ensure permissions on /etc/issue are configured" + ansible.builtin.file: + path: /etc/issue owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_5 + - rhel9cis_rule_1_7_5 tags: - level1-server - level1-workstation - perms - patch - - rule_1.8.1.5 + - rule_1.7.5 -- name: "1.8.1.6 | L1 | PATCH | Ensure permissions on /etc/issue.net are configured" - file: - dest: /etc/issue.net - state: file +- name: "1.7.6 | PATCH | Ensure permissions on /etc/issue.net are configured" + ansible.builtin.file: + path: /etc/issue.net owner: root group: root mode: 0644 when: - - rhel9cis_rule_1_8_1_6 + - rhel9cis_rule_1_7_6 tags: - level1-server - level1-workstation - perms - patch - - rule_1.8.1.6 + - rule_1.7.6 diff --git a/tasks/section_1/cis_1.8.2.yml b/tasks/section_1/cis_1.8.2.yml deleted file mode 100644 index 2062c69f..00000000 --- a/tasks/section_1/cis_1.8.2.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- - -- 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.8.x.yml b/tasks/section_1/cis_1.8.x.yml new file mode 100644 index 00000000..4f6922f0 --- /dev/null +++ b/tasks/section_1/cis_1.8.x.yml @@ -0,0 +1,264 @@ +--- + +- name: "1.8.1 | PATCH | Ensure GNOME Display Manager is removed" + ansible.builtin.package: + name: gdm + state: absent + when: + - rhel9cis_rule_1_8_1 + - "'gdm' in ansible_facts.packages" + - not rhel9cis_gui + tags: + - level2-server + - patch + - gui + - gdm + - rule_1.8.1 + +- name: "1.8.2 | PATCH | Ensure GDM login banner is configured" + block: + - name: "1.8.2 | PATCH | Ensure GDM login banner is configured | gdm profile" + ansible.builtin.lineinfile: + path: /etc/dconf/profile/gdm + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + create: true + owner: root + group: root + mode: 0644 + notify: Reload dconf + loop: + - { regexp: 'user-db', line: 'user-db:user' } + - { regexp: 'system-db', line: 'system-db:gdm' } + - { regexp: 'file-db', line: 'file-db:/usr/share/gdm/greeter-dconf-defaults' } + + - name: "1.8.2 | PATCH | Ensure GDM login banner is configured | gdm profile" + ansible.builtin.template: + src: etc/dconf/db/gdm.d/01-banner-message.j2 + dest: /etc/dconf/db/gdm.d/01-banner-message + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_2 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - gdm + - rule_1.8.2 + +- name: "1.8.3 | PATCH | Ensure GDM disable-user-list option is enabled" + ansible.builtin.lineinfile: + path: "{{ item.file }}" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + create: true + owner: root + group: root + mode: 0644 + notify: Reload dconf + loop: + - { file: '/etc/dconf/profile/gdm', regexp: 'user-db', line: 'user-db:user' } + - { file: '/etc/dconf/profile/gdm', regexp: 'system-db', line: 'system-db:gdm' } + - { file: '/etc/dconf/profile/gdm', regexp: 'file-db', line: 'file-db:/usr/share/gdm/greeter-dconf-defaults'} + - { file: '/etc/dconf/db/gdm.d/00-login-screen', regexp: '\[org\/gnome\/login-screen\]', line: '[org/gnome/login-screen]' } + - { file: '/etc/dconf/db/gdm.d/00-login-screen', regexp: 'disable-user-list=', line: 'disable-user-list=true' } + when: + - rhel9cis_rule_1_8_3 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - rule_1.8.3 + +- name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle" + block: + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | User profile" + ansible.builtin.lineinfile: + path: /etc/dconf/profile/user + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + create: true + owner: root + group: root + mode: 0644 + loop: + - { regexp: '^user-db', line: 'user-db: user' } + - { regexp: '^system-db', line: 'system-db: local' } + + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | Make db directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.4 | PATCH | Ensure GDM screen locks when the user is idle | Make conf file" + ansible.builtin.template: + src: etc/dconf/db/00-screensaver.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-screensaver" + owner: root + group: root + mode: '0644' + notify: Reload dconf + when: + - rhel9cis_rule_1_8_4 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - rule_1.8.4 + +- name: "1.8.5 PATCH | Ensure GDM screen locks cannot be overridden" + block: + - name: "1.8.5 | PATCH | Ensure GDM screen locks cannot be overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.5 | PATCH | Ensure GDM screen locks cannot be overridden | Make lock file" + ansible.builtin.template: + src: etc/dconf/db/00-screensaver_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-screensaver" + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_5 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - rule_1.8.5 + +- name: "1.8.6 | PATCH | Ensure GDM automatic mounting of removable media is disabled" + ansible.builtin.template: + src: etc/dconf/db/00-media-automount.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-media-automount" + owner: root + group: root + mode: '0644' + notify: Reload dconf + when: + - rhel9cis_rule_1_8_6 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.6 + +- name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden" + block: + - name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.7 | PATCH | Ensure GDM disabling automatic mounting of removable media is not overridden | Make lock file" + ansible.builtin.template: + src: etc/dconf/db/00-automount_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-automount_lock" + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_7 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.7 + +- name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled" + block: + - name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled | Make directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.8 | PATCH | Ensure GDM autorun-never is enabled | Make conf file" + ansible.builtin.template: + src: etc/dconf/db/00-media-autorun.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/00-media-autorun" + owner: root + group: root + mode: '0644' + notify: Reload dconf + when: + - rhel9cis_rule_1_8_8 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.8 + +- name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden" + block: + - name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden | Make lock directory" + ansible.builtin.file: + path: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks" + owner: root + group: root + mode: 0755 + state: directory + + - name: "1.8.9 | PATCH | Ensure GDM autorun-never is not overridden | Make lockfile" + ansible.builtin.template: + src: etc/dconf/db/00-autorun_lock.j2 + dest: "/etc/dconf/db/{{ rhel9cis_dconf_db_name }}.d/locks/00-autorun_lock" + owner: root + group: root + mode: 0644 + notify: Reload dconf + when: + - rhel9cis_rule_1_8_9 + - rhel9cis_gui + tags: + - level1-server + - level2-workstation + - patch + - gui + - rule_1.8.9 + +- name: "1.8.10 | PATCH | Ensure XDMCP is not enabled" + ansible.builtin.lineinfile: + path: /etc/gdm/custom.conf + regexp: 'Enable=true' + state: absent + when: + - rhel9cis_rule_1_8_10 + - rhel9cis_gui + tags: + - level1-server + - level1-workstation + - patch + - gui + - rule_1.8.4 diff --git a/tasks/section_1/cis_1.9.yml b/tasks/section_1/cis_1.9.yml index a67d5dbd..e226948c 100644 --- a/tasks/section_1/cis_1.9.yml +++ b/tasks/section_1/cis_1.9.yml @@ -1,9 +1,10 @@ --- -- name: "1.9 | L1 | PATCH | Ensure updates, patches, and additional security software are installed" - package: +- name: "1.9 | PATCH | Ensure updates, patches, and additional security software are installed" + ansible.builtin.package: name: "*" state: latest + notify: Change_requires_reboot when: - rhel9cis_rule_1_9 - not system_is_ec2 diff --git a/tasks/section_1/main.yml b/tasks/section_1/main.yml index 5c7b083a..d9bc3b5d 100644 --- a/tasks/section_1/main.yml +++ b/tasks/section_1/main.yml @@ -1,42 +1,59 @@ --- -- 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.1.1.x | Disable unused filesystems" + ansible.builtin.import_tasks: cis_1.1.1.x.yml -- name: "SECTION | 1.2 | Configure Software Updates" - include: cis_1.2.x.yml +- name: "SECTION | 1.1.2.x | Configure /tmp" + ansible.builtin.import_tasks: cis_1.1.2.x.yml + +- name: "SECTION | 1.1.3.x | Configure /var" + ansible.builtin.import_tasks: cis_1.1.3.x.yml + +- name: "SECTION | 1.1.4.x | Configure /var/tmp" + ansible.builtin.import_tasks: cis_1.1.4.x.yml + +- name: "SECTION | 1.1.5.x | Configure /var/log" + ansible.builtin.import_tasks: cis_1.1.5.x.yml + +- name: "SECTION | 1.1.6.x | Configure /var/log/audit" + ansible.builtin.import_tasks: cis_1.1.6.x.yml + +- name: "SECTION | 1.1.7.x | Configure /home" + ansible.builtin.import_tasks: cis_1.1.7.x.yml + +- name: "SECTION | 1.1.8.x | Configure /dev/shm" + ansible.builtin.import_tasks: cis_1.1.8.x.yml -- name: "SECTION | 1.3 | Configure sudo" - include: cis_1.3.x.yml +- name: "SECTION | 1.1.x | Disable various mounting" + ansible.builtin.import_tasks: cis_1.1.x.yml -- name: "SECTION | 1.4 | Filesystem Integrity" - include: cis_1.4.x.yml +- name: "SECTION | 1.2 | Configure Software Updates" + ansible.builtin.import_tasks: cis_1.2.x.yml + +- name: "SECTION | 1.3 | Filesystem Integrity Checking" + ansible.builtin.import_tasks: cis_1.3.x.yml when: rhel9cis_config_aide -- name: "SECTION | 1.5 | Secure Boot Settings" - include: cis_1.5.x.yml +- name: "SECTION | 1.4 | Secure Boot Settings" + ansible.builtin.import_tasks: cis_1.4.x.yml -- name: "SECTION | 1.6 | Additional Process Hardening" - include: cis_1.6.x.yml +- name: "SECTION | 1.5 | Additional Process Hardening" + ansible.builtin.import_tasks: cis_1.5.x.yml -- name: "SECTION | 1.7 | bootloader and Mandatory Access Control" - include: cis_1.7.1.x.yml +- name: "SECTION | 1.6 | Mandatory Access Control" + include_tasks: cis_1.6.1.x.yml when: not rhel9cis_selinux_disable -- name: "SECTION | 1.8 | Warning Banners" - include: cis_1.8.1.x.yml +- name: "SECTION | 1.7 | Command Line Warning Banners" + ansible.builtin.import_tasks: cis_1.7.x.yml -- name: "SECTION | 1.9 | Updated and Patches" - include: cis_1.9.yml +- name: "SECTION | 1.8 | Gnome Display Manager" + ansible.builtin.import_tasks: cis_1.8.x.yml -- name: "SECTION | 1.10 | Crypto policies" - include: cis_1.10.yml - when: - - not system_is_ec2 +- name: "SECTION | 1.9 | Updates and Patches" + ansible.builtin.import_tasks: cis_1.9.yml -- name: "SECTION | 1.11 | FIPS/FUTURE Crypto policies" - include: cis_1.11.yml +- name: "SECTION | 1.10 | Crypto policies" + include_tasks: cis_1.10.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 deleted file mode 100644 index 5b563645..00000000 --- a/tasks/section_2/cis_2.1.1.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- - -- 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.1.x.yml b/tasks/section_2/cis_2.1.x.yml new file mode 100644 index 00000000..43cc2260 --- /dev/null +++ b/tasks/section_2/cis_2.1.x.yml @@ -0,0 +1,40 @@ +--- + +- name: "2.1.1 | PATCH | Ensure time synchronization is in use" + ansible.builtin.package: + name: chrony + state: present + when: + - rhel9cis_rule_2_1_1 + - not system_is_container + tags: + - level1-server + - level1-workstation + - patch + - rule_2.1.1 + +- name: "2.1.2 | PATCH | Ensure chrony is configured" + block: + - name: "2.1.2 | PATCH | Ensure chrony is configured | Set configuration" + ansible.builtin.template: + src: etc/chrony.conf.j2 + dest: /etc/chrony.conf + owner: root + group: root + mode: 0644 + + - name: "2.1.2 | PATCH | Ensure chrony is configured | modify /etc/sysconfig/chronyd | 1" + ansible.builtin.lineinfile: + path: /etc/sysconfig/chronyd + regexp: "^(#)?OPTIONS" + line: "OPTIONS=\"-u chrony\"" + create: true + mode: 0644 + when: + - rhel9cis_rule_2_1_2 + - not system_is_container + tags: + - level1-server + - level1-workstation + - patch + - rule_2.1.2 diff --git a/tasks/section_2/cis_2.2.1.x.yml b/tasks/section_2/cis_2.2.1.x.yml deleted file mode 100644 index fbdf9c1e..00000000 --- a/tasks/section_2/cis_2.2.1.x.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- - -- 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 index f6203dab..e592d176 100644 --- a/tasks/section_2/cis_2.2.x.yml +++ b/tasks/section_2/cis_2.2.x.yml @@ -1,288 +1,349 @@ --- -- 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.1 | PATCH | Ensure xorg-x11-server-common is not installed" + ansible.builtin.package: + name: xorg-x11-server-common + state: absent + when: + - rhel9cis_rule_2_2_1 + - "'xorg-x11-server-common' in ansible_facts.packages" + - not rhel9cis_gui + tags: + - level1-server + - patch + - x11 + - rule_2.2.1 - - 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 +- name: "2.2.2 | PATCH | Ensure Avahi Server is not installed" + ansible.builtin.package: + name: + - avahi-autoipd + - avahi + state: absent when: - - not rhel9cis_xwindows_required - rhel9cis_rule_2_2_2 + - not rhel9cis_avahi_server + - "'avahi' in ansible_facts.packages or 'avahi-autopd' in ansible_facts.packages" tags: - level1-server - - scored - - xwindows + - level2-workstation - patch + - avahi - rule_2.2.2 -- name: "2.2.3 | L1 | PATCH | Ensure rsync service is not enabled " - service: - name: rsyncd - state: stopped - enabled: no +- name: "2.2.3 | PATCH | Ensure CUPS is not installed" + ansible.builtin.package: + name: cups + state: absent when: - - not rhel9cis_rsyncd_server - - "'rsyncd' in ansible_facts.packages" + - not rhel9cis_cups_server + - "'cups' in ansible_facts.packages" - rhel9cis_rule_2_2_3 tags: - level1-server - - level1-workstation - patch + - cups - rule_2.2.3 -- name: "2.2.4 | L1 | PATCH | Ensure Avahi Server is not enabled" - service: - name: avahi-daemon - state: stopped - enabled: no +- name: "2.2.4 | PATCH | Ensure DHCP Server is not installed" + ansible.builtin.package: + name: dhcp-server + state: absent when: - - not rhel9cis_avahi_server - - "'avahi' in ansible_facts.packages" + - not rhel9cis_dhcp_server + - "'dhcp-server' in ansible_facts.packages" - rhel9cis_rule_2_2_4 tags: - level1-server - level1-workstation - - scored - - avahi - - services - patch + - dhcp - rule_2.2.4 -- name: "2.2.5 | L1 | PATCH | Ensure SNMP Server is not enabled" - service: - name: snmpd - state: stopped - enabled: no +- name: "2.2.5 | PATCH | Ensure DNS Server is not installed" + ansible.builtin.package: + name: bind + state: absent when: - - not rhel9cis_snmp_server - - "'net-snmp' in ansible_facts.packages" + - not rhel9cis_dns_server + - "'bind' in ansible_facts.packages" - rhel9cis_rule_2_2_5 tags: - level1-server - level1-workstation - patch + - dns - rule_2.2.5 -- name: "2.2.6 | L1 | PATCH | Ensure HTTP Proxy Server is not enabled" - service: - name: squid - state: stopped - enabled: no +- name: "2.2.6 | PATCH | Ensure VSFTP Server is not installed" + ansible.builtin.package: + name: vsftpd + state: absent when: - - not rhel9cis_squid_server - - "'squid' in ansible_facts.packages" + - not rhel9cis_vsftpd_server + - "'vsftpd' in ansible_facts.packages" - rhel9cis_rule_2_2_6 tags: - level1-server - level1-workstation - patch + - vsftpd - rule_2.2.6 -- name: "2.2.7 | L1 | PATCH | Ensure Samba is not enabled" - service: - name: smb - state: stopped - enabled: no +- name: "2.2.7 | PACH | Ensure TFTP Server is not installed" + ansible.builtin.package: + name: tftp-server + state: absent when: - - not rhel9cis_smb_server - - "'samba' in ansible_facts.packages" + - not rhel9cis_tftp_server + - "'tftp-server' in ansible_facts.packages" - rhel9cis_rule_2_2_7 tags: - level1-server - level1-workstation - patch + - tftp - 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 +- name: "2.2.8 | PATCH | Ensure a web server is not installed" + block: + - name: "2.2.8 | PATCH | Ensure a web server is not installed | Remove httpd server" + ansible.builtin.package: + name: httpd + state: absent + when: + - not rhel9cis_httpd_server + - "'httpd' in ansible_facts.packages" + + - name: "2.2.8 | PATCH | Ensure a web server is not installed | Remove nginx server" + ansible.builtin.package: + name: nginx + state: absent + when: + - not rhel9cis_nginx_server + - "'nginx' in ansible_facts.packages" when: - - not rhel9cis_dovecot_server - - "'dovecot' in ansible_facts.packages" - rhel9cis_rule_2_2_8 tags: - level1-server - level1-workstation - patch + - httpd + - nginx + - webserver - rule_2.2.8 -- name: "2.2.9 | L1 | PATCH | Ensure HTTP server is not enabled" - service: - name: httpd - state: stopped - enabled: no +- name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" + block: + - name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" + ansible.builtin.package: + name: + - dovecot + state: absent + when: + - not rhel9cis_dovecot_server + - "'dovecot' in ansible_facts.packages" + + - name: "2.2.9 | PATCH | Ensure IMAP and POP3 server is not installed" + ansible.builtin.package: + name: + - cyrus-imapd + state: absent + when: + - not rhel9cis_imap_server + - "'cyrus-imapd' in ansible_facts.packages" + when: - - not rhel9cis_httpd_server - - "'httpd' in ansible_facts.packages" - rhel9cis_rule_2_2_9 tags: - level1-server - level1-workstation - patch + - dovecot + - imap + - pop3 - rule_2.2.9 -- name: "2.2.10 | L1 | PATCH | Ensure FTP Server is not enabled" - service: - name: vsftpd - state: stopped - enabled: no +- name: "2.2.10 | PATCH | Ensure Samba is not enabled" + ansible.builtin.package: + name: samba + state: absent when: - - not rhel9cis_vsftpd_server - - "'vsftpd' in ansible_facts.packages" + - not rhel9cis_samba_server + - "'samba' in ansible_facts.packages" - rhel9cis_rule_2_2_10 tags: - level1-server - level1-workstation - patch + - samba - rule_2.2.10 -- name: "2.2.11 | L1 | PATCH | Ensure DNS Server is not enabled" - service: - name: named - state: stopped - enabled: no +- name: "2.2.11 | PATCH | Ensure HTTP Proxy Server is not installed" + ansible.builtin.package: + name: squid + state: absent when: - - not rhel9cis_named_server - - "'bind' in ansible_facts.packages" + - not rhel9cis_squid_server + - "'squid' in ansible_facts.packages" - rhel9cis_rule_2_2_11 tags: - level1-server - level1-workstation - patch + - squid - rule_2.2.11 -- name: "2.2.12 | L1 | PATCH | Ensure NFS is not enabled" - service: - name: nfs-server - state: stopped - enabled: no +- name: "2.2.12 | PATCH | Ensure net-snmp is not installed" + ansible.builtin.package: + name: net-snmp + state: absent when: - - not rhel9cis_nfs_rpc_server - - "'nfs-utils' in ansible_facts.packages" + - not rhel9cis_snmp_server + - "'net-snmp' in ansible_facts.packages" - rhel9cis_rule_2_2_12 tags: - level1-server - level1-workstation - - scored - - nfs - - services - patch + - snmp - rule_2.2.12 -- name: "2.2.13 | L1 | PATCH | Ensure RPC is not enabled" - service: - name: rpcbind - state: stopped - enabled: no +- name: "2.2.13 | PATCH | Ensure telnet-server is not installed" + ansible.builtin.package: + name: telnet-server + state: absent when: - - not rhel9cis_nfs_rpc_server - - "'rpcbind' in ansible_facts.packages" + - not rhel9cis_telnet_server + - "'telnet-server' in ansible_facts.packages" - rhel9cis_rule_2_2_13 tags: - level1-server - level1-workstation - - scored - - rpc - - services - patch - - rule_2.2.7 + - telnet + - rule_2.2.13 -- name: "2.2.14 | L1 | PATCH | Ensure LDAP server is not enabled" - service: - name: slapd - state: stopped - enabled: no +- name: "2.2.14 | PATCH | Ensure dnsmasq is not installed" + ansible.builtin.package: + name: dnsmasq + state: absent + notify: Restart postfix when: - - not rhel9cis_ldap_server - - "'openldap-servers' in ansible_facts.packages" + - not rhel9cis_is_mail_server + - "'dnsmasq' in ansible_facts.packages" - rhel9cis_rule_2_2_14 tags: - level1-server - level1-workstation - - scored - - ldap - - services - patch - - rule_2.2.6 + - dnsmasq + - rule_2.2.14 -- name: "2.2.15 | L1 | PATCH | Ensure DHCP Server is not enabled" - service: - name: dhcpd - state: stopped - enabled: no +- name: "2.2.15 | PATCH | Ensure mail transfer agent is configured for local-only mode" + ansible.builtin.lineinfile: + path: /etc/postfix/main.cf + regexp: "^(#)?inet_interfaces" + line: "inet_interfaces = loopback-only" + notify: Restart postfix when: - - not rhel9cis_dhcp_server - - "'dhcp' in ansible_facts.packages" + - not rhel9cis_is_mail_server + - "'postfix' in ansible_facts.packages" - rhel9cis_rule_2_2_15 tags: - level1-server - level1-workstation - - scored - - dhcp - - services - patch + - postfix - rule_2.2.15 -- name: "2.2.16 | L1 | PATCH | Ensure CUPS is not enabled" - service: - name: cups - state: stopped - enabled: no +# The name title of the service says mask the service, but the fix allows for both options +# Options available in default/main if to remove the package default is false just mask the server service +- name: "2.2.16 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked" + block: + - name: "2.2.16 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | remove package" + ansible.builtin.package: + name: nfs-utils + state: absent + when: + - not rhel9cis_use_nfs_server + - not rhel9cis_use_nfs_service + + - name: "2.2.16 | PATCH | Ensure nfs-utils is not installed or the nfs-server service is masked | mask service" + ansible.builtin.systemd: + name: nfs-server + masked: true + state: stopped + when: + - not rhel9cis_use_nfs_server + - rhel9cis_use_nfs_service when: - - not rhel9cis_cups_server - - "'cups' in ansible_facts.packages" + - "'nfs-utils' in ansible_facts.packages" - rhel9cis_rule_2_2_16 tags: - level1-server - - level2-workstation - - scored - - cups - - services + - level1-workstation - patch + - nfs + - services - rule_2.2.16 -- name: "2.2.17 | L1 | PATCH | Ensure NIS Server is not enabled" - service: - name: ypserv - state: stopped - enabled: no +# The name title of the service says mask the service, but the fix allows for both options +# Options available in default/main if to remove the package default is false just mask the server service +- name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked" + block: + - name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | remove package" + ansible.builtin.package: + name: rpcbind + state: absent + when: + - not rhel9cis_use_rpc_server + - not rhel9cis_use_rpc_service + + - name: "2.2.17 | PATCH | Ensure rpcbind is not installed or the rpcbind services are masked | mask service" + ansible.builtin.systemd: + name: rpcbind.socket + masked: true + state: stopped + when: + - rhel9cis_use_rpc_server + - not rhel9cis_use_rpc_service when: - - not rhel9cis_nis_server - - "'ypserv' in ansible_facts.packages" + - "'rpcbind' in ansible_facts.packages" - rhel9cis_rule_2_2_17 tags: - level1-server - level1-workstation - patch + - rpc - 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 +# The name title of the service says mask the service, but the fix allows for both options +# Options available in default/main if to remove the package default is false just mask the server service +- name: "2.2.18 | PATCH | Ensure rsync service is not enabled " + block: + - name: "2.2.18 | PATCH | Ensure rsync-daemon is not installed or the rsync service is masked | remove package" + ansible.builtin.package: + name: rsync-daemon + state: absent + when: + - not rhel9cis_use_rsync_server + - not rhel9cis_use_rsync_service + + - name: "2.2.18 | PATCH | Ensure rsync service is not enabled | mask service" + ansible.builtin.systemd: + name: rsyncd + masked: true + state: stopped + when: + - rhel9cis_use_rsync_server + - not rhel9cis_use_rsync_service when: - - not rhel9cis_is_mail_server - - "'postfix' in ansible_facts.packages" + - "'rsync' in ansible_facts.packages" - rhel9cis_rule_2_2_18 tags: - level1-server - level1-workstation - patch - - rule_2.2.1 + - rsync + - rule_2.2.18 diff --git a/tasks/section_2/cis_2.3.x.yml b/tasks/section_2/cis_2.3.x.yml index 875eff8d..10a06623 100644 --- a/tasks/section_2/cis_2.3.x.yml +++ b/tasks/section_2/cis_2.3.x.yml @@ -1,43 +1,61 @@ --- -- name: "2.3.1 | L1 | PATCH | Ensure NIS Client is not installed" - package: - name: ypbind +- name: "2.3.1 | PATCH | Ensure telnet client is not installed" + ansible.builtin.package: + name: telnet state: absent when: - - not rhel9cis_ypbind_required - - "'ypbind' in ansible_facts.packages" + - not rhel9cis_telnet_required + - "'telnet' in ansible_facts.packages" - rhel9cis_rule_2_3_1 tags: - level1-server - level1-workstation - patch + - telnet - rule_2.3.1 -- name: "2.3.2 | L1 | PATCH | Ensure telnet client is not installed" - package: - name: telnet +- name: "2.3.2 | PATCH | Ensure LDAP client is not installed" + ansible.builtin.package: + name: openldap-clients state: absent when: - - not rhel9cis_telnet_required - - "'telnet' in ansible_facts.packages" + - not rhel9cis_openldap_clients_required + - "'openldap-clients' in ansible_facts.packages" - rhel9cis_rule_2_3_2 tags: - level1-server - level1-workstation - patch + - ldap - rule_2.3.2 -- name: "2.3.3 | L1 | PATCH | Ensure LDAP client is not installed" - package: - name: openldap-clients +- name: "2.3.3 | PATCH | Ensure TFTP client is not installed" + ansible.builtin.package: + name: tftp state: absent when: - - not rhel9cis_openldap_clients_required - - "'openldap-clients' in ansible_facts.packages" + - not rhel9cis_tftp_client + - "'tftp' in ansible_facts.packages" - rhel9cis_rule_2_3_3 tags: - level1-server - level1-workstation - patch + - tftp - rule_2.3.3 + +- name: "2.3.4 | PATCH | Ensure FTP client is not installed" + ansible.builtin.package: + name: ftp + state: absent + when: + - not rhel9cis_tftp_client + - "'ftp' in ansible_facts.packages" + - rhel9cis_rule_2_3_4 + tags: + - level1-server + - level1-workstation + - patch + - ftp + - rule_2.3.4 diff --git a/tasks/section_2/cis_2.4.yml b/tasks/section_2/cis_2.4.yml new file mode 100644 index 00000000..ce02b408 --- /dev/null +++ b/tasks/section_2/cis_2.4.yml @@ -0,0 +1,39 @@ +--- + +- name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked" + block: + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Get list of services" + ansible.builtin.shell: systemctl list-units --type=service + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_2_4_services + + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Get list of sockets" + ansible.builtin.shell: systemctl list-units --type=sockets + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_2_4_sockets + + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Display list of services" + ansible.builtin.debug: + msg: + - "Warning!! Below are the list of services and sockets, both active and inactive" + - "Please review to make sure all are essential" + - "{{ rhel9cis_2_4_services.stdout_lines }}" + - "{{ rhel9cis_2_4_sockets.stdout_lines }}" + + - name: "2.4 | AUDIT | Ensure nonessential services listening on the system are removed or masked | Warn Count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '2.4' + when: + - rhel9cis_rule_2_4 + tags: + - level1-server + - level1-workstation + - manual + - audit + - services + - rule_2.4 diff --git a/tasks/section_2/main.yml b/tasks/section_2/main.yml index 1c99c032..39b912d5 100644 --- a/tasks/section_2/main.yml +++ b/tasks/section_2/main.yml @@ -1,13 +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.1 | Time Synchronization" + ansible.builtin.import_tasks: cis_2.1.x.yml - name: "SECTION | 2.2 | Special Purpose Services" - include: cis_2.2.x.yml + ansible.builtin.import_tasks: cis_2.2.x.yml - name: "SECTION | 2.3 | Service Clients" - include: cis_2.3.x.yml + ansible.builtin.import_tasks: cis_2.3.x.yml + +- name: "SECTION | 2.4 | Nonessential services removed" + ansible.builtin.import_tasks: cis_2.4.yml diff --git a/tasks/section_3/cis_3.1.x.yml b/tasks/section_3/cis_3.1.x.yml index ad692faf..7ffe31c2 100644 --- a/tasks/section_3/cis_3.1.x.yml +++ b/tasks/section_3/cis_3.1.x.yml @@ -1,43 +1,83 @@ --- -- name: "3.1.1 | L1 | PATCH | Ensure IP forwarding is disabled" +# The CIS Control wants IPv6 disabled if not in use. +# We are using the rhel9cis_ipv6_required to specify if you have IPv6 in use +- name: "3.1.1 | PATCH | Ensure IPv6 status is identified" 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 | PATCH | Ensure IPv6 status is identified | refresh" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv6_route: true - - 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 + - name: "3.1.1 | PATCH | Ensure IPv6 status is identified | disable" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-disable_ipv6.conf" when: - - not rhel9cis_is_router + - not rhel9cis_ipv6_required - rhel9cis_rule_3_1_1 tags: - level1-server - level1-workstation - - sysctl + - manual - patch + - ipv6 + - networking - 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 +- name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled" + block: + - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if nmcli command is available" + ansible.builtin.shell: rpm -q NetworkManager + changed_when: false + failed_when: false + check_mode: false + register: rhel_09_nmcli_available + + - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if wifi is enabled" + ansible.builtin.shell: nmcli radio wifi + register: rhel_09_wifi_enabled + changed_when: rhel_09_wifi_enabled.stdout != "disabled" + failed_when: false + when: rhel_09_nmcli_available.rc == 0 + + - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Disable wifi if enabled" + ansible.builtin.shell: nmcli radio all off + changed_when: false + failed_when: false + when: rhel_09_wifi_enabled is changed when: - - not rhel9cis_is_router - rhel9cis_rule_3_1_2 tags: - level1-server - - level1-workstation - - sysctl - patch + - wireless - rule_3.1.2 + +- name: "3.1.3 | PATCH | Ensure TIPC is disabled" + block: + - name: "3.1.3 | PATCH | Ensure TIPC is disabled" + ansible.builtin.template: + src: "etc/modprobe.d/modprobe.conf.j2" + dest: "/etc/modprobe.d/{{ item }}.conf" + mode: "0600" + owner: root + group: root + loop: + - tipc + # note the item used in the template + + - name: "3.1.3 | PATCH | Ensure TIPC is disabled | blacklist" + ansible.builtin.lineinfile: + path: /etc/modprobe.d/blacklist.conf + regexp: "^(#)?blacklist tipc(\\s|$)" + line: "blacklist tipc" + create: true + mode: 0600 + when: + - rhel9cis_rule_3_1_3 + tags: + - level2-server + - level2-workstation + - patch + - tipc + - rule_3.1.3 diff --git a/tasks/section_3/cis_3.2.x.yml b/tasks/section_3/cis_3.2.x.yml index ce855070..cc5567f0 100644 --- a/tasks/section_3/cis_3.2.x.yml +++ b/tasks/section_3/cis_3.2.x.yml @@ -1,22 +1,29 @@ --- -- name: "3.2.1 | L1 | PATCH | Ensure source routed packets are not accepted" +- name: "3.2.1 | PATCH | Ensure IP forwarding is disabled" 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 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv4 forwarding" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + + - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | IPv6" + block: + - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding | Set Fact" + ansible.builtin.set_fact: + rhel9cis_flush_ipv6_route: true + + - name: "3.2.1 | PATCH | Ensure IP forwarding is disabled | Disable IPv6 forwarding" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" - - 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: + - not rhel9cis_is_router - rhel9cis_rule_3_2_1 tags: - level1-server @@ -25,131 +32,21 @@ - patch - rule_3.2.1 -- name: "3.2.2 | L1 | PATCH | Ensure ICMP redirects are not accepted" +- name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled" 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: + - name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + - name: "3.2.2 | PATCH | Ensure packet redirect sending is disabled" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + when: + - not rhel9cis_is_router - 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 + - rule_3.2.2 diff --git a/tasks/section_3/cis_3.3.x.yml b/tasks/section_3/cis_3.3.x.yml index 8789558c..e8f3a5f6 100644 --- a/tasks/section_3/cis_3.3.x.yml +++ b/tasks/section_3/cis_3.3.x.yml @@ -1,61 +1,194 @@ --- -- 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 +- name: "3.3.1 | PATCH | Ensure source routed packets are not accepted" + block: + - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv4 | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv4" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + + - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6" + block: + - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6 | Set Fact" + ansible.builtin.set_fact: + rhel9cis_flush_ipv6_route: true + + - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" + when: rhel9cis_ipv6_required when: - rhel9cis_rule_3_3_1 tags: - - level2-server - - level2-workstation + - level1-server + - level1-workstation + - sysctl - 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 +- name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted" + block: + - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv4 | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv4" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + + - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6" + block: + - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6 | Set Fact" + ansible.builtin.set_fact: + rhel9cis_flush_ipv6_route: true + + - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl.conf" + when: rhel9cis_ipv6_required when: - rhel9cis_rule_3_3_2 tags: - - level2-server - - level2-workstation + - level1-server + - level1-workstation + - sysctl - 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 +- name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted" + block: + - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_3 tags: - - level2-server - - level2-workstation + - level1-server + - level1-workstation + - sysctl - 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 +- name: "3.3.4 | PATCH | Ensure suspicious packets are logged" + block: + - name: "3.3.4 | PATCH | Ensure suspicious packets are logged | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.4 | PATCH | Ensure suspicious packets are logged" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" when: - rhel9cis_rule_3_3_4 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.3.4 + +- name: "3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored" + block: + - name: "3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: 3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + when: + - rhel9cis_rule_3_3_5 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.3.5 + +- name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored" + block: + - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + when: + - rhel9cis_rule_3_3_6 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.3.6 + +- name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled" + block: + - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + when: + - rhel9cis_rule_3_3_7 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.3.7 + +- name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled" + block: + - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv4_route: true + + - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv4_sysctl.conf" + when: + - rhel9cis_rule_3_3_8 + tags: + - level1-server + - level1-workstation + - sysctl + - patch + - rule_3.3.8 + +- name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted" + block: + - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted | IPv6 | Set Fact" + ansible.builtin.set_fact: + rhel9cis_sysctl_update: true + rhel9cis_flush_ipv6_route: true + + - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted | IPv6" + ansible.builtin.debug: + msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-netipv6_sysctl" + when: + - rhel9cis_ipv6_required + - rhel9cis_rule_3_3_9 tags: - level2-server - level2-workstation + - sysctl - patch - - rule_3.3.4 + - rule_3.3.9 diff --git a/tasks/section_3/cis_3.4.1.1.yml b/tasks/section_3/cis_3.4.1.1.yml deleted file mode 100644 index 3373d97c..00000000 --- a/tasks/section_3/cis_3.4.1.1.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- - -- 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.1.x.yml b/tasks/section_3/cis_3.4.1.x.yml new file mode 100644 index 00000000..8a7e7212 --- /dev/null +++ b/tasks/section_3/cis_3.4.1.x.yml @@ -0,0 +1,59 @@ +--- + +- name: "3.4.1.1 | PATCH | Ensure nftables is installed" + ansible.builtin.package: + name: + - nftables + state: present + when: + - rhel9cis_rule_3_4_1_1 + - rhel9cis_firewall == 'nftables' + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.1.1 + +- name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use" + block: + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | nftables" + ansible.builtin.systemd: + name: "{{ item }}" + masked: true + loop: + - firewalld + when: + - item in ansible_facts.packages + - rhel9cis_firewall == 'nftables' + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | firewalld" + ansible.builtin.systemd: + name: "{{ item }}" + masked: true + loop: + - nftables + when: + - item in ansible_facts.packages + - rhel9cis_firewall == 'firewalld' + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | package installed" + ansible.builtin.package: + name: "{{ rhel9cis_firewall }}" + state: installed + + - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | {{ rhel9cis_firewall }} started and enabled" + ansible.builtin.systemd: + name: "{{ rhel9cis_firewall }}" + enabled: true + state: started + + when: + - rhel9cis_rule_3_4_1_2 + tags: + - level1-server + - level1-workstation + - patch + - firewalld + - nftables + - rule_3.4.1.2 diff --git a/tasks/section_3/cis_3.4.2.x.yml b/tasks/section_3/cis_3.4.2.x.yml index 9f90c674..865fe59b 100644 --- a/tasks/section_3/cis_3.4.2.x.yml +++ b/tasks/section_3/cis_3.4.2.x.yml @@ -1,10 +1,16 @@ --- -- name: "3.4.2.1 | L1 | PATCH | Ensure firewalld service is enabled and running" - service: - name: firewalld - state: started - enabled: yes +- name: "3.4.2.1 | PATCH | Ensure firewalld default zone is set" + block: + - name: "3.4.2.1 | AUDIT | Ensure firewalld default zone is set" + ansible.builtin.shell: "firewall-cmd --get-default-zone | grep {{ rhel9cis_default_zone }}" + changed_when: false + failed_when: ( firewalld_zone_set.rc not in [ 0, 1 ] ) + register: firewalld_zone_set + + - name: "3.4.2.1 | AUDIT | Ensure firewalld default zone is set" + ansible.builtin.command: firewall-cmd --set-default-zone="{{ rhel9cis_default_zone }}" + when: firewalld_zone_set.rc != 0 when: - rhel9cis_firewall == "firewalld" - rhel9cis_rule_3_4_2_1 @@ -12,40 +18,155 @@ - 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 + - firewalld + - rule_3.4.2.1 + +- name: "3.4.2.2 | AUDIT | Ensure at least one nftables table exists" + block: + - name: "3.4.2.2 | AUDIT | Ensure a table exists | Check for tables" + ansible.builtin.command: nft list tables + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_2_nft_tables + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Show existing tables" + ansible.builtin.debug: + msg: + - "Below are the current nft tables, please review" + - "{{ rhel9cis_3_4_2_2_nft_tables.stdout_lines }}" + when: rhel9cis_3_4_2_2_nft_tables.stdout | length > 0 + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Alert on no tables" + ansible.builtin.debug: + msg: + - "Warning!! You currently have no nft tables, please review your setup" + - 'Use the command "nft create table inet " to create a new table' + when: + - rhel9cis_3_4_2_2_nft_tables.stdout | length == 0 + - not rhel9cis_nft_tables_autonewtable + + - name: "3.4.2.2 | AUDIT | Ensure an nftables table exists | Alert on no tables | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: + - rhel9cis_3_4_2_2_nft_tables.stdout | length == 0 + - not rhel9cis_nft_tables_autonewtable + + - name: "3.4.2.2 | PATCH | Ensure a table exists | Create table if needed" + ansible.builtin.command: nft create table inet "{{ rhel9cis_nft_tables_tablename }}" + failed_when: false + when: rhel9cis_nft_tables_autonewtable + vars: + warn_control_id: '3.4.2.2' when: - - rhel9cis_firewall == "firewalld" + - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_2 tags: - - skip_ansible_lint - level1-server - level1-workstation - patch - - rule_3_4_2_2 + - nftables + - rule_3.4.2.2 + +- name: "3.4.2.3 | PATCH | Ensure nftables base chains exist" + block: + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for INPUT" + ansible.builtin.shell: nft list ruleset | grep 'hook input' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_3_input_chains + + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for FORWARD" + ansible.builtin.shell: nft list ruleset | grep 'hook forward' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_3_forward_chains -- name: "3.4.2.3 | L1 | PATCH | Ensure nftables is not enabled with firewalld" - systemd: - name: nftables - enabled: false - masked: true + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Get current chains for OUTPUT" + ansible.builtin.shell: nft list ruleset | grep 'hook output' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_3_output_chains + + - name: "3.4.2.3 | AUDIT | Ensure nftables base chains exist | Display chains for review" + ansible.builtin.debug: + msg: + - "Below are the current INPUT chains" + - "{{ rhel9cis_3_4_2_3_input_chains.stdout_lines }}" + - "Below are the current FORWARD chains" + - "{{ rhel9cis_3_4_2_3_forward_chains.stdout_lines }}" + - "Below are teh current OUTPUT chains" + - "{{ rhel9cis_3_4_2_3_output_chains.stdout_lines }}" + when: not rhel9cis_nft_tables_autochaincreate + + - name: "3.4.2.3 | PATCH | Ensure nftables base chains exist | Create chains if needed" + ansible.builtin.shell: "{{ item }}" + failed_when: false + loop: + - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" input { type filter hook input priority 0 \; } + - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" forward { type filter hook forward priority 0 \; } + - nft create chain inet "{{ rhel9cis_nft_tables_tablename }}" output { type filter hook output priority 0 \; } + when: rhel9cis_nft_tables_autochaincreate when: - - rhel9cis_firewall == "firewalld" + - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_3 tags: - level1-server - level1-workstation - patch - - rule_3_4_2_3 + - nftables + - rule_3.4.2.3 + +- name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured" + block: + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather iif lo accept existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'iif "lo" accept' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_iiflo + + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather ip saddr existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip saddr' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_ipsaddr + + - name: "3.4.2.4 | AUDIT | Ensure host based firewall loopback traffic is configured | Gather ip6 saddr existence | nftables" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep 'ip6 saddr' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_4_ip6saddr + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set iif lo accept rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input iif lo accept + when: '"iif \"lo\" accept" not in rhel9cis_3_4_2_4_iiflo.stdout' + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set ip sddr rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip saddr 127.0.0.0/8 counter drop + when: '"ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_4_ipsaddr.stdout' + + - name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | Set ip6 saddr rule | nftables" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip6 saddr ::1 counter drop + when: '"ip6 saddr ::1 counter packets 0 bytes 0 drop" not in rhel9cis_3_4_2_4_ip6saddr.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_2_4 + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.2.4 -- name: "3.4.2.4 | L1 | PATCH | Ensure default zone is set" - command: firewall-cmd --set-default-zone="{{ rhel9cis_default_zone }}" +- name: "3.4.2.4 | PATCH | Ensure host based firewall loopback traffic is configured | firewalld" + ansible.posix.firewalld: + rich_rule: "{{ item }}" + zone: "{{ rhel9cis_default_zone }}" + permanent: true + immediate: true + state: enabled + loop: + - rule family="ipv4" source address="127.0.0.1" destination not address="127.0.0.1" drop + - rule family="ipv6" source address="::1" destination not address="::1" drop when: - rhel9cis_firewall == "firewalld" - rhel9cis_rule_3_4_2_4 @@ -53,50 +174,126 @@ - level1-server - level1-workstation - patch + - nftables - rule_3.4.2.4 -- name: "3.4.2.5 | L1 | AUDIT | Ensure network interfaces are assigned to appropriate zone" +- name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports" 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" + - name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports | Get list of services and ports" + ansible.builtin.shell: "firewall-cmd --get-active-zones | awk '!/:/ {print $1}' | while read ZN; do firewall-cmd --list-all --zone=$ZN; done" changed_when: false failed_when: false - check_mode: no - register: rhel9cis_3_4_2_5_interfacepolicy + check_mode: false + register: rhel9cis_3_4_2_5_servicesport - - 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: + - name: "3.4.2.5 | AUDIT | Ensure firewalld drops unnecessary services and ports | Show services and ports" + ansible.builtin.debug: msg: - - "The items below are the policies tied to the interfaces, please correct as needed" - - "{{ rhel9cis_3_4_2_5_interfacepolicy.stdout_lines }}" + - "The items below are the services and ports that are accepted, please correct as needed" + - "{{ rhel9cis_3_4_2_5_servicesport.stdout_lines }}" when: - - rhel9cis_firewall == "firewalld" - rhel9cis_rule_3_4_2_5 tags: - level1-server - level1-workstation + - manual - audit - rule_3.4.2.5 -- name: "3.4.2.6 | L1 | AUDIT | Ensure firewalld drops unnecessary services and ports" +- name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured" 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" + - name: "3.4.2.6 | AUDIT | EEnsure nftables established connections are configured | Gather incoming connection rules" + ansible.builtin.shell: nft list ruleset | awk '/hook input/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' changed_when: false failed_when: false - check_mode: no - register: rhel9cis_3_4_2_6_servicesport + register: rhel9cis_3_4_2_6_inconnectionrule - - 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 }}" + - name: "3.4.2.6| AUDIT | Ensure nftables established connections are configured | Gather outbound connection rules" + ansible.builtin.shell: nft list ruleset | awk '/hook output/,/}/' | grep -E 'ip protocol (tcp|udp|icmp) ct state' + changed_when: false + failed_when: false + register: rhel9cis_3_4_2_6_outconnectionrule + + - name: "3.4.2.6| PATCH | Ensure nftables established connections are configured | Add input tcp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol tcp ct state established accept + when: '"ip protocol tcp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add input udp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol udp ct state established accept + when: '"ip protocol udp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add input icmp established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input ip protocol icmp ct state established accept + when: '"ip protocol icmp ct state established accept" not in rhel9cis_3_4_2_6_inconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output tcp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol tcp ct state new,related,established accept + when: '"ip protocol tcp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output udp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol udp ct state new,related,established accept + when: '"ip protocol udp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' + + - name: "3.4.2.6 | PATCH | Ensure nftables established connections are configured | Add output icmp new, related, established accept policy" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" output ip protocol icmp ct state new,related,established accept + when: '"ip protocol icmp ct state established,related,new accept" not in rhel9cis_3_4_2_6_outconnectionrule.stdout' when: - - rhel9cis_firewall == "firewalld" + - rhel9cis_firewall == "nftables" - rhel9cis_rule_3_4_2_6 tags: - level1-server - level1-workstation - - audit + - patch + - nftables - rule_3.4.2.6 + +- name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy" + block: + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook input deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook input' + failed_when: false + changed_when: false + register: rhel9cis_3_4_2_7_inputpolicy + + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook forward deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook forward' + failed_when: false + changed_when: false + register: rhel9cis_3_4_2_7_forwardpolicy + + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for hook output deny policy" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'hook output' + failed_when: false + changed_when: false + register: rhel9cis_3_4_2_7_outputpolicy + + - name: "3.4.2.7 | AUDIT | Ensure nftables default deny firewall policy | Check for SSH allow" + ansible.builtin.shell: nft list table inet "{{ rhel9cis_nft_tables_tablename }}" | grep 'ssh' + failed_when: false + changed_when: false + register: rhel9cis_3_4_2_7_sshallowcheck + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Enable SSH traffic" + ansible.builtin.command: nft add rule inet "{{ rhel9cis_nft_tables_tablename }}" input tcp dport ssh accept + when: '"tcp dport ssh accept" not in rhel9cis_3_4_2_7_sshallowcheck.stdout' + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Set hook input deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" input { policy drop \; } + when: '"type filter hook input priority 0; policy drop;" not in rhel9cis_3_4_2_7_inputpolicy.stdout' + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Create hook forward deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" forward { policy drop \; } + when: '"type filter hook forward priority 0; policy drop;" not in rhel9cis_3_4_2_7_forwardpolicy.stdout' + + - name: "3.4.2.7 | PATCH | Ensure nftables default deny firewall policy | Create hook output deny policy" + ansible.builtin.command: nft chain inet "{{ rhel9cis_nft_tables_tablename }}" output { policy drop \; } + when: '"type filter hook output priority 0; policy drop;" not in rhel9cis_3_4_2_7_outputpolicy.stdout' + when: + - rhel9cis_firewall == "nftables" + - rhel9cis_rule_3_4_2_7 + tags: + - level1-server + - level1-workstation + - patch + - nftables + - rule_3.4.2.7 diff --git a/tasks/section_3/cis_3.4.3.x.yml b/tasks/section_3/cis_3.4.3.x.yml deleted file mode 100644 index 202daa02..00000000 --- a/tasks/section_3/cis_3.4.3.x.yml +++ /dev/null @@ -1,264 +0,0 @@ ---- - -- 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 deleted file mode 100644 index edeb4980..00000000 --- a/tasks/section_3/cis_3.4.4.1.x.yml +++ /dev/null @@ -1,144 +0,0 @@ ---- - -- 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 deleted file mode 100644 index 4e96f49c..00000000 --- a/tasks/section_3/cis_3.4.4.2.x.yml +++ /dev/null @@ -1,136 +0,0 @@ ---- - -- 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 deleted file mode 100644 index 5a60a5e6..00000000 --- a/tasks/section_3/cis_3.5.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - -- 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 deleted file mode 100644 index 9b393184..00000000 --- a/tasks/section_3/cis_3.6.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- - -- name: "3.6 | L2 | PATCH | Disable IPv6" - replace: - dest: /etc/default/grub - regexp: '(^GRUB_CMDLINE_LINUX\s*\=\s*)(?:")(.+)(? 0 + tags: + - level2-server + - level2-workstation + - patch + - auditd diff --git a/tasks/section_4/cis_4.1.3.x.yml b/tasks/section_4/cis_4.1.3.x.yml new file mode 100644 index 00000000..922ea616 --- /dev/null +++ b/tasks/section_4/cis_4.1.3.x.yml @@ -0,0 +1,292 @@ +--- + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.1 | PATCH | Ensure changes to system administration scope (sudoers) is collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_1 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.1 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.2 | PATCH | Ensure actions as another user are always logged" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_2 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.2 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.3 | PATCH | Ensure events that modify the sudo log file are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_3 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.3 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.4 | PATCH | Ensure events that modify date and time information are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_4 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.4 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.5 | PATCH | Ensure events that modify the system's network environment are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_5 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.5 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" + block: + - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" + ansible.builtin.shell: for i in $(df | grep '^/dev' | awk '{ print $NF }'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done + changed_when: false + failed_when: false + check_mode: false + register: priv_procs + + - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" + ansible.builtin.set_fact: + update_audit_template: true + notify: update auditd + when: + - rhel9cis_rule_4_1_3_6 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.6 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.7 | PATCH | Ensure unsuccessful unauthorized file access attempts are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_7 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3_7 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.8 | PATCH | Ensure events that modify user/group information are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_8 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.8 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.9 | PATCH | Ensure discretionary access control permission modification events are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_9 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.9 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.10 | PATCH | Ensure successful file system mounts are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_10 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.10 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.11 | PATCH | Ensure session initiation information is collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_11 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.11 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.12 | PATCH | Ensure login and logout events are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_12 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.12 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.13 | PATCH | Ensure file deletion events by users are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_13 + tags: + - level2-server + - level2-workstation + - auditd + - patch + - rule_4.1.3.13 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.14 | PATCH | Ensure events that modify the system's Mandatory Access Controls are collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_14 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.14 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.15 | PATCH | Ensure successful and unsuccessful attempts to use the chcon command are recorded" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_15 + tags: + - level2-server + - level2- workstation + - patch + - auditd + - rule_4.1.3.15 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.16 | PATCH | Ensure successful and unsuccessful attempts to use the setfacl command are recorded" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_16 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.16 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.17 | PATCH | Ensure successful and unsuccessful attempts to use the chacl command are recorded" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_17 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.17 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.18 | PATCH | Ensure successful and unsuccessful attempts to use the usermod command are recorded" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_18 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.18 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.19 | PATCH | Ensure kernel module loading and unloading is collected" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_19 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.3.19 + +# All changes selected are managed by the POST audit and handlers to update +- name: "4.1.3.20 | PATCH | Ensure the audit configuration is immutable" + ansible.builtin.set_fact: + update_audit_template: true + when: + - rhel9cis_rule_4_1_3_20 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.20 + +- name: "4.1.3.21 | AUDIT | Ensure the running and on disk configuration is the same" + ansible.builtin.debug: + msg: + - "Please run augenrules --load if you suspect there is a configuration that is not active" + when: + - rhel9cis_rule_4_1_3_21 + tags: + - level2-server + - level2-workstation + - manual + - patch + - auditd + - rule_4.1.3.21 + +- name: Auditd | 4.1.3 | Auditd controls updated + ansible.builtin.debug: + msg: "Auditd Controls handled in POST using template - updating /etc/auditd/rules.d/99_auditd.rules" + changed_when: false + when: + - update_audit_template diff --git a/tasks/section_4/cis_4.1.4.x.yml b/tasks/section_4/cis_4.1.4.x.yml new file mode 100644 index 00000000..ec3eebd5 --- /dev/null +++ b/tasks/section_4/cis_4.1.4.x.yml @@ -0,0 +1,184 @@ +--- + +- name: | + "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" + "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" + "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" + + block: + - name: "4.1.4.1 | AUDIT | Ensure audit log files are mode 0640 or less permissive | discover file" + ansible.builtin.shell: grep ^log_file /etc/audit/auditd.conf | awk '{ print $NF }' + changed_when: false + register: audit_discovered_logfile + + - name: "4.1.4.1 | AUDIT | Ensure audit log files are mode 0640 or less permissive | stat file" + ansible.builtin.stat: + path: "{{ audit_discovered_logfile.stdout }}" + changed_when: false + register: auditd_logfile + + - name: | + "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" + "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" + "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" + ansible.builtin.file: + path: "{{ audit_discovered_logfile.stdout }}" + mode: "{% if auditd_logfile.stat.mode != '0600' %}0640{% endif %}" + owner: root + group: root + when: + - rhel9cis_rule_4_1_4_1 or + rhel9cis_rule_4_1_4_2 or + rhel9cis_rule_4_1_4_3 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.1 + - rule_4.1.4.2 + - rule_4.1.4.3 + +- name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive" + block: + - name: "4.1.4.4 | AUDIT | Ensure the audit log directory is 0750 or more restrictive | get current permissions" + ansible.builtin.stat: + path: "{{ audit_discovered_logfile.stdout | dirname }}" + register: auditlog_dir + + - name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive | set" + ansible.builtin.file: + path: "{{ audit_discovered_logfile.stdout | dirname }}" + state: directory + mode: 0750 + when: not auditlog_dir.stat.mode is match('07(0|5)0') + when: + - rhel9cis_rule_4_1_4_4 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.4 + +- name: "4.1.4.5 | PATCH | Ensure audit configuration files are 640 or more restrictive" + ansible.builtin.file: + path: "{{ item.path }}" + mode: 0640 + loop: "{{ auditd_conf_files.files }}" + loop_control: + label: "{{ item.path }}" + when: + - item.mode != '06(0|4)0' + - rhel9cis_rule_4_1_4_5 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.5 + +- name: "4.1.4.6 | PATCH | Ensure audit configuration files are owned by root" + ansible.builtin.file: + path: "{{ item.path }}" + owner: root + loop: "{{ auditd_conf_files.files }}" + loop_control: + label: "{{ item.path }}" + when: + - rhel9cis_rule_4_1_4_6 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.6 + +- name: "4.1.4.7 | PATCH | Ensure audit configuration files belong to group root" + ansible.builtin.file: + path: "{{ item.path }}" + group: root + loop: "{{ auditd_conf_files.files }}" + loop_control: + label: "{{ item.path }}" + when: + - rhel9cis_rule_4_1_4_7 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.7 + +- name: "4.1.4.8 | PATCH | Ensure audit tools are 755 or more restrictive" + block: + - name: "4.1.4.8 | AUDIT | Get audit binary file stat | get current mode" + ansible.builtin.stat: + path: "{{ item }}" + register: "audit_bins" + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + + - name: "4.1.4.8 | PATCH | Ensure audit tools are 755 or more restrictive | set if required" + ansible.builtin.file: + path: "{{ item.item }}" + mode: 0750 + + loop: "{{ audit_bins.results }}" + loop_control: + label: "{{ item.item }}" + when: not item.stat.mode is match('07(0|5)0') + when: + - rhel9cis_rule_4_1_4_8 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.8 + +- name: "4.1.4.9 | PATCH | Ensure audit tools are owned by root" + ansible.builtin.file: + path: "{{ item }}" + owner: root + group: root + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + when: + - rhel9cis_rule_4_1_4_9 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.9 + +- name: "4.1.4.10 | PATCH | Ensure audit tools belong to group root" + ansible.builtin.file: + path: "{{ item }}" + group: root + loop: + - /sbin/auditctl + - /sbin/aureport + - /sbin/ausearch + - /sbin/autrace + - /sbin/auditd + - /sbin/augenrules + when: + - rhel9cis_rule_4_1_4_10 + tags: + - level2-server + - level2-workstation + - patch + - auditd + - rule_4.1.4.10 diff --git a/tasks/section_4/cis_4.1.x.yml b/tasks/section_4/cis_4.1.x.yml deleted file mode 100644 index fd43b3cb..00000000 --- a/tasks/section_4/cis_4.1.x.yml +++ /dev/null @@ -1,205 +0,0 @@ ---- - -- name: "4.1.3 | L2 | PATCH | Ensure changes to system administration scope (sudoers) 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_3 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.3 - -- name: "4.1.4 | L2 | PATCH | Ensure login and logout events 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_4 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.4 - -- name: "4.1.5 | L2 | PATCH | Ensure session initiation information 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_5 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.5 - -- name: "4.1.6 | L2 | PATCH | Ensure events that modify date and time information 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_6 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.6 - -- name: "4.1.7 | L2 | PATCH | Ensure events that modify the system's Mandatory Access Controls 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_7 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.7 - -- name: "4.1.8 | L2 | PATCH | Ensure events that modify the system's network environment 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_8 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.8 - -- name: "4.1.9 | L2 | PATCH | Ensure discretionary access control permission modification events 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_9 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.9 - -- name: "4.1.10 | L2 | PATCH | Ensure unsuccessful unauthorized file access attempts 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_10 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.10 - -- name: "4.1.11 | L2 | PATCH | Ensure events that modify user/group information 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_11 - tags: - - level2-server - - level2-workstation - - auditd - - patch - - rule_4.1.11 - -- name: "4.1.12 | L2 | PATCH | Ensure successful file system mounts are collected" - block: - - name: "4.1.12 | L2 | AUDIT | 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 - - - 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 index b9d525ea..10e0ac2e 100644 --- a/tasks/section_4/cis_4.2.1.x.yml +++ b/tasks/section_4/cis_4.2.1.x.yml @@ -1,7 +1,7 @@ --- -- name: "4.2.1.1 | L1 | PATCH | Ensure rsyslog installed" - package: +- name: "4.2.1.1 | PATCH | Ensure rsyslog installed" + ansible.builtin.package: name: rsyslog state: present when: @@ -11,12 +11,13 @@ - level1-server - level1-workstation - patch + - rsyslog - rule_4.2.1.1 -- name: "4.2.1.2 | L1 | PATCH | Ensure rsyslog Service is enabled" - service: +- name: "4.2.1.2 | PATCH | Ensure rsyslog Service is enabled" + ansible.builtin.systemd: name: rsyslog - enabled: yes + enabled: true when: - rhel9cis_rule_4_2_1_2 tags: @@ -26,41 +27,55 @@ - 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 +- name: "4.2.1.3 | PATCH | Ensure journald is configured to send logs to rsyslog" + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf + regexp: "^#ForwardToSyslog=|^ForwardToSyslog=" + line: ForwardToSyslog=yes + notify: Restart rsyslog when: - rhel9cis_rule_4_2_1_3 + - rhel9cis_syslog == "rsyslog" tags: - level1-server - level1-workstation - patch - rule_4.2.1.3 -- name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured" +- name: "4.2.1.4 | PATCH | Ensure rsyslog default file permissions configured" + ansible.builtin.lineinfile: + path: /etc/rsyslog.conf + regexp: '^\$FileCreateMode' + line: '$FileCreateMode 0640' + 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 | 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 + - name: "4.2.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" + ansible.builtin.shell: cat /etc/rsyslog.conf changed_when: false - failed_when: no - check_mode: no - register: rhel_09_4_2_1_4_audit + failed_when: false + check_mode: false + register: rhel_09_4_2_1_5_audit - - name: "4.2.1.4 | L1 | AUDIT | Ensure logging is configured | rsyslog current config message out" - debug: + - name: "4.2.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" + ansible.builtin.debug: msg: - "These are the current logging configurations for rsyslog, please review:" - - "{{ rhel_09_4_2_1_4_audit.stdout_lines }}" + - "{{ rhel_09_4_2_1_5_audit.stdout_lines }}" - - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | mail.* log setting" - blockinfile: + - name: "4.2.1.5 | PATCH | Ensure logging is configured | mail.* log setting" + ansible.builtin.blockinfile: path: /etc/rsyslog.conf - state: present - marker: "# {mark} MAIL LOG SETTINGS (ANSIBLE MANAGED)" + marker: "# {mark} MAIL LOG SETTINGS - CIS benchmark - Ansible-lockdown" block: | # mail logging additions to meet CIS standards mail.* -/var/log/mail @@ -68,110 +83,134 @@ mail.warning -/var/log/mail.warning mail.err /var/log/mail.err insertafter: '# Log all the mail messages in one place.' - notify: restart rsyslog + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | news.crit log setting" - blockinfile: + - name: "4.2.1.5 | PATCH | Ensure logging is configured | news.crit log setting" + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present - marker: "# {mark} NEWS LOG SETTINGS (ANSIBLE MANAGED)" + marker: "# {mark} NEWS LOG SETTINGS - CIS benchmark - Ansible-lockdown" 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 + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | Misc. log setting" - blockinfile: + - name: "4.2.1.5 | PATCH | Ensure logging is configured | Misc. log setting" + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present - marker: "# {mark} MISC. LOG SETTINGS (ANSIBLE MANAGED)" + marker: "# {mark} MISC. LOG SETTINGS - CIS benchmark - Ansible-lockdown" 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 + notify: Restart rsyslog when: rhel9cis_rsyslog_ansiblemanaged - - name: "4.2.1.4 | L1 | PATCH | Ensure logging is configured | Local log settings" - blockinfile: + - name: "4.2.1.5 | PATCH | Ensure logging is configured | Local log settings" + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present - marker: "#{mark} LOCAL LOG SETTINGS (ANSIBLE MANAGED)" + marker: "#{mark} LOCAL LOG SETTINGS - CIS benchmark - Ansible-lockdown" block: | - # local log settings + # local log settings to meet CIS standards 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 + notify: Restart rsyslog + + - name: "4.2.1.5 | PATCH | Ensure logging is configured | Auth Settings" + ansible.builtin.blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "#{mark} Auth SETTINGS - CIS benchmark - Ansible-lockdown" + block: | + # Private settings to meet CIS standards + auth,authpriv.* /var/log/secure + insertafter: '#### RULES ####' + notify: Restart rsyslog + + - name: "4.2.1.5 | PATCH | Ensure logging is configured | Cron Settings" + ansible.builtin.blockinfile: + path: /etc/rsyslog.conf + state: present + marker: "#{mark} Cron SETTINGS - CIS benchmark - Ansible-lockdown" + block: | + # Cron settings to meet CIS standards + cron.* /var/log/cron + insertafter: '#### RULES ####' + notify: Restart rsyslog when: - - rhel9cis_rule_4_2_1_4 + - rhel9cis_rule_4_2_1_5 tags: - level1-server - level1-workstation - patch - rsyslog - - rule_4.2.1.4 + - rule_4.2.1.5 -- name: "4.2.1.5 | L1 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" - blockinfile: +- name: "4.2.1.6 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" + ansible.builtin.blockinfile: path: /etc/rsyslog.conf state: present block: | - # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional - *.* @@{{ rhel9cis_remote_log_server }} + # target can be IP or FQDN + *.* action(type="omfwd" target="{{ rhel9cis_remote_log_host }}" port="{{ rhel9cis_remote_log_port }}" protocol="{{ rhel9cis_remote_log_protocol }}" action.resumeRetryCount="{{ rhel9cis_remote_log_retrycount }}" queue.type="LinkedList" queue.size="{{ rhel9cis_remote_log_queuesize }}") insertafter: EOF register: result failed_when: - result is failed - result.rc != 257 - notify: restart rsyslog + notify: Restart rsyslog when: - - rhel9cis_rule_4_2_1_5 - - rhel9cis_remote_log_server is defined + - rhel9cis_rule_4_2_1_6 + - rhel9cis_remote_log_server tags: - level1-server - level1-workstation - patch - - rule_4.2.1.5 - rsyslog + - rule_4.2.1.6 -- name: "4.2.1.6 | L1 | PATCH | Ensure remote rsyslog messages are only accepted on designated log hosts." +- name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote client" block: - - name: "4.2.1.6 | L1 | PATCH | Ensure remote rsyslog messages are only accepted on designated log hosts. | When not log host" - replace: + - name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote client. | When not log host" + ansible.builtin.replace: path: /etc/rsyslog.conf - regexp: '({{ item }})' + regexp: '{{ item }}' replace: '#\1' - notify: restart rsyslog - with_items: + notify: Restart rsyslog + loop: - '^(\$ModLoad imtcp)' - '^(\$InputTCPServerRun)' + - '^(module\(load="imtcp"\))' + - '^(input\(type="imtcp")' 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: + - name: "4.2.1.7 | PATCH | Ensure rsyslog is not configured to recieve logs from a remote clients. | When log host" + ansible.builtin.replace: path: /etc/rsyslog.conf regexp: '^#(.*{{ item }}.*)' replace: '\1' - notify: restart rsyslog - with_items: + notify: Restart rsyslog + loop: - 'ModLoad imtcp' - 'InputTCPServerRun' when: rhel9cis_system_is_log_server when: - - rhel9cis_rule_4_2_1_6 + - rhel9cis_rule_4_2_1_7 tags: - level1-server - level1-workstation - patch - - rule_4.2.1.6 - rsyslog + - rule_4.2.1.7 diff --git a/tasks/section_4/cis_4.2.2.x.yml b/tasks/section_4/cis_4.2.2.x.yml index 1c87ed47..2c9355b3 100644 --- a/tasks/section_4/cis_4.2.2.x.yml +++ b/tasks/section_4/cis_4.2.2.x.yml @@ -1,43 +1,199 @@ --- -- 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 +- name: "4.2.2.1.1 | PATCH | Ensure systemd-journal-remote is installed" + ansible.builtin.package: + name: systemd-journal-remote state: present when: - - rhel9cis_rule_4_2_2_1 + - rhel9cis_rule_4_2_2_1_1 tags: - level1-server - level1-workstation + - manual - patch - - rule_4.2.2.1 + - journald + - rule_4.2.2.1.1 -- name: "4.2.2.2 | L1 | PATCH | Ensure journald is configured to compress large log files" - lineinfile: - dest: /etc/systemd/journald.conf +- name: "4.2.2.1.2 | PATCH | Ensure systemd-journal-remote is configured" + ansible.builtin.lineinfile: + path: /etc/systemd/journal-upload.conf + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + notify: Restart journald + loop: + - { regexp: 'URL=', line: 'URL={{ rhel9cis_journal_upload_url }}'} + - { regexp: 'ServerKeyFile=', line: 'ServerKeyFile={{ rhel9cis_journal_upload_serverkeyfile }}'} + - { regexp: 'ServerCertificateFile=', line: 'ServerCertificateFile={{ rhel9cis_journal_servercertificatefile }}'} + - { regexp: 'TrustedCertificateFile=', line: 'TrustedCertificateFile={{ rhel9cis_journal_trustedcertificatefile }}'} + when: + - rhel9cis_rule_4_2_2_1_2 + tags: + - level1-server + - level1-workstation + - manual + - patch + - journald + - rule_4.2.2.1.2 + +- name: "4.2.2.1.3 | PATCH | Ensure systemd-journal-remote is enabled" + ansible.builtin.systemd: + name: systemd-journal-upload + state: started + enabled: true + when: + - rhel9cis_system_is_log_server + - rhel9cis_rule_4_2_2_1_3 + tags: + - level1-server + - level1-workstation + - manual + - patch + - journald + - rule_4.2.2.1.3 + +- name: "4.2.2.1.4 | PATCH | Ensure journald is not configured to recieve logs from a remote client" + ansible.builtin.systemd: + name: systemd-journal-remote.socket + state: stopped + enabled: false + masked: true + when: + - not rhel9cis_system_is_log_server + - rhel9cis_rule_4_2_2_1_4 + tags: + - level1-server + - level1-workstation + - patch + - journald + - rule_4.2.2.1.4 + +- name: "4.2.2.2 | PATCH | Ensure journald service is enabled" + block: + - name: "4.2.2.2 | PATCH | Ensure journald service is enabled | Enable service" + ansible.builtin.systemd: + name: systemd-journald + state: started + enabled: true + + - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Capture status" + ansible.builtin.shell: systemctl is-enabled systemd-journald.service + changed_when: false + failed_when: false + register: rhel9cis_4_2_2_2_status + + - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Alert on bad status" + ansible.builtin.debug: + msg: + - "Warning!! The status of systemd-journald should be static and it is not. Please investigate" + when: "'static' not in rhel9cis_4_2_2_2_status.stdout" + + - name: "4.2.2.2 | AUDIT | Ensure journald service is enabled | Warn Count" + ansible.builtin.import_tasks: warning_facts.yml + when: "'static' not in rhel9cis_4_2_2_2_status.stdout" + vars: + warn_control_id: '4.2.2.2' + when: + - rhel9cis_rule_4_2_2_2 + tags: + - level1-server + - level1-workstation + - audit + - journald + - rule_4.2.2.2 + +- name: "4.2.2.3 | PATCH | Ensure journald is configured to compress large log files" + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf regexp: "^#Compress=|^Compress=" line: Compress=yes - state: present + notify: Restart journald when: - - rhel9cis_rule_4_2_2_2 + - rhel9cis_rule_4_2_2_3 tags: - level1-server - level1-workstation - patch - - rule_4.2.2.2 + - journald + - rule_4.2.2.3 -- name: "4.2.2.3 | L1 | PATCH | Ensure journald is configured to write logfiles to persistent disk" - lineinfile: - dest: /etc/systemd/journald.conf +- name: "4.2.2.4 | PATCH | Ensure journald is configured to write logfiles to persistent disk" + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf regexp: "^#Storage=|^Storage=" line: Storage=persistent - state: present + notify: Restart journald when: - - rhel9cis_rule_4_2_2_3 + - rhel9cis_rule_4_2_2_4 tags: - level1-server - level1-workstation - patch - - rule_4.2.2.3 + - journald + - rule_4.2.2.4 + +# This is counter to control 4.2.1.3?? +- name: "4.2.2.5 | PATCH | Ensure journald is not configured to send logs to rsyslog" + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf + regexp: "^ForwardToSyslog=" + line: "#ForwardToSyslog=yes" + notify: Restart journald + when: + - rhel9cis_rule_4_2_2_5 + tags: + - level1-server + - level2-workstation + - manual + - patch + - journald + - rule_4.2.2.5 + +- name: "4.2.2.6 | PATCH | Ensure journald log rotation is configured per site policy" + ansible.builtin.lineinfile: + path: /etc/systemd/journald.conf + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + notify: Restart journald + loop: + - { regexp: '^#SystemMaxUse=|^SystemMaxUse=', line: 'SystemMaxUse={{ rhel9cis_journald_systemmaxuse }}'} + - { regexp: '^#SystemKeepFree=|^SystemKeepFree=', line: 'SystemKeepFree={{ rhel9cis_journald_systemkeepfree }}' } + - { regexp: '^#RuntimeMaxUse=|^RuntimeMaxUse=', line: 'RuntimeMaxUse={{ rhel9cis_journald_runtimemaxuse }}'} + - { regexp: '^#RuntimeKeepFree=|^RuntimeKeepFree=', line: 'RuntimeKeepFree={{ rhel9cis_journald_runtimekeepfree }}'} + - { regexp: '^#MaxFileSec=|^MaxFileSec=', line: 'MaxFileSec={{ rhel9cis_journald_maxfilesec }}'} + when: + - rhel9cis_rule_4_2_2_6 + tags: + - level1-server + - level1-workstation + - manual + - patch + - journald + - rule_4.2.2.6 + +- name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured" + block: + - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Check for override file" + ansible.builtin.stat: + path: /etc/tmpfiles.d/systemd.conf + register: rhel9cis_4_2_2_7_override + + - name: "4.2.2.7 | AUDIT | Ensure journald default file permissions configured | Set live file" + ansible.builtin.set_fact: + systemd_conf_file: /etc/tmpfiles.d/systemd.conf + when: rhel9cis_4_2_2_7_override.stat.exists + + - name: "4.2.2.7 | PATCH | Ensure journald default file permissions configured | Set permission" + ansible.builtin.lineinfile: + path: "{{ systemd_conf_file | default('/usr/lib/tmpfiles.d/systemd.conf') }}" + regexp: "^z \/var\/log\/journal\/%m\/system.journal (!?06(0|4)0) root" + line: 'z /var/log/journal/%m/system.journal 0640 root systemd-journal - -' + + when: + - rhel9cis_rule_4_2_2_7 + tags: + - level1-server + - level1-workstation + - manual + - patch + - journald + - rule_4.2.2.7 diff --git a/tasks/section_4/cis_4.2.3.yml b/tasks/section_4/cis_4.2.3.yml index d1992696..a3912547 100644 --- a/tasks/section_4/cis_4.2.3.yml +++ b/tasks/section_4/cis_4.2.3.yml @@ -1,13 +1,30 @@ --- -- 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 +- name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured" + block: + - name: "4.2.3 | AUDIT | Ensure permissions on all logfiles are configured | find files" + ansible.builtin.find: + paths: "/var/log" + file_type: file + recurse: true + register: logfiles + + - name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured | change permissions" + ansible.builtin.file: + path: "{{ item.path }}" + mode: 0640 + loop: "{{ logfiles.files }}" + loop_control: + label: "{{ item.path }}" + when: + - item.path != "/var/log/btmp" + - item.path != "/var/log/utmp" + - item.path != "/var/log/wtmp" when: - rhel9cis_rule_4_2_3 tags: - level1-server - level1-workstation - patch + - logfiles - rule_4.2.3 diff --git a/tasks/section_4/cis_4.3.yml b/tasks/section_4/cis_4.3.yml index 853a2159..be17c702 100644 --- a/tasks/section_4/cis_4.3.yml +++ b/tasks/section_4/cis_4.3.yml @@ -1,24 +1,55 @@ --- -- name: "4.3 | L1 | PATCH | Ensure logrotate is configured" +- name: "4.3 | 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 | PATCH | Ensure logrotate is configured | installed" + ansible.builtin.package: + name: rsyslog-logrotate + state: present + + - name: "4.3 | PATCH | Ensure logrotate is configured | scheduled" + ansible.builtin.systemd: + name: logrotate.timer + state: started + enabled: true - - name: "4.3 | L1 | PATCH | Ensure logrotate is configured" - replace: - path: "{{ item.path }}" + - name: "4.3 | PATCH | Ensure logrotate is configured | set default conf" + ansible.builtin.replace: + path: "/etc/logrotate.conf" regexp: '^(\s*)(daily|weekly|monthly|yearly)$' replace: "\\1{{ rhel9cis_logrotate }}" - with_items: - - "{{ log_rotates.files }}" - - { path: "/etc/logrotate.conf" } + + - name: "4.3 | AUDIT | Ensure logrotate is configured | Get non default logrotate settings" + ansible.builtin.find: + paths: /etc/logrotate.d/ + contains: '^(\s*)(?!{{ rhel9cis_logrotate }})(daily|weekly|monthly|yearly)$' + register: log_rotates + + - name: "4.3 | AUDIT | Ensure logrotate is configured" + block: + - name: "4.3 | AUDIT | Ensure logrotate is configured | generate file list" + ansible.builtin.set_fact: + logrotate_non_def_conf: "{{ log_rotates.files | map(attribute='path') | join (', ') }}" + + - name: "4.3 | AUDIT | Ensure logrotate is configured | List configured files" + ansible.builtin.debug: + msg: | + "Warning!! The following files are not covered by default logrotate settings ensure they match site policy" + "{{ logrotate_non_def_conf }}" + loop: "{{ log_rotates.files }}" + + - name: "4.3 | AUDIT | Ensure logrotate is configured | Warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '4.3' + when: log_rotates.matched > 0 + when: - rhel9cis_rule_4_3 tags: - level1-server - level1-workstation + - manual - patch + - logrotate - rule_4.3 diff --git a/tasks/section_4/main.yml b/tasks/section_4/main.yml index aecac9f5..285a2f37 100644 --- a/tasks/section_4/main.yml +++ b/tasks/section_4/main.yml @@ -1,23 +1,29 @@ --- -- name: "SECTION | 4.1| Configure System Accounting (auditd)" - include: cis_4.1.1.x.yml +- name: "SECTION | 4.1 | Configure System Accounting (auditd)" + ansible.builtin.import_tasks: cis_4.1.1.x.yml + when: + - not system_is_container -- name: "SECTION | 4.1.2.x| Configure Data Retention" - include: cis_4.1.2.x.yml +- name: "SECTION | 4.1.2 | Configure Data Retention" + ansible.builtin.import_tasks: cis_4.1.2.x.yml -- name: "SECTION | 4.1.x| Auditd rules" - include: cis_4.1.x.yml +- name: "SECTION | 4.1.3 | Configure Auditd rules" + ansible.builtin.import_tasks: cis_4.1.3.x.yml -- name: "SECTION | 4.2.x| Configure Logging" - include: cis_4.2.1.x.yml +- name: "SECTION | 4.1.4 | Configure Audit files" + ansible.builtin.import_tasks: cis_4.1.4.x.yml + +- name: "SECTION | 4.2 | Configure Logging" + ansible.builtin.import_tasks: 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.2 | Configure journald" + ansible.builtin.import_tasks: cis_4.2.2.x.yml + when: rhel9cis_syslog == 'journald' - name: "SECTION | 4.2.3 | Configure logile perms" - include: cis_4.2.3.yml + ansible.builtin.import_tasks: cis_4.2.3.yml - name: "SECTION | 4.3 | Configure logrotate" - include: cis_4.3.yml + ansible.builtin.import_tasks: cis_4.3.yml diff --git a/tasks/section_5/cis_5.1.x.yml b/tasks/section_5/cis_5.1.x.yml index a0470c55..f897c6c7 100644 --- a/tasks/section_5/cis_5.1.x.yml +++ b/tasks/section_5/cis_5.1.x.yml @@ -1,20 +1,21 @@ --- -- name: "5.1.1 | L1 | PATCH | Ensure cron daemon is enabled" - service: +- name: "5.1.1 | PATCH | Ensure cron daemon is enabled" + ansible.builtin.service: name: crond - enabled: yes + enabled: true when: - rhel9cis_rule_5_1_1 tags: - level1-server - level1-workstation - patch + - cron - rule_5.1.1 -- name: "5.1.2 | L1 | PATCH | Ensure permissions on /etc/crontab are configured" - file: - dest: /etc/crontab +- name: "5.1.2 | PATCH | Ensure permissions on /etc/crontab are configured" + ansible.builtin.file: + path: /etc/crontab owner: root group: root mode: 0600 @@ -24,11 +25,12 @@ - level1-server - level1-workstation - patch + - cron - rule_5.1.2 -- name: "5.1.3 | L1 | PATCH | Ensure permissions on /etc/cron.hourly are configured" - file: - dest: /etc/cron.hourly +- name: "5.1.3 | PATCH | Ensure permissions on /etc/cron.hourly are configured" + ansible.builtin.file: + path: /etc/cron.hourly state: directory owner: root group: root @@ -39,11 +41,12 @@ - level1-server - level1-workstation - patch + - cron - rule_5.1.3 -- name: "5.1.4 | L1 | PATCH | Ensure permissions on /etc/cron.daily are configured" - file: - dest: /etc/cron.daily +- name: "5.1.4 | PATCH | Ensure permissions on /etc/cron.daily are configured" + ansible.builtin.file: + path: /etc/cron.daily state: directory owner: root group: root @@ -54,11 +57,12 @@ - level1-server - level1-workstation - patch + - cron - rule_5.1.4 -- name: "5.1.5 | L1 | PATCH | Ensure permissions on /etc/cron.weekly are configured" - file: - dest: /etc/cron.weekly +- name: "5.1.5 | PATCH | Ensure permissions on /etc/cron.weekly are configured" + ansible.builtin.file: + path: /etc/cron.weekly state: directory owner: root group: root @@ -71,9 +75,9 @@ - patch - rule_5.1.5 -- name: "5.1.6 | L1 | PATCH | Ensure permissions on /etc/cron.monthly are configured" - file: - dest: /etc/cron.monthly +- name: "5.1.6 | PATCH | Ensure permissions on /etc/cron.monthly are configured" + ansible.builtin.file: + path: /etc/cron.monthly state: directory owner: root group: root @@ -86,9 +90,9 @@ - patch - rule_5.1.6 -- name: "5.1.7 | L1 | PATCH | Ensure permissions on /etc/cron.d are configured" - file: - dest: /etc/cron.d +- name: "5.1.7 | PATCH | Ensure permissions on /etc/cron.d are configured" + ansible.builtin.file: + path: /etc/cron.d state: directory owner: root group: root @@ -99,49 +103,61 @@ - level1-server - level1-workstation - patch + - cron - rule_5.1.7 -- name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users" +- name: "5.1.8 | PATCH | Ensure 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 + - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Remove cron.deny" + ansible.builtin.file: + path: /etc/cron.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 | PATCH | Ensure cron is restricted to authorized users | Check if cron.allow exists" + ansible.builtin.stat: + path: "/etc/cron.allow" + register: rhel9cis_5_1_8_cron_allow_state - - 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" }}' + - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Ensure cron.allow is restricted to authorized users" + ansible.builtin.file: + path: /etc/cron.allow + state: '{{ "file" if rhel9cis_5_1_8_cron_allow_state.stat.exists else "touch" }}' owner: root group: root mode: 0600 + when: + - rhel9cis_rule_5_1_8 + tags: + - level1-server + - level1-workstation + - patch + - cron + - rule_5.1.8 - - name: "5.1.8 | L1 | PATCH | Ensure at/cron is restricted to authorized users | Remove cron.deny" - file: - dest: /etc/cron.deny +- name: "5.1.9 | PATCH | Ensure at is restricted to authorized users" + block: + - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Remove at.deny" + ansible.builtin.file: + path: /etc/at.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.9 | PATCH | Ensure at is restricted to authorized users | Check if at.allow exists" + ansible.builtin.stat: + path: "/etc/at.allow" + register: rhel9cis_5_1_9_at_allow_state - - 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" }}' + - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Ensure at.allow is restricted to authorized users" + ansible.builtin.file: + path: /etc/at.allow + state: '{{ "file" if rhel9cis_5_1_9_at_allow_state.stat.exists else "touch" }}' owner: root group: root mode: 0600 when: - - rhel9cis_rule_5_1_8 + - rhel9cis_rule_5_1_9 tags: - level1-server - level1-workstation - patch - - rule_5.1.8 + - cron + - rule_5.1.9 diff --git a/tasks/section_5/cis_5.2.x.yml b/tasks/section_5/cis_5.2.x.yml index cce02269..9054afd2 100644 --- a/tasks/section_5/cis_5.2.x.yml +++ b/tasks/section_5/cis_5.2.x.yml @@ -1,9 +1,8 @@ --- -- name: "5.2.1 | L1 | PATCH | Ensure permissions on /etc/ssh/sshd_config are configured" - file: - dest: /etc/ssh/sshd_config - state: file +- name: "5.2.1 | Ensure permissions on /etc/ssh/sshd_config are configured" + ansible.builtin.file: + path: "/etc/ssh/sshd_config" owner: root group: root mode: 0600 @@ -13,258 +12,272 @@ - level1-server - level1-workstation - patch + - ssh + - permissions - rule_5.2.1 -- name: "5.2.2 | L1 | PATCH | Ensure SSH access is limited" +- name: "5.2.2 | PATCH | Ensure permissions on SSH private host key files are configured" 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 | AUDIT | Ensure permissions on SSH private host key files are configured | Find the SSH private host keys" + ansible.builtin.find: + paths: /etc/ssh + patterns: 'ssh_host_*_key' + recurse: true + file_type: any + register: rhel9cis_5_2_2_ssh_private_host_key - - 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" + - name: "5.2.2 | PATCH | Ensure permissions on SSH private host key files are configured | Set permissions on SSH private host keys" + ansible.builtin.file: + path: "{{ item.path }}" + owner: root + group: root + mode: 0600 + loop: "{{ rhel9cis_5_2_2_ssh_private_host_key.files }}" + loop_control: + label: "{{ item.path }}" when: - rhel9cis_rule_5_2_2 tags: - level1-server - level1-workstation - patch + - ssh + - permissions - rule_5.2.2 -- name: "5.2.3 | L1 | PATCH | Ensure permissions on SSH private host key files are configured" +- name: "5.2.3 | PATCH | Ensure permissions on SSH public 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: + - name: "5.2.3 | AUDIT | Ensure permissions on SSH public host key files are configured | Find the SSH public host keys" + ansible.builtin.find: paths: /etc/ssh - patterns: 'ssh_host_*_key' + patterns: 'ssh_host_*_key.pub' recurse: true file_type: any - register: rhel9cis_5_2_3_ssh_private_host_key + register: rhel9cis_5_2_3_ssh_public_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: + - name: "5.2.3 | PATCH | Ensure permissions on SSH public host key files are configured | Set permissions on SSH public host keys" + ansible.builtin.file: path: "{{ item.path }}" owner: root group: root - mode: 0600 - with_items: - - "{{ rhel9cis_5_2_3_ssh_private_host_key.files }}" + mode: 0644 + loop: "{{ rhel9cis_5_2_3_ssh_public_host_key.files }}" + loop_control: + label: "{{ item.path }}" when: - rhel9cis_rule_5_2_3 tags: - level1-server - level1-workstation - patch + - ssh - rule_5.2.3 -- name: "5.2.4 | L1 | PATCH | Ensure permissions on SSH public host key files are configured" +- name: "5.2.4 | PATCH | Ensure SSH access is limited" 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 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowusers" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^AllowUsers" + line: "AllowUsers {{ rhel9cis_sshd['allowusers'] }}" + validate: sshd -t -f %s + notify: Restart sshd + when: "rhel9cis_sshd['allowusers']|default('') | length > 0" - - 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 }}" + - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for allowgroups" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^AllowGroups" + line: "AllowGroups {{ rhel9cis_sshd['allowgroups'] }}" + validate: sshd -t -f %s + notify: Restart sshd + when: "rhel9cis_sshd['allowgroups']|default('') | length > 0" + + - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denyusers" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^DenyUsers" + line: "DenyUsers {{ rhel9cis_sshd['denyusers'] }}" + validate: sshd -t -f %s + notify: Restart sshd + when: "rhel9cis_sshd['denyusers']|default('') | length > 0" + + - name: "5.2.4 | PATCH | Ensure SSH access is limited | Add line to sshd_config for denygroups" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^DenyGroups" + line: "DenyGroups {{ rhel9cis_sshd['denygroups'] }}" + validate: sshd -t -f %s + notify: Restart sshd + when: "rhel9cis_sshd['denygroups']|default('') | length > 0" when: - rhel9cis_rule_5_2_4 tags: - level1-server - level1-workstation - patch + - ssh - rule_5.2.4 -- name: "5.2.5 | L1 | PATCH | Ensure SSH LogLevel is appropriate" - lineinfile: - state: present - dest: /etc/ssh/sshd_config +- name: "5.2.5 | PATCH | Ensure SSH LogLevel is appropriate" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" regexp: "^#LogLevel|^LogLevel" line: 'LogLevel {{ rhel9cis_ssh_loglevel }}' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_5 tags: - level1-server - level1-workstation - patch + - sshs - 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' +- name: "5.2.6 | PATCH | Ensure SSH PAM is enabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#UsePAM|^UsePAM" + line: 'UsePAM yes' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_6 tags: - - level2-server + - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.7 | PATCH | Ensure SSH root login is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#PermitRootLogin|^PermitRootLogin" + line: 'PermitRootLogin no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_7 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.8 | PATCH | Ensure SSH HostbasedAuthentication is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#HostbasedAuthentication|^HostbasedAuthentication" + line: 'HostbasedAuthentication no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_8 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.9 | PATCH | Ensure SSH PermitEmptyPasswords is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#PermitEmptyPasswords|^PermitEmptyPasswords" + line: 'PermitEmptyPasswords no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_9 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.10 | PATCH | Ensure SSH PermitUserEnvironment is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#PermitUserEnvironment|^PermitUserEnvironment" + line: 'PermitUserEnvironment no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_10 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.11 | PATCH | Ensure SSH IgnoreRhosts is enabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#IgnoreRhosts|^IgnoreRhosts" + line: 'IgnoreRhosts yes' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_11 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.12 | PATCH | Ensure SSH X11 forwarding is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#X11Forwarding|^X11Forwarding" + line: 'X11Forwarding no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_12 tags: - - level1-server + - level2-server - level1-workstation - patch + - ssh - 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'] }}" +- name: "5.2.13 | PATCH | Ensure SSH AllowTcpForwarding is disabled" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#AllowTcpForwarding|^AllowTcpForwarding" + line: 'AllowTcpForwarding no' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_13 tags: - - level1-server - - level1-workstation + - level2-server + - level2-workstation - patch + - ssh - 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'] }}" +- name: "5.2.14 | PATCH | Ensure system-wide crypto policy is not over-ridden" + block: + - name: "5.2.14 | AUDIT | Ensure system-wide crypto policy is not over-ridden" + ansible.builtin.shell: grep -i '^\s*CRYPTO_POLICY=' /etc/sysconfig/sshd + changed_when: false + failed_when: ( ssh_crypto_discovery.rc not in [ 0, 1 ] ) + register: ssh_crypto_discovery + + - name: "5.2.14 | PATCH | Ensure system-wide crypto policy is not over-ridden" + ansible.builtin.shell: sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd + notify: Restart sshd + when: ssh_crypto_discovery.stdout | length > 0 when: - rhel9cis_rule_5_2_14 tags: - level1-server - level1-workstation - patch + - ssh - rule_5.2.14 -- name: "5.2.15 | L1 | PATCH | Ensure SSH warning banner is configured" - lineinfile: - state: present - dest: /etc/ssh/sshd_config +- name: "5.2.15 | PATCH | Ensure SSH warning banner is configured" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" regexp: '^Banner' line: 'Banner /etc/issue.net' when: @@ -273,73 +286,89 @@ - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.16 | PATCH | Ensure SSH MaxAuthTries is set to 4 or less" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: '^(#)?MaxAuthTries \d' + line: 'MaxAuthTries 4' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_16 tags: - level1-server - level1-workstation - patch + - ssh - 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' +- name: "5.2.17 | PATCH | Ensure SSH MaxStartups is configured" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#MaxStartups|^MaxStartups" + line: 'MaxStartups 10:30:60' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_17 tags: - - level2-server - - level2-workstation + - level1-server + - level1-workstation - patch + - ssh - 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' +- name: "5.2.18 | PATCH | Ensure SSH MaxSessions is set to 10 or less" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#MaxSessions|^MaxSessions" + line: 'MaxSessions {{ rhel9cis_ssh_maxsessions }}' + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_18 tags: - level1-server - level1-workstation - patch + - ssh - 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 }}' +- name: "5.2.19 | PATCH | Ensure SSH LoginGraceTime is set to one minute or less" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: "^#LoginGraceTime|^LoginGraceTime" + line: "LoginGraceTime {{ rhel9cis_sshd['logingracetime'] }}" + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_19 tags: - level1-server - level1-workstation - patch + - ssh - 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 +- name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured" + block: + - name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured | Add line in sshd_config for ClientAliveInterval" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: '^ClientAliveInterval' + line: "ClientAliveInterval {{ rhel9cis_sshd['clientaliveinterval'] }}" + validate: sshd -t -f %s + + - name: "5.2.20 | PATCH | Ensure SSH Idle Timeout Interval is configured | Ensure SSH ClientAliveCountMax set to <= 3" + ansible.builtin.lineinfile: + path: "{{ rhel9_cis_sshd_config_file }}" + regexp: '^ClientAliveCountMax' + line: "ClientAliveCountMax {{ rhel9cis_sshd['clientalivecountmax'] }}" + validate: sshd -t -f %s when: - rhel9cis_rule_5_2_20 tags: - level1-server - level1-workstation - patch + - ssh - rule_5.2.20 diff --git a/tasks/section_5/cis_5.3.x.yml b/tasks/section_5/cis_5.3.x.yml index c643b0e5..2f63b232 100644 --- a/tasks/section_5/cis_5.3.x.yml +++ b/tasks/section_5/cis_5.3.x.yml @@ -1,90 +1,138 @@ --- -- 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 +- name: "5.3.1 | PATCH | Ensure sudo is installed" + ansible.builtin.package: + name: sudo + state: present when: - rhel9cis_rule_5_3_1 tags: - level1-server - level1-workstation - patch - - authselect + - sudo - rule_5.3.1 -- name: "5.3.2 | L1 | PATCH | Select authselect profile" +- name: "5.3.2 | PATCH | Ensure sudo commands use pty" + ansible.builtin.lineinfile: + path: /etc/sudoers + line: "Defaults use_pty" + validate: '/usr/sbin/visudo -cf %s' + when: + - rhel9cis_rule_5_3_2 + tags: + - level1-server + - level1-workstation + - patch + - sudo + - rule_5.3.2 + +- name: "5.3.3 | PATCH | Ensure sudo log file exists" + ansible.builtin.lineinfile: + path: /etc/sudoers + regexp: '^Defaults logfile=' + line: 'Defaults logfile="{{ rhel9cis_sudolog_location }}"' + validate: '/usr/sbin/visudo -cf %s' + when: + - rhel9cis_rule_5_3_3 + tags: + - level1-server + - level1-workstation + - patch + - sudo + - rule_5.3.3 + +- name: "5.3.4 | PATCH | Ensure users must provide password for escalation" + ansible.builtin.replace: + path: "{{ item }}" + regexp: '^([^#|{% if system_is_ec2 %}ec2-user{% endif %}].*)NOPASSWD(.*)' + replace: '\1PASSWD\2' + validate: '/usr/sbin/visudo -cf %s' + loop: "{{ rhel9cis_sudoers_files.stdout_lines }}" + when: + - rhel9cis_rule_5_3_4 + tags: + - level2-server + - level2-workstation + - patch + - sudo + - rule_5.3.4 + +- name: "5.3.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" + ansible.builtin.replace: + path: "{{ item }}" + regexp: '^([^#].*)!authenticate(.*)' + replace: '\1authenticate\2' + validate: '/usr/sbin/visudo -cf %s' + loop: "{{ rhel9cis_sudoers_files.stdout_lines }}" + when: + - rhel9cis_rule_5_3_5 + tags: + - level1-server + - level1-workstation + - patch + - sudo + - rule_5.3.5 + +- name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly" block: - - name: "5.3.2 | L1 | AUDIT | Select authselect profile | Gather profiles and enabled features" - shell: "authselect current" - args: - warn: no - failed_when: false + - name: "5.3.6 | AUDIT | Ensure sudo authentication timeout is configured correctly | Get files with timeout set" + ansible.builtin.shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort changed_when: false - check_mode: no - register: rhel9cis_5_3_2_profiles + failed_when: false + register: rhel9cis_5_3_6_timeout_files - - 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.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" + ansible.builtin.lineinfile: + path: /etc/sudoers + regexp: 'Defaults timestamp_timeout=' + line: "Defaults timestamp_timeout={{ rhel9cis_sudo_timestamp_timeout }}" + validate: '/usr/sbin/visudo -cf %s' + when: rhel9cis_5_3_6_timeout_files.stdout | length == 0 - - 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 + - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" + ansible.builtin.replace: + path: "{{ item }}" + regexp: 'timestamp_timeout=(\d+)' + replace: "timestamp_timeout={{ rhel9cis_sudo_timestamp_timeout }}" + validate: '/usr/sbin/visudo -cf %s' + loop: "{{ rhel9cis_5_3_6_timeout_files.stdout_lines }}" + when: rhel9cis_5_3_6_timeout_files.stdout | length > 0 when: - - rhel9cis_rule_5_3_2 + - rhel9cis_rule_5_3_6 tags: - level1-server - level1-workstation - patch - - authselect - - rule_5.3.2 + - sudo + - rule_5.3.6 -- name: "5.3.3 | L1 | PATCH | Ensure authselect includes with-faillock" +- name: "5.3.7 | PATCH | Ensure access to the su command is restricted" 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.7 | PATCH | Ensure access to the su command is restricted | Ensure sugroup exists" + ansible.builtin.group: + name: "{{ rhel9cis_sugroup }}" + state: present + register: rhel9cis_5_3_7_sugroup + + - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | remove users from group" + ansible.builtin.lineinfile: + path: /etc/group + regexp: '^{{ rhel9cis_sugroup }}(:.:.*:).*$' + line: '{{ rhel9cis_sugroup }}\g<1>' + backrefs: true - - 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 + - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" + ansible.builtin.lineinfile: + path: /etc/pam.d/su + regexp: '^(#)?auth\s+required\s+pam_wheel\.so' + line: 'auth required pam_wheel.so use_uid group={{ rhel9cis_sugroup }}' when: - - rhel9cis_rule_5_3_3 + - rhel9cis_rule_5_3_7 tags: - level1-server - level1-workstation - patch - - authselect - - rule_5.3.3 + - sudo + - rule_5.3.7 diff --git a/tasks/section_5/cis_5.4.x.yml b/tasks/section_5/cis_5.4.x.yml index 3870b8fb..cb370246 100644 --- a/tasks/section_5/cis_5.4.x.yml +++ b/tasks/section_5/cis_5.4.x.yml @@ -1,131 +1,85 @@ --- -- 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" +- name: "5.4.1 | PATCH | Ensure custom authselect profile is used" 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 | AUDIT | Ensure custom authselect profile is used | Gather profiles" + ansible.builtin.shell: 'authselect current | grep "Profile ID: custom/"' + failed_when: false + changed_when: false + check_mode: false + register: rhel9cis_5_4_1_profiles - - name: | - "5.4.1 | 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.1 | AUDIT | Ensure custom authselect profile is used | Show profiles" + ansible.builtin.debug: + msg: + - "Below are the current custom profiles" + - "{{ rhel9cis_5_4_1_profiles.stdout_lines }}" - - 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.1 | PATCH | Ensure custom authselect profile is used | Create custom profiles" + ansible.builtin.shell: authselect create-profile {{ rhel9cis_authselect['custom_profile_name'] }} -b {{ rhel9cis_authselect['default_file_to_copy'] }} + when: rhel9cis_authselect_custom_profile_create + when: + - rhel9cis_rule_5_4_1 + tags: + - level1-server + - level1-workstation + - manual + - patch + - authselect + - rule_5.4.1 - - 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.2 | PATCH | Ensure authselect includes with-faillock | with auth select profile" + block: + - name: "5.4.2 | AUDIT | Ensure authselect includes with-faillock | Gather profiles and enabled features" + ansible.builtin.shell: "authselect current | grep with-faillock" + failed_when: false + changed_when: false + check_mode: false + register: rhel9cis_5_4_2_profiles_faillock - - name: | - "5.4.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.2 | AUDIT | Ensure authselect includes with-faillock | Show profiles" + ansible.builtin.debug: + msg: + - "Below are the current custom profiles" + - "{{ rhel9cis_5_4_2_profiles_faillock.stdout_lines }}" - - name: "5.4.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 + - name: "5.4.2 | PATCH | Ensure authselect includes with-faillock | Create custom profiles" + ansible.builtin.shell: "authselect select custom/{{ rhel9cis_authselect['custom_profile_name'] }} with-faillock" + when: rhel9cis_authselect_custom_profile_select - # 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.2 | PATCH | Ensure authselect includes with-faillock | not auth select profile" + ansible.builtin.lineinfile: + path: "/etc/pam.d/password-auth" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before }}" + loop: + - { 'regexp': '^auth\s+required\s+pam_faillock.so preauth silent deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so preauth silent deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+sufficient\s+pam_unix.so try_first_pass'} + - { 'regexp': '^auth\s+required\s+pam_faillock.so authfail deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so authfail deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+required\s+pam_deny.so'} + - { 'regexp': '^account\s+required\s+pam_faillock.so', 'line': 'account required pam_faillock.so', 'before':'^account required pam_unix.so'} + when: + - rhel9cis_add_faillock_without_authselect + - rhel9cis_5_4_2_risks == 'ACCEPT' - - name: | - "5.4.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" + - name: 5.4.2 | PATCH | Ensure authselect includes with-faillock | not auth select profile" + ansible.builtin.lineinfile: + path: "/etc/pam.d/system-auth" + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before | default(omit)}}" + insertafter: "{{ item.after | default(omit)}}" + loop: + - { 'regexp': '^auth\s+required\s+pam_faillock.so preauth silent deny=.*unlock_time=.*', 'line':'auth required pam_faillock.so preauth silent deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+sufficient\s+pam_unix.so try_first_pass'} + - { 'regexp': '^auth\s+required\s+pam_faillock.so authfail deny=.*unlock_time=.*', 'line': 'auth required pam_faillock.so authfail deny={{ rhel9cis_pam_faillock.deny }} unlock_time={{ rhel9cis_pam_faillock.unlock_time }}', 'before':'^auth\s+required\s+pam_deny.so'} + - { 'regexp': '^account\s+required\s+pam_faillock.so', 'line': 'account required pam_faillock.so', 'before':'^account required pam_unix.so'} + when: + - rhel9cis_add_faillock_without_authselect + - rhel9cis_5_4_2_risks == 'ACCEPT' when: - - rhel9cis_rule_5_4_1 or - rhel9cis_rule_5_4_2 or - rhel9cis_rule_5_4_3 or - rhel9cis_rule_5_4_4 + - rhel9cis_rule_5_4_2 tags: - level1-server - level1-workstation - patch - - rule_5.4.1 + - authselect - 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 deleted file mode 100644 index 453f31b7..00000000 --- a/tasks/section_5/cis_5.5.1.x.yml +++ /dev/null @@ -1,117 +0,0 @@ ---- - -- 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 index 856e6b44..13ac418b 100644 --- a/tasks/section_5/cis_5.5.x.yml +++ b/tasks/section_5/cis_5.5.x.yml @@ -1,96 +1,98 @@ --- -- name: "5.5.2 | L1 | PATCH | Ensure system accounts are secured" +- name: "5.5.1 | PATCH | Ensure password creation requirements are configured" 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.1 | PATCH | Ensure password creation requirements are configured | Set pwquality config settings" + ansible.builtin.lineinfile: + path: /etc/security/pwquality.conf + regexp: ^{{ item.name }} + line: "{{ item.name }} = {{ item.value }}" + loop: + - { name: minlen, value: "{{ rhel9cis_pam_password.minlen }}" } + - { name: minclass, value: "{{ rhel9cis_pam_password.minclass }}" } - - 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" + - name: "5.5.1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings" + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + regexp: '^password\s*requisite\s*pam_pwquality.so' + line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce_for_root retry=3" + insertbefore: '^#?password ?' + + - name: "5.5.1 | PATCH | Ensure password creation requirements are configured | Set system-auth retry settings" + ansible.builtin.lineinfile: + path: /etc/pam.d/password-auth + regexp: '^password\s*requisite\s*pam_pwquality.so' + line: "password requisite pam_pwquality.so try_first_pass local_users_only enforce_for_root retry=3" + insertbefore: '^#?password ?' when: - - rhel9cis_rule_5_5_2 + - rhel9cis_rule_5_5_1 tags: - level1-server - level1-workstation - patch - - rule_5.5.2 + - rule_5.5.1 -- 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') }}" } +- name: "5.5.2 | PATCH | Ensure lockout for failed password attempts is configured" + ansible.builtin.lineinfile: + path: /etc/security/faillock.conf + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + loop: + - { regexp: '^\s*deny\s*=\s*[1-5]\b', line: 'deny = 5' } + - { regexp: '^\s*unlock_time\s*=\s*(0|9[0-9][0-9]|[1-9][0-9][0-9][0-9]+)\b', line: 'unlock_time = 900' } when: - - rhel9cis_rule_5_5_3 - tags: - - level1-server - - level1-workstation - - patch - - rule_5.5.3 + - rhel9cis_rule_5_5_2 -- 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 +- name: "5.5.3 | PATCH | Ensure password reuse is limited" + block: + - name: "5.5.3 | PATCH | Ensure password reuse is limited | pwquality" + ansible.builtin.lineinfile: + path: /etc/pam.d/system-auth + line: "password requisite pam_pwhistory.so try_first_pass local_users_only enforce_for_root retry=3 remember={{ rhel9cis_pam_faillock.remember }}" + insertafter: '^password\s*requisite\s*pam_pwquality.so' + + - name: "5.5.3 | PATCH | Ensure password reuse is limited | pam_unix" + ansible.builtin.replace: + path: /etc/pam.d/system-auth + regexp: '^password\s*(sufficient|requisite|sufficient)\s*pam_unix.so.*$' + replace: 'password requisite pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' when: - - rhel9cis_rule_5_5_4 + - rhel9cis_rule_5_5_3 tags: - level1-server - level1-workstation - patch - - rule_5.5.4 + - rule_5.5.3 -- name: "5.5.5 | L1 | PATCH | Ensure default user umask is 027 or more restrictive" +- name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 or yescrypt" 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.4 | PATCH | Ensure password hashing algorithm is SHA-512 | libuser.conf" + ansible.builtin.replace: + path: /etc/libuser.conf + regexp: '^crypt_style\s*=\s*.*$' + replace: 'crypt_style = sha512' - - 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' + - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | login.defs" + ansible.builtin.replace: + path: /etc/login.defs + regexp: '^ENCRYPT_METHOD.*' + replace: 'ENCRYPT_METHOD SHA512' + + - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | password-auth" + ansible.builtin.replace: + path: /etc/pam.d/password-auth + regexp: '^password\s*sufficient\s*pam_unix.so.*$' + replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' + + - name: "5.5.4 | PATCH | Ensure password hashing algorithm is SHA-512 | system-auth" + ansible.builtin.replace: + path: /etc/pam.d/system-auth + regexp: '^password\s*sufficient\s*pam_unix.so.*$' + replace: 'password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok remember={{ rhel9cis_pam_faillock.remember }}' when: - - rhel9cis_rule_5_5_5 + - rhel9cis_rule_5_5_4 tags: - level1-server - level1-workstation - patch - - rule_5.5.5 + - rule_5.5.4 diff --git a/tasks/section_5/cis_5.6.1.x.yml b/tasks/section_5/cis_5.6.1.x.yml new file mode 100644 index 00000000..141c0136 --- /dev/null +++ b/tasks/section_5/cis_5.6.1.x.yml @@ -0,0 +1,119 @@ +--- + +- name: "5.6.1.1 | PATCH | Ensure password expiration is 365 days or less" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MAX_DAYS' + line: "PASS_MAX_DAYS {{ rhel9cis_pass['max_days'] }}" + when: + - rhel9cis_rule_5_6_1_1 + tags: + - level1-server + - level1-workstation + - patch + - password + - rule_5.5.1.1 + +- name: "5.6.1.2 | PATCH | Ensure minimum days between password changes is 7 or more" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_MIN_DAYS' + line: "PASS_MIN_DAYS {{ rhel9cis_pass['min_days'] }}" + when: + - rhel9cis_rule_5_6_1_2 + tags: + - level1-server + - level1-workstation + - patch + - password + - rule_5.6.1.2 + +- name: "5.6.1.3 | PATCH | Ensure password expiration warning days is 7 or more" + ansible.builtin.lineinfile: + path: /etc/login.defs + regexp: '^PASS_WARN_AGE' + line: "PASS_WARN_AGE {{ rhel9cis_pass['warn_age'] }}" + when: + - rhel9cis_rule_5_6_1_3 + tags: + - level1-server + - level1-workstation + - patch + - password + - rule_5.5.1.3 + +- name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less" + block: + - name: "5.6.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | Check current settings" + ansible.builtin.shell: useradd -D | grep INACTIVE={{ rhel9cis_inactivelock.lock_days }} | cut -f2 -d= + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_5_6_1_4_inactive_settings + + - name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less | Set default inactive setting" + ansible.builtin.shell: useradd -D -f {{ rhel9cis_inactivelock.lock_days }} + when: rhel9cis_5_6_1_4_inactive_settings.stdout | length == 0 + + - name: "5.6.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | Getting user list" + ansible.builtin.shell: "awk -F: '/^[^#:]+:[^\\!\\*:]*:[^:]*:[^:]*:[^:]*:[^:]*:(\\s*|-1|3[1-9]|[4-9][0-9]|[1-9][0-9][0-9]+):[^:]*:[^:]*\\s*$/ {print $1}' /etc/shadow" + changed_when: false + check_mode: false + register: rhel9cis_5_6_1_4_user_list + + - name: "5.6.1.4 | PATCH | Ensure inactive password lock is 30 days or less | Apply Inactive setting to existing accounts" + ansible.builtin.shell: chage --inactive {{ rhel9cis_inactivelock.lock_days }} "{{ item }}" + loop: "{{ rhel9cis_5_6_1_4_user_list.stdout_lines }}" + when: + - rhel9cis_rule_5_6_1_4 + tags: + - level1-server + - level1-workstation + - patch + - password + - rule_5.6.1.4 + +- name: "5.6.1.5 | PATCH | Ensure all users last password change date is in the past" + block: + - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Get current date in Unix Time" + ansible.builtin.shell: echo $(($(date --utc --date "$1" +%s)/86400)) + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_5_6_1_5_currentut + + - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Get list of users with last changed pw date in the future" + ansible.builtin.shell: "cat /etc/shadow | awk -F: '{if($3>{{ rhel9cis_5_6_1_5_currentut.stdout }})print$1}'" + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_5_6_1_5_user_list + + - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | Alert on accounts with pw change in the future" + ansible.builtin.debug: + msg: "Warning!! The following accounts have the last PW change date in the future: {{ rhel9cis_5_6_1_5_user_list.stdout_lines }}" + when: + - rhel9cis_5_6_1_5_user_list.stdout | length > 0 + - not rhel9cis_futurepwchgdate_autofix + + - name: "5.6.1.5 | AUDIT | Ensure all users last password change date is in the past | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: + - rhel9cis_5_6_1_5_user_list.stdout | length > 0 + - not rhel9cis_futurepwchgdate_autofix + + - name: "5.6.1.5 | PATCH | Ensure all users last password change date is in the past | Fix accounts with pw change in the future" + ansible.builtin.shell: passwd --expire {{ item }} + when: + - rhel9cis_5_6_1_5_user_list.stdout | length > 0 + - rhel9cis_futurepwchgdate_autofix + loop: "{{ rhel9cis_5_6_1_5_user_list.stdout_lines }}" + vars: + warn_control_id: '5.6.1.5' + when: + - rhel9cis_rule_5_6_1_5 + tags: + - level1-server + - level1-workstation + - patch + - rule_5.5.1.5 diff --git a/tasks/section_5/cis_5.6.x.yml b/tasks/section_5/cis_5.6.x.yml new file mode 100644 index 00000000..56b3d5f1 --- /dev/null +++ b/tasks/section_5/cis_5.6.x.yml @@ -0,0 +1,125 @@ +--- + +- name: "5.6.2 | PATCH | Ensure system accounts are secured" + block: + - name: "5.6.2 | Ensure system accounts are secured | Set nologin" + ansible.builtin.user: + name: "{{ item.id }}" + shell: /usr/sbin/nologin + loop: "{{ rhel9cis_passwd }}" + when: + - item.id != "root" + - item.id != "sync" + - item.id != "shutdown" + - item.id != "halt" + - item.id != "nfsnobody" + - item.gid < min_int_uid | int + - item.shell != " /bin/false" + - item.shell != " /usr/sbin/nologin" + loop_control: + label: "{{ item.id }}" + + - name: "5.6.2 | PATCH | Ensure system accounts are secured | Lock accounts" + ansible.builtin.user: + name: "{{ item.id }}" + password_lock: true + loop: "{{ rhel9cis_passwd }}" + when: + - item.id != "halt" + - item.id != "shutdown" + - item.id != "sync" + - item.id != "root" + - item.id != "nfsnobody" + - item.gid < min_int_uid | int + - item.shell != " /bin/false" + - item.shell != " /usr/sbin/nologin" + loop_control: + label: "{{ item.id }}" + when: + - rhel9cis_rule_5_6_2 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - rule_5.6.2 + +- name: "5.6.3 | PATCH | Ensure default user shell timeout is 900 seconds or less" + ansible.builtin.blockinfile: + path: "{{ item.path }}" + state: "{{ item.state }}" + marker: "# {mark} - CIS benchmark - Ansible-lockdown" + create: true + mode: 0644 + block: | + TMOUT={{ rhel9cis_shell_session_timeout.timeout }} + export TMOUT + readonly TMOUT + loop: + - { path: "{{ rhel9cis_shell_session_timeout.file }}", state: present } + - { path: /etc/profile, state: "{{ (rhel9cis_shell_session_timeout.file == '/etc/profile') | ternary('present', 'absent') }}" } + when: + - rhel9cis_rule_5_6_3 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - rule_5.6.3 + +- name: "5.6.4 | PATCH | Ensure default group for the root account is GID 0" + ansible.builtin.user: + name: root + group: 0 + when: + - rhel9cis_rule_5_6_4 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - rule_5.6.4 + +- name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive" + block: + - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/login.defs pam_umask settings" + ansible.builtin.replace: + path: /etc/login.defs + regexp: "{{ item.regexp }}" + replace: "{{ item.replace }}" + loop: + - { regexp: '(UMASK\s+)0[012][0-6]', replace: '\1 027' } + - { regexp: '(USERGROUPS_ENAB\s+)yes', replace: '\1 no' } + + - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/bashrc" + ansible.builtin.replace: + path: /etc/bashrc + regexp: '^(?i)(\s+UMASK|UMASK)\s0[0-2][0-6]' + replace: '\1 027' + + - name: "5.6.5 | PATCH | Ensure default user umask is 027 or more restrictive | Set umask for /etc/profile" + ansible.builtin.replace: + path: /etc/profile + regexp: '^(?i)(\s+UMASK|UMASK)\s0[0-2][0-6]' + replace: '\1 027' + when: + - rhel9cis_rule_5_6_5 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - rule_5.6.5 + +- name: "5.6.6 | PATCH | Ensure root password is set" + ansible.builtin.debug: + msg: "The root password has been set as per the assert in early stages" + when: + - rhel9cis_rule_5_6_6 + tags: + - level1-server + - level1-workstation + - patch + - accounts + - root + - rule_5.6.6 diff --git a/tasks/section_5/cis_5.6.yml b/tasks/section_5/cis_5.6.yml deleted file mode 100644 index 58eb7e54..00000000 --- a/tasks/section_5/cis_5.6.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- - -# 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 deleted file mode 100644 index 9e7bbec8..00000000 --- a/tasks/section_5/cis_5.7.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- - -- 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 index f290165e..5aed1c18 100644 --- a/tasks/section_5/main.yml +++ b/tasks/section_5/main.yml @@ -1,27 +1,26 @@ --- +# Access, Authentication, and Authorization + - name: "SECTION | 5.1 | Configure time-based job schedulers" - include: cis_5.1.x.yml + ansible.builtin.import_tasks: 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 + ansible.builtin.import_tasks: cis_5.2.x.yml when: - - rhel9cis_use_authconfig + - "'openssh-server' in ansible_facts.packages" -- name: "SECTION | 5.4 | Configure PAM " - include: cis_5.4.x.yml +- name: "SECTION | 5.3 | Configure privilege escalation" + ansible.builtin.import_tasks: cis_5.3.x.yml -- name: "SECTION | 5.5.1.x | Passwords and Accounts" - include: cis_5.5.1.x.yml +- name: "SECTION | 5.4 | Configure authselect" + ansible.builtin.import_tasks: cis_5.4.x.yml -- name: "SECTION | 5.5.x | System Accounts and User Settings" - include: cis_5.5.x.yml +- name: "SECTION | 5.5 | Configure PAM " + ansible.builtin.import_tasks: cis_5.5.x.yml -- name: "SECTION | 5.6 | Root Login" - include: cis_5.6.yml +- name: "SECTION | 5.6.1.x | Shadow Password Suite Parameters" + ansible.builtin.import_tasks: cis_5.6.1.x.yml -- name: Section | 5.7 | su Command Restriction - include: cis_5.7.yml +- name: "SECTION | 5.6.x | Misc. User Account Settings" + ansible.builtin.import_tasks: cis_5.6.x.yml diff --git a/tasks/section_6/cis_6.1.x.yml b/tasks/section_6/cis_6.1.x.yml index ad162e34..298492d9 100644 --- a/tasks/section_6/cis_6.1.x.yml +++ b/tasks/section_6/cis_6.1.x.yml @@ -1,44 +1,23 @@ --- -- 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 +- name: "6.1.1 | PATCH | Ensure permissions on /etc/passwd are configured" + ansible.builtin.file: + path: /etc/passwd + owner: root + group: root + mode: 0644 when: - rhel9cis_rule_6_1_1 tags: - - level2-server - - level2-workstation - - audit + - level1-server + - level1-workstation + - patch + - permissions - rule_6.1.1 -- name: "6.1.2 | L1 | PATCH | Ensure permissions on /etc/passwd are configured" - file: - dest: /etc/passwd +- name: "6.1.2 | PATCH | Ensure permissions on /etc/passwd- are configured" + ansible.builtin.file: + path: /etc/passwd- owner: root group: root mode: 0644 @@ -48,11 +27,12 @@ - level1-server - level1-workstation - patch + - permissions - rule_6.1.2 -- name: "6.1.3 | L1 | PATCH | Ensure permissions on /etc/passwd- are configured" - file: - dest: /etc/passwd- +- name: "6.1.3 | PATCH | Ensure permissions on /etc/group are configured" + ansible.builtin.file: + path: /etc/group- owner: root group: root mode: 0644 @@ -62,25 +42,27 @@ - level1-server - level1-workstation - patch + - permissions - rule_6.1.3 -- name: "6.1.4 | L1 | PATCH | Ensure permissions on /etc/shadow are configured" - file: - dest: /etc/shadow +- name: "6.1.4 | PATCH | Ensure permissions on /etc/group- are configured" + ansible.builtin.file: + path: /etc/group- owner: root group: root - mode: 0000 + mode: 0644 when: - rhel9cis_rule_6_1_4 tags: - level1-server - level1-workstation - patch + - permissionss - rule_6.1.4 -- name: "6.1.5 | L1 | PATCH | Ensure permissions on /etc/shadow- are configured" - file: - dest: /etc/shadow- +- name: "6.1.5 | PATCH | Ensure permissions on /etc/shadow are configured" + ansible.builtin.file: + path: /etc/shadow owner: root group: root mode: 0000 @@ -90,11 +72,12 @@ - level1-server - level1-workstation - patch + - permissions - rule_6.1.5 -- name: "6.1.6 | L1 | PATCH | Ensure permissions on /etc/gshadow are configured" - file: - dest: /etc/gshadow +- name: "6.1.6 | PATCH | Ensure permissions on /etc/shadow- are configured" + ansible.builtin.file: + path: /etc/shadow- owner: root group: root mode: 0000 @@ -104,11 +87,12 @@ - level1-server - level1-workstation - patch + - permissions - rule_6.1.6 -- name: "6.1.7 | L1 | PATCH | Ensure permissions on /etc/gshadow- are configured" - file: - dest: /etc/gshadow- +- name: "6.1.7 | PATCH | Ensure permissions on /etc/gshadow are configured" + ansible.builtin.file: + path: /etc/gshadow owner: root group: root mode: 0000 @@ -118,82 +102,115 @@ - level1-server - level1-workstation - patch + - permissions - rule_6.1.7 -- name: "6.1.8 | L1 | PATCH | Ensure permissions on /etc/group are configured" - file: - dest: /etc/group- +- name: "6.1.8 | PATCH | Ensure permissions on /etc/gshadow- are configured" + ansible.builtin.file: + path: /etc/gshadow- owner: root group: root - mode: 0644 + mode: 0000 when: - rhel9cis_rule_6_1_8 tags: - level1-server - level1-workstation - patch - - rule_6.1.8 + - permissions + - rule_6.1.10 -- name: "6.1.9 | L1 | PATCH | Ensure permissions on /etc/group- are configured" - file: - dest: /etc/group- - owner: root - group: root - mode: 0644 +- name: "6.1.9 | PATCH | Ensure no world writable files exist" + block: + - name: "6.1.9 | AUDIT | Ensure no world writable files exist | Get list of world-writable files" + ansible.builtin.shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002 + failed_when: false + changed_when: false + register: rhel_09_6_1_9_perms_results + + - name: "6.1.9 | PATCH | Ensure no world writable files exist | Adjust world-writable files if they exist (Configurable)" + ansible.builtin.file: + path: '{{ item }}' + mode: o-w + state: touch + loop: "{{ rhel_09_6_1_9_perms_results.stdout_lines }}" + when: + - rhel_09_6_1_9_perms_results.stdout_lines is defined + - rhel9cis_no_world_write_adjust when: - rhel9cis_rule_6_1_9 tags: - level1-server - level1-workstation - patch + - files + - permissions - rule_6.1.9 -- name: "6.1.10 | L1 | PATCH | Ensure no world writable files exist" +- name: "6.1.10 | AUDIT | Ensure no unowned files or directories 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 + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | Finding all unowned files or directories" + ansible.builtin.shell: find "{{ item.mount }}" -xdev -nouser changed_when: false - register: rhel_09_6_1_10_perms_results + failed_when: false + check_mode: false + register: rhel_09_6_1_10_audit + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" + when: item['device'].startswith('/dev') and not 'bind' in item['options'] - - name: "6.1.10 | 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" + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | Displaying any unowned files or directories" + ansible.builtin.debug: + msg: "Warning !! Manual intervention is required -- missing owner on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_10_audit.results }}" when: - - rhel_09_6_1_10_perms_results.stdout is not defined + - item.stdout_lines is defined + - item.stdout_lines | length > 0 - - 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 }}" + - name: "6.1.10 | AUDIT | Ensure no unowned files or directories exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.10' when: - - rhel_09_6_1_10_perms_results.stdout_lines is defined - - rhel9cis_no_world_write_adjust + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + when: - rhel9cis_rule_6_1_10 tags: - level1-server - level1-workstation - - patch + - audit + - files + - permissions - rule_6.1.10 -- name: "6.1.11 | L1 | AUDIT | Ensure no unowned files or directories exist" +- name: "6.1.11 | AUDIT | Ensure no ungrouped 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 + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | Finding all ungrouped files or directories" + ansible.builtin.shell: find "{{ item.mount }}" -xdev -nogroup check_mode: false failed_when: false changed_when: false - with_items: "{{ ansible_mounts }}" register: rhel_09_6_1_11_audit + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" when: item['device'].startswith('/dev') and not 'bind' in item['options'] - - name: "6.1.11 | 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 }}" + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | Displaying all ungrouped files or directories" + ansible.builtin.debug: + msg: "Warning !! Manual intervention is required -- missing group on items in {{ item.item.mount }}: {{ item.stdout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_11_audit.results }}" + when: + - item.stdout_lines is defined + - item.stdout_lines | length > 0 + + - name: "6.1.11 | AUDIT | Ensure no ungrouped files or directories exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.11' when: - item.stdout_lines is defined - item.stdout_lines | length > 0 @@ -203,55 +220,46 @@ - level1-server - level1-workstation - audit + - files + - permissions - 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 +- name: "6.1.12 | PATCH | Ensure sticky bit is set on all world-writable directories" + ansible.builtin.shell: df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d -perm -0002 2>/dev/null | xargs chmod a+t + changed_when: false + failed_when: false when: - rhel9cis_rule_6_1_12 tags: - level1-server - level1-workstation - patch - - rule_6.1.12 + - stickybits + - permissons + - rule_1.1.21 -- name: "6.1.13 | L1 | AUDIT | Audit SUID executables" +- name: "6.1.13 | 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 + - name: "6.1.13 | AUDIT | Audit SUID executables | Find all SUID executables" + ansible.builtin.shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 failed_when: false changed_when: false register: rhel_09_6_1_13_perms_results - with_items: "{{ ansible_mounts }}" + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" - - 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 + - name: "6.1.13 | AUDIT | Audit SUID executables | Alert SUID executables exist" + ansible.builtin.debug: + msg: "Warning!! Manual intervention is required -- SUID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_13_perms_results.stdout_lines }}" when: - - rhel_09_6_1_13_perms_results.stdout is not defined + - rhel_09_6_1_13_perms_results.stdout is 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 }}" + - name: "6.1.13 | AUDIT | Audit SUID executables | Alert SUID executables exist | warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.13' when: - rhel_09_6_1_13_perms_results.stdout is defined when: @@ -259,30 +267,33 @@ tags: - level1-server - level1-workstation + - manual - audit + - files - rule_6.1.13 -- name: "6.1.14 | L1 | AUDIT | Audit SGID executables" +- name: "6.1.14 | 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 + - name: "6.1.14 | AUDIT | Audit SGID executables | Find all SGID executables" + ansible.builtin.shell: df {{ item.mount }} -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -2000 failed_when: false changed_when: false register: rhel_09_6_1_14_perms_results - with_items: "{{ ansible_mounts }}" + loop: "{{ ansible_mounts }}" + loop_control: + label: "{{ item.mount }}" - - 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 + - name: "6.1.14 | AUDIT | Audit SGID executables | Alert SGID executables exist" + ansible.builtin.debug: + msg: "Manual intervention is required -- SGID set on items in {{ item.item.mount }}: {{ item.stout_lines | join(', ') }}" + loop: "{{ rhel_09_6_1_14_perms_results.stdout_lines }}" when: - - rhel_09_6_1_14_perms_results.stdout is not defined + - rhel_09_6_1_14_perms_results.stdout is 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 }}" + - name: "6.1.14 | AUDIT | Audit SGID executables| warning" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.14' when: - rhel_09_6_1_14_perms_results.stdout is defined when: @@ -290,5 +301,46 @@ tags: - level1-server - level1-workstation - - patch + - manual + - audit + - files - rule_6.1.14 + +- name: "6.1.15 | AUDIT | Audit system file permissions" + block: + - name: "6.1.15 | AUDIT | Audit system file permissions | Audit the packages" + ansible.builtin.shell: rpm -Va --nomtime --nosize --nomd5 --nolinkto + changed_when: false + failed_when: false + register: rhel9cis_6_1_15_packages_rpm + + - name: "6.1.15 | AUDIT | Audit system file permissions | Create list and warning" + block: + - name: "6.1.15 | AUDIT | Audit system file permissions | Add file discrepancy list to system" + ansible.builtin.copy: + dest: "{{ rhel9cis_rpm_audit_file }}" # noqa template-instead-of-copy + content: "{{ rhel9cis_6_1_15_packages_rpm.stdout }}" + owner: root + group: root + mode: 0640 + + - name: "6.1.15 | AUDIT | Audit system file permissions | Message out alert for package descrepancies" + ansible.builtin.debug: + msg: | + "Warning!! You have some package descrepancies issues. + The file list can be found in {{ rhel9cis_rpm_audit_file }}" + + - name: "6.1.15 | AUDIT | Audit system file permissions | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.1.15' + when: rhel9cis_6_1_15_packages_rpm.stdout|length > 0 + when: + - rhel9cis_rule_6_1_15 + tags: + - level2-server + - level2-workstation + - manual + - audit + - permissions + - rule_6.1.15 diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index 1edab263..618cadb9 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -1,519 +1,452 @@ --- -- 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 }}" +- name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords" + block: + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | discover" + ansible.builtin.shell: awk -F':' '($2 != "x" ) { print $1 " is not set to shadowed passwords "}' /etc/passwd + changed_when: false + register: shadow_passwd + + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | Output" + ansible.builtin.debug: + msg: | + - "Warning!! Below are the accounts that do not have shadowed passwords set" + - "{{ shadow_passwd.stdout_line }}" + when: shadow_passwd.stdout | length > 0 + + - name: "6.2.1 | AUDIT | Ensure accounts in /etc/passwd use shadowed passwords | warning fact" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.1' + when: shadow_passwd.stdout | length >= 1 + when: - - empty_password_accounts.rc - rhel9cis_rule_6_2_1 tags: - level1-server - level1-workstation - patch + - accounts - rule_6.2.1 -- name: "6.2.2 | L1 | PATCH | Ensure no legacy '+' entries exist in /etc/passwd" - command: sed -i '/^+/ d' /etc/passwd +- name: "6.2.2 | PATCH | Ensure password fields are not empty" + ansible.builtin.shell: passwd -l {{ item }} changed_when: false failed_when: false + loop: "{{ empty_password_accounts.stdout_lines }}" when: + - empty_password_accounts.rc - rhel9cis_rule_6_2_2 tags: - level1-server - level1-workstation - patch + - accounts - rule_6.2.2 - - skip_ansible_lint -- name: "6.2.3 | L1 | PATCH | Ensure root PATH Integrity" +- name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group" 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 }}" + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Check /etc/passwd entries" + ansible.builtin.shell: pwck -r | grep 'no group' | awk '{ gsub("[:\47]",""); print $2}' + changed_when: false + failed_when: false + check_mode: false + register: rhel9cis_6_2_3_passwd_gid_check + + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | Print warning about users with invalid GIDs missing GID entries in /etc/group" + ansible.builtin.debug: + msg: "Warning!! The following users have non-existent GIDs (Groups): {{ rhel9cis_6_2_3_passwd_gid_check.stdout_lines | join (', ') }}" + when: rhel9cis_6_2_3_passwd_gid_check.stdout | length >= 1 + + - name: "6.2.3 | AUDIT | Ensure all groups in /etc/passwd exist in /etc/group | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.3' + when: rhel9cis_6_2_3_passwd_gid_check.stdout | length >= 1 when: - rhel9cis_rule_6_2_3 tags: - level1-server - level1-workstation - - patch - - rule_6.2.3 + - audit + - accounts + - groups + - rule_6.2.2 -- 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 +- name: "6.2.4 | AUDIT Ensure no duplicate UIDs exist" + block: + - name: "6.2.4 | AUDIT | Ensure no duplicate UIDs exist | Check for duplicate UIDs" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($3 in uid) print $1 ; else uid[$3]}' /etc/passwd" + changed_when: false + failed_when: false + register: rhel9cis_6_2_4_user_uid_check + + - name: "6.2.4 | AUDIT | Ensure no duplicate UIDs exist | Print warning about users with duplicate UIDs" + ansible.builtin.debug: + msg: "Warning!! The following users have UIDs that are duplicates: {{ rhel9cis_6_2_4_user_uid_check.stdout_lines }}" + when: rhel9cis_6_2_4_user_uid_check.stdout | length >= 1 + + - name: "6.2.4 | AUDIT| Ensure no duplicate UIDs exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + when: rhel9cis_6_2_4_user_uid_check.stdout | length >= 1 + vars: + warn_control_id: '6.2.4' when: - rhel9cis_rule_6_2_4 tags: - level1-server - level1-workstation - - patch + - audit + - accounts + - users - 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 +- name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist" + block: + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | Check for duplicate GIDs" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($3 in users) print $1 ; else users[$3]}' /etc/group" + changed_when: false + failed_when: false + register: rhel9cis_6_2_5_user_user_check + + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | Print warning about users with duplicate GIDs" + ansible.builtin.debug: + msg: "Warning!! The following groups have duplicate GIDs: {{ rhel9cis_6_2_5_user_user_check.stdout_lines }}" + when: rhel9cis_6_2_5_user_user_check.stdout | length >= 1 + + - name: "6.2.5 | AUDIT | Ensure no duplicate GIDs exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.5' + when: rhel9cis_6_2_5_user_user_check.stdout_lines | length >= 1 + when: - rhel9cis_rule_6_2_5 tags: - level1-server - level1-workstation - - patch + - audit + - accounts + - groups - 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 }}" +- name: "6.2.6 | AUDIT | Ensure no duplicate user names exist" + block: + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | Check for duplicate User Names" + ansible.builtin.shell: "pwck -r | awk -F: '{if ($1 in users) print $1 ; else users[$1]}' /etc/passwd" + changed_when: false + failed_when: false + register: rhel9cis_6_2_6_user_username_check + + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | Print warning about users with duplicate User Names" + ansible.builtin.debug: + msg: "Warning!! The following user names are duplicates: {{ rhel9cis_6_2_6_user_username_check.stdout_lines }}" + when: rhel9cis_6_2_6_user_username_check.stdout | length >= 1 + + - name: "6.2.6 | AUDIT | Ensure no duplicate user names exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.6' + when: rhel9cis_6_2_6_user_username_check.stdout | length >= 1 when: - - uid_zero_accounts_except_root.rc - rhel9cis_rule_6_2_6 tags: - level1-server - level1-workstation - - patch + - audit + - accounts + - users - rule_6.2.6 -- name: "6.2.7 | L1 | PATCH | Ensure users' home directories permissions are 750 or more restrictive" +- name: "6.2.7 | AUDIT | Ensure no duplicate group names exist" 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 + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | Check for duplicate group names" + ansible.builtin.shell: 'getent passwd | cut -d: -f1 | sort -n | uniq -d' + changed_when: false + failed_when: false 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' + register: rhel9cis_6_2_7_group_group_check + + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | Print warning about users with duplicate group names" + ansible.builtin.debug: + msg: "Warning!! The following group names are duplicates: {{ rhel9cis_6_2_7_group_group_check.stdout_lines }}" + when: rhel9cis_6_2_7_group_group_check.stdout is not defined + + - name: "6.2.7 | AUDIT | Ensure no duplicate group names exist | warning count" + ansible.builtin.import_tasks: warning_facts.yml + vars: + warn_control_id: '6.2.7' + when: rhel9cis_6_2_7_group_group_check.stdout is not defined when: - rhel9cis_rule_6_2_7 tags: - level1-server - level1-workstation - - patch + - audit + - accounts + - groups - 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 }}" +- name: "6.2.8 | PATCH | Ensure root PATH Integrity" + block: + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Get root paths" + ansible.builtin.shell: sudo -Hiu root env | grep '^PATH' | cut -d= -f2 + changed_when: false + register: rhel9cis_6_2_8_root_paths + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Get root paths" + ansible.builtin.shell: sudo -Hiu root env | grep '^PATH' | cut -d= -f2 | tr ":" "\n" + changed_when: false + register: rhel9cis_6_2_8_root_paths_split + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Set fact" + ansible.builtin.set_fact: + root_paths: "{{ rhel9cis_6_2_8_root_paths.stdout }}" + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for empty dirs" + ansible.builtin.shell: 'echo {{ root_paths }} | grep -q "::" && echo "roots path contains a empty directory (::)"' + changed_when: false + failed_when: root_path_empty_dir.rc not in [ 0, 1 ] + register: root_path_empty_dir + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for trailing ':'" + ansible.builtin.shell: '{{ root_paths }} | cut -d= -f2 | grep -q ":$" && echo "roots path contains a trailing (:)"' + changed_when: false + failed_when: root_path_trailing_colon.rc not in [ 0, 1 ] + register: root_path_trailing_colon + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for owner and permissions" + block: + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Check for owner and permissions" + ansible.builtin.stat: + path: "{{ item }}" + register: root_path_perms + loop: "{{ rhel9cis_6_2_8_root_paths_split.stdout_lines }}" + + - name: "6.2.8 | AUDIT | Ensure root PATH Integrity | Set permissions" + ansible.builtin.file: + path: "{{ item.stat.path }}" + state: directory + owner: root + group: root + mode: "0755" + follow: false + loop: "{{ root_path_perms.results }}" + loop_control: + label: "{{ item }}" + when: + - item.stat.exists + - item.stat.isdir + - item.stat.pw_name != 'root' or item.stat.gr_name != 'root' or item.stat.woth or item.stat.wgrp when: - - 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 + - paths - 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 +- name: "6.2.9 | PATCH | Ensure root is the only UID 0 account" + ansible.builtin.shell: passwd -l {{ item }} + changed_when: false + failed_when: false + loop: "{{ rhel9cis_uid_zero_accounts_except_root.stdout_lines }}" when: + - rhel9cis_uid_zero_accounts_except_root.rc - rhel9cis_rule_6_2_9 tags: - level1-server - level1-workstation - patch + - accounts + - users - 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 }}" +- name: "6.2.10 | PATCH | Ensure local interactive user home directories exist" + block: + - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Create dir if absent" + ansible.builtin.file: + path: "{{ item.dir }}" + state: directory + owner: "{{ item.id }}" + group: "{{ item.gid }}" + register: rhel_09_6_2_10_home_dir + loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" + loop_control: + label: "{{ item.id }}" + + # set default ACLs so the homedir has an effective umask of 0027 + - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Set group ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: group + permissions: rx + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container + + - name: "6.2.10 | PATCH | Ensure local interactive user home directories exist | Set other ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: other + permissions: 0 + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container when: - rhel9cis_rule_6_2_10 tags: - level1-server - level1-workstation - patch + - users - 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 }}" +- name: "6.2.11 | PATCH | Ensure local interactive users own their home directories" + ansible.builtin.file: + path: "{{ item.dir }}" + owner: "{{ item.id }}" + state: directory + loop: "{{ rhel9cis_passwd | selectattr('uid', '>=', min_int_uid | int ) | selectattr('uid', '<=', max_int_uid | int ) | list }}" + loop_control: + label: "{{ item.id }}" when: + - item.uid >= min_int_uid | int + - item.id != 'nobody' + - (item.id != 'tss' and item.dir != '/dev/null') + - item.shell != '/sbin/nologin' - rhel9cis_rule_6_2_11 tags: - level1-server - level1-workstation - patch + - users - 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 +- name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive" + block: + - name: "6.2.12 | AUDIT | Ensure local interactive user home directories are mode 750 or more restrictive | get stat" + ansible.builtin.stat: + path: "{{ item }}" + register: rhel_09_6_2_12_home_dir_perms + loop: "{{ interactive_users_home.stdout_lines }}" + + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | amend if needed" + ansible.builtin.file: + path: "{{ item.stat.path }}" + state: directory + mode: "0750" + loop: "{{ rhel_09_6_2_12_home_dir_perms.results }}" + loop_control: + label: "{{ item }}" + when: + - item.stat.mode > '0750' + + # set default ACLs so the homedir has an effective umask of 0027 + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | Set group ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: group + permissions: rx + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container + + - name: "6.2.12 | PATCH | Ensure local interactive user home directories are mode 750 or more restrictive | Set other ACL" + ansible.posix.acl: + path: "{{ item }}" + default: true + etype: other + permissions: 0 + state: present + loop: "{{ interactive_users_home.stdout_lines }}" + when: not system_is_container when: - rhel9cis_rule_6_2_12 tags: - level1-server - level1-workstation - patch + - users + - permissions - rule_6.2.12 -- name: "6.2.13 | L1 | PATCH | Ensure no users have .rhosts files" - file: +- name: "6.2.13 | PATCH | Ensure no local interactive user has .netrc files" + ansible.builtin.file: + path: "{{ item }}/.netrc" state: absent - dest: "~{{ item }}/.rhosts" - with_items: "{{ users.stdout_lines }}" + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_13 tags: - level1-server - level1-workstation - patch + - users + - permissions - 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 +- name: "6.2.14 | PATCH | Ensure no local interactive user has .forward files" + ansible.builtin.file: + path: "{{ item }}/.forward" + state: absent + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_14 tags: - level1-server - level1-workstation - - audit + - patch + - users + - files - 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 +- name: "6.2.15 | PATCH | Ensure no local interactive user has .rhosts files" + ansible.builtin.file: + path: "~{{ item }}/.rhosts" + state: absent + loop: "{{ interactive_users_home.stdout_lines }}" when: - rhel9cis_rule_6_2_15 tags: - level1-server - level1-workstation - patch + - users + - files - rule_6.2.15 -- name: "6.2.16 | L1 | AUDIT | Ensure no duplicate GIDs exist" +- name: "6.2.16 | PATCH | Ensure local interactive user dot files are not group or world writable" 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 }}" + - name: "6.2.16 | AUDIT | Ensure local interactive user dot files are not group or world writable | Check for files" + ansible.builtin.find: + path: /home + depth: 3 + patterns: ".*" + hidden: true + recurse: true + file_type: file + register: user_dot_files + + - name: "6.2.16 | AUDIT | Ensure local interactive user dot files are not group or world writable | update permissions" + ansible.builtin.file: + path: "{{ item.path }}" + mode: go-w + follow: "{{ rhel_09_6_2_16_home_follow_symlinks }}" + loop: "{{ user_dot_files.files }}" loop_control: - label: "{{ item.0 }}" + label: "{{ item.path }}" - # 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 + - rhel9cis_rule_6_2_16 tags: - level1-server - level1-workstation - patch - - rule_6.2.20 + - users + - permissions + - rule_6.2.16 diff --git a/tasks/section_6/main.yml b/tasks/section_6/main.yml index bf6943af..35328e5f 100644 --- a/tasks/section_6/main.yml +++ b/tasks/section_6/main.yml @@ -1,7 +1,7 @@ --- - name: "SECTION | 6.1 | System File Permissions" - include: cis_6.1.x.yml + ansible.builtin.import_tasks: cis_6.1.x.yml - name: "SECTION | 6.2 | User and Group Settings" - include: cis_6.2.x.yml + ansible.builtin.import_tasks: cis_6.2.x.yml diff --git a/tasks/warning_facts.yml b/tasks/warning_facts.yml new file mode 100644 index 00000000..6e804873 --- /dev/null +++ b/tasks/warning_facts.yml @@ -0,0 +1,20 @@ +--- + +# This task is used to create variables used in giving a warning summary for manual tasks +# that need attention +# +# The warn_control_list and warn_count vars start life in vars/main.yml but get updated +# as the tasks that have a warning complete +# +# Those two variables are used in the tasks/main.yml to display a list of warnings +# +# warn_control_id is set within the task itself and has the control ID as the value +# +# warn_control_list is the main variable to be used and is a list made up of the warn_control_id’s +# +# warn_count the main variable for the number of warnings and each time a warn_control_id is added +# the count increases by a value of 1 +- name: "{{ warn_control_id }} | AUDIT | Set fact for manual task warning." + ansible.builtin.set_fact: + warn_control_list: "{{ warn_control_list }} [{{ warn_control_id }}]" + warn_count: "{{ warn_count | int + 1 }}" diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 index ec9dac64..8749fc13 100644 --- a/templates/ansible_vars_goss.yml.j2 +++ b/templates/ansible_vars_goss.yml.j2 @@ -1,16 +1,19 @@ + +## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! +## metadata for benchmark + ## metadata for Audit benchmark -benchmark_version: '1.0.1' +benchmark_version: '1.0.0' # Set if genuine RHEL (subscription manager check) not for derivatives e.g. CentOS -is_redhat_os: {% if ansible_distribution == "RedHat" %}true{% else %}false{% endif %} +# If run via script this is discovered and set +host_os_distribution: {{ ansible_distribution | lower }} -rhel9cis_os_distribution: {{ ansible_distribution | lower }} -# timeout for each command to run where set - default = 10seconds/10000ms -timeout_ms: {{ audit_cmd_timeout }} +# timeout for each command to run where set - default = 10seconds/10000ms +timeout_ms: 60000 # Taken from LE rhel9-cis -rhel9cis_notauto: {{ rhel9cis_notauto }} rhel9cis_section1: {{ rhel9cis_section1 }} rhel9cis_section2: {{ rhel9cis_section2 }} rhel9cis_section3: {{ rhel9cis_section3 }} @@ -23,84 +26,115 @@ 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 +# to enable rules that may have IO impact on a system e.g. full filesystem scans or CPU heavy run_heavy_tests: true + +# True is BIOS based system else set to false {% 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 +# 1.1.1 Disable unused filesystems rhel9cis_rule_1_1_1_1: {{ rhel9cis_rule_1_1_1_1 }} rhel9cis_rule_1_1_1_2: {{ rhel9cis_rule_1_1_1_2 }} -rhel9cis_rule_1_1_1_3: {{ rhel9cis_rule_1_1_1_3 }} -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 }} +# 1.1.2 Configure /tmp +rhel9cis_rule_1_1_2_1: {{ rhel9cis_rule_1_1_2_1 }} +rhel9cis_rule_1_1_2_2: {{ rhel9cis_rule_1_1_2_2 }} +rhel9cis_rule_1_1_2_3: {{ rhel9cis_rule_1_1_2_3 }} +rhel9cis_rule_1_1_2_4: {{ rhel9cis_rule_1_1_2_4 }} +# 1.1.3 Configure /var +rhel9cis_rule_1_1_3_1: {{ rhel9cis_rule_1_1_3_1 }} +rhel9cis_rule_1_1_3_2: {{ rhel9cis_rule_1_1_3_2 }} +rhel9cis_rule_1_1_3_3: {{ rhel9cis_rule_1_1_3_3 }} +# 1.1.4 Configure /var/tmp +rhel9cis_rule_1_1_4_1: {{ rhel9cis_rule_1_1_4_1 }} +rhel9cis_rule_1_1_4_2: {{ rhel9cis_rule_1_1_4_2 }} +rhel9cis_rule_1_1_4_3: {{ rhel9cis_rule_1_1_4_3 }} +rhel9cis_rule_1_1_4_4: {{ rhel9cis_rule_1_1_4_4 }} +# 1.1.5 Configure /var/log +rhel9cis_rule_1_1_5_1: {{ rhel9cis_rule_1_1_5_1 }} +rhel9cis_rule_1_1_5_2: {{ rhel9cis_rule_1_1_5_2 }} +rhel9cis_rule_1_1_5_3: {{ rhel9cis_rule_1_1_5_3 }} +rhel9cis_rule_1_1_5_4: {{ rhel9cis_rule_1_1_5_4 }} +# 1.1.6 Configure /var/log/audit +rhel9cis_rule_1_1_6_1: {{ rhel9cis_rule_1_1_6_1 }} +rhel9cis_rule_1_1_6_2: {{ rhel9cis_rule_1_1_6_2 }} +rhel9cis_rule_1_1_6_3: {{ rhel9cis_rule_1_1_6_3 }} +rhel9cis_rule_1_1_6_4: {{ rhel9cis_rule_1_1_6_4 }} +# 1.1.7 Configure /home +rhel9cis_rule_1_1_7_1: {{ rhel9cis_rule_1_1_7_1 }} +rhel9cis_rule_1_1_7_2: {{ rhel9cis_rule_1_1_7_2 }} +rhel9cis_rule_1_1_7_3: {{ rhel9cis_rule_1_1_7_3 }} +# 1.1.8 Configure /dev/shm +rhel9cis_rule_1_1_8_1: {{ rhel9cis_rule_1_1_8_1 }} +rhel9cis_rule_1_1_8_2: {{ rhel9cis_rule_1_1_8_2 }} +rhel9cis_rule_1_1_8_3: {{ rhel9cis_rule_1_1_8_3 }} +rhel9cis_rule_1_1_8_4: {{ rhel9cis_rule_1_1_8_4 }} +# 1.9 usb-storage rhel9cis_rule_1_1_9: {{ rhel9cis_rule_1_1_9 }} -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 +# 1.2 Configure Software Updates +rhel9cis_rule_1_2_1: {{ rhel9cis_rule_1_2_1 }} rhel9cis_rule_1_2_2: {{ rhel9cis_rule_1_2_2 }} rhel9cis_rule_1_2_3: {{ rhel9cis_rule_1_2_3 }} rhel9cis_rule_1_2_4: {{ rhel9cis_rule_1_2_4 }} -rhel9cis_rule_1_2_5: {{ rhel9cis_rule_1_2_5 }} +# 1.3 Filesystem Integrity Checking rhel9cis_rule_1_3_1: {{ rhel9cis_rule_1_3_1 }} rhel9cis_rule_1_3_2: {{ rhel9cis_rule_1_3_2 }} rhel9cis_rule_1_3_3: {{ rhel9cis_rule_1_3_3 }} +# 1.4 Secure Boot Settings rhel9cis_rule_1_4_1: {{ rhel9cis_rule_1_4_1 }} rhel9cis_rule_1_4_2: {{ rhel9cis_rule_1_4_2 }} +# 1.5 Additional Process Hardening rhel9cis_rule_1_5_1: {{ rhel9cis_rule_1_5_1 }} rhel9cis_rule_1_5_2: {{ rhel9cis_rule_1_5_2 }} 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 }} +# 1.6 Mandatory Access Control +rhel9cis_rule_1_6_1_1: {{ rhel9cis_rule_1_6_1_1 }} +rhel9cis_rule_1_6_1_2: {{ rhel9cis_rule_1_6_1_2 }} +rhel9cis_rule_1_6_1_3: {{ rhel9cis_rule_1_6_1_3 }} +rhel9cis_rule_1_6_1_4: {{ rhel9cis_rule_1_6_1_4 }} +rhel9cis_rule_1_6_1_5: {{ rhel9cis_rule_1_6_1_5 }} +rhel9cis_rule_1_6_1_6: {{ rhel9cis_rule_1_6_1_6 }} +rhel9cis_rule_1_6_1_7: {{ rhel9cis_rule_1_6_1_7 }} +rhel9cis_rule_1_6_1_8: {{ rhel9cis_rule_1_6_1_8 }} +# 1.7 Command Line Warning Banners +rhel9cis_rule_1_7_1: {{ rhel9cis_rule_1_7_1 }} +rhel9cis_rule_1_7_2: {{ rhel9cis_rule_1_7_2 }} +rhel9cis_rule_1_7_3: {{ rhel9cis_rule_1_7_3 }} +rhel9cis_rule_1_7_4: {{ rhel9cis_rule_1_7_4 }} +rhel9cis_rule_1_7_5: {{ rhel9cis_rule_1_7_5 }} +rhel9cis_rule_1_7_6: {{ rhel9cis_rule_1_7_6 }} +# 1.8 Gnome Display Manager +rhel9cis_rule_1_8_1: {{ rhel9cis_rule_1_8_1 }} rhel9cis_rule_1_8_2: {{ rhel9cis_rule_1_8_2 }} +rhel9cis_rule_1_8_3: {{ rhel9cis_rule_1_8_3 }} +rhel9cis_rule_1_8_4: {{ rhel9cis_rule_1_8_4 }} +rhel9cis_rule_1_8_5: {{ rhel9cis_rule_1_8_5 }} +rhel9cis_rule_1_8_6: {{ rhel9cis_rule_1_8_6 }} +rhel9cis_rule_1_8_7: {{ rhel9cis_rule_1_8_7 }} +rhel9cis_rule_1_8_8: {{ rhel9cis_rule_1_8_8 }} +rhel9cis_rule_1_8_9: {{ rhel9cis_rule_1_8_9 }} +rhel9cis_rule_1_8_10: {{ rhel9cis_rule_1_8_10 }} +# 1.9 Ensure updates, patches, and additional security software are installed rhel9cis_rule_1_9: {{ rhel9cis_rule_1_9 }} +# Ensure system-wide crypto policy is not legacy rhel9cis_rule_1_10: {{ rhel9cis_rule_1_10 }} -rhel9cis_rule_1_11: {{ rhel9cis_rule_1_11 }} -# section 2 rules + +# section 2 +# Services +# 2.1 Time Synchronization 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_1_2: {{ rhel9cis_rule_2_1_2 }} +# 2.2 Special Purpose Services +rhel9cis_rule_2_2_1: {{ rhel9cis_rule_2_2_1 }} 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 }} @@ -118,74 +152,123 @@ 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 }} +# 2.3 service clients rhel9cis_rule_2_3_1: {{ rhel9cis_rule_2_3_1 }} rhel9cis_rule_2_3_2: {{ rhel9cis_rule_2_3_2 }} rhel9cis_rule_2_3_3: {{ rhel9cis_rule_2_3_3 }} +rhel9cis_rule_2_3_4: {{ rhel9cis_rule_2_3_4 }} +rhel9cis_rule_2_4: true # Section 3 rules +# 3.1 Disable unused network protocols and devices rhel9cis_rule_3_1_1: {{ rhel9cis_rule_3_1_1 }} rhel9cis_rule_3_1_2: {{ rhel9cis_rule_3_1_2 }} +rhel9cis_rule_3_1_3: {{ rhel9cis_rule_3_1_3 }} +# 3.2 Network Parameters (Host Only) rhel9cis_rule_3_2_1: {{ rhel9cis_rule_3_2_1 }} rhel9cis_rule_3_2_2: {{ rhel9cis_rule_3_2_2 }} -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 }} +# 3.3 Network Parameters (Host and Router) 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_3_5: {{ rhel9cis_rule_3_3_5 }} +rhel9cis_rule_3_3_6: {{ rhel9cis_rule_3_3_6 }} +rhel9cis_rule_3_3_7: {{ rhel9cis_rule_3_3_7 }} +rhel9cis_rule_3_3_8: {{ rhel9cis_rule_3_3_8 }} +rhel9cis_rule_3_3_9: {{ rhel9cis_rule_3_3_9 }} +# 3.4.1 Configure firewalld rhel9cis_rule_3_4_1_1: {{ rhel9cis_rule_3_4_1_1 }} +rhel9cis_rule_3_4_1_2: {{ rhel9cis_rule_3_4_1_2 }} + +# 3.4.1 Configure nftables rhel9cis_rule_3_4_2_1: {{ rhel9cis_rule_3_4_2_1 }} rhel9cis_rule_3_4_2_2: {{ rhel9cis_rule_3_4_2_2 }} 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 }} +rhel9cis_rule_3_4_2_7: {{ rhel9cis_rule_3_4_2_7 }} -# Section 4 rules +# Section 4 rules +# 4.1 Configure System Accounting 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 }} + +# 4.1.2 Configure Data retention 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 }} + +# 4.1.3 Configure auditd rules +rhel9cis_rule_4_1_3_1: {{ rhel9cis_rule_4_1_3_1 }} +rhel9cis_rule_4_1_3_2: {{ rhel9cis_rule_4_1_3_2 }} +rhel9cis_rule_4_1_3_3: {{ rhel9cis_rule_4_1_3_3 }} +rhel9cis_rule_4_1_3_4: {{ rhel9cis_rule_4_1_3_4 }} +rhel9cis_rule_4_1_3_5: {{ rhel9cis_rule_4_1_3_5 }} +rhel9cis_rule_4_1_3_6: {{ rhel9cis_rule_4_1_3_6 }} +rhel9cis_rule_4_1_3_7: {{ rhel9cis_rule_4_1_3_7 }} +rhel9cis_rule_4_1_3_8: {{ rhel9cis_rule_4_1_3_8 }} +rhel9cis_rule_4_1_3_9: {{ rhel9cis_rule_4_1_3_9 }} +rhel9cis_rule_4_1_3_10: {{ rhel9cis_rule_4_1_3_10 }} +rhel9cis_rule_4_1_3_11: {{ rhel9cis_rule_4_1_3_11 }} +rhel9cis_rule_4_1_3_12: {{ rhel9cis_rule_4_1_3_12 }} +rhel9cis_rule_4_1_3_13: {{ rhel9cis_rule_4_1_3_13 }} +rhel9cis_rule_4_1_3_14: {{ rhel9cis_rule_4_1_3_14 }} +rhel9cis_rule_4_1_3_15: {{ rhel9cis_rule_4_1_3_15 }} +rhel9cis_rule_4_1_3_16: {{ rhel9cis_rule_4_1_3_16 }} +rhel9cis_rule_4_1_3_17: {{ rhel9cis_rule_4_1_3_17 }} +rhel9cis_rule_4_1_3_18: {{ rhel9cis_rule_4_1_3_18 }} +rhel9cis_rule_4_1_3_19: {{ rhel9cis_rule_4_1_3_19 }} +rhel9cis_rule_4_1_3_20: {{ rhel9cis_rule_4_1_3_20 }} +rhel9cis_rule_4_1_3_21: {{ rhel9cis_rule_4_1_3_21 }} + +# 4.1.4 Configure auditd file Access +rhel9cis_rule_4_1_4_1: {{ rhel9cis_rule_4_1_4_1 }} +rhel9cis_rule_4_1_4_2: {{ rhel9cis_rule_4_1_4_2 }} +rhel9cis_rule_4_1_4_3: {{ rhel9cis_rule_4_1_4_3 }} +rhel9cis_rule_4_1_4_4: {{ rhel9cis_rule_4_1_4_4 }} +rhel9cis_rule_4_1_4_5: {{ rhel9cis_rule_4_1_4_5 }} +rhel9cis_rule_4_1_4_6: {{ rhel9cis_rule_4_1_4_6 }} +rhel9cis_rule_4_1_4_7: {{ rhel9cis_rule_4_1_4_7 }} +rhel9cis_rule_4_1_4_8: {{ rhel9cis_rule_4_1_4_8 }} +rhel9cis_rule_4_1_4_9: {{ rhel9cis_rule_4_1_4_9 }} +rhel9cis_rule_4_1_4_10: {{ rhel9cis_rule_4_1_4_10 }} + +# 4.2.1 Configure rsyslog rhel9cis_rule_4_2_1_1: {{ rhel9cis_rule_4_2_1_1 }} rhel9cis_rule_4_2_1_2: {{ rhel9cis_rule_4_2_1_2 }} +rhel9cis_rule_4_2_1_2: {{ rhel9cis_rule_4_2_1_3 }} 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_1_7: {{ rhel9cis_rule_4_2_1_7 }} + +# 4.2.2 Configure journald +rhel9cis_rule_4_2_2_1_1: {{ rhel9cis_rule_4_2_2_1_1 }} +rhel9cis_rule_4_2_2_1_2: {{ rhel9cis_rule_4_2_2_1_2 }} +rhel9cis_rule_4_2_2_1_3: {{ rhel9cis_rule_4_2_2_1_3 }} +rhel9cis_rule_4_2_2_1_4: {{ rhel9cis_rule_4_2_2_1_4 }} 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_2_4: {{ rhel9cis_rule_4_2_2_4 }} +rhel9cis_rule_4_2_2_5: {{ rhel9cis_rule_4_2_2_5 }} +rhel9cis_rule_4_2_2_6: {{ rhel9cis_rule_4_2_2_6 }} +rhel9cis_rule_4_2_2_7: {{ rhel9cis_rule_4_2_2_7 }} rhel9cis_rule_4_2_3: {{ rhel9cis_rule_4_2_3 }} + +# 4.3 Logrotate rhel9cis_rule_4_3: {{ rhel9cis_rule_4_3 }} + # Section 5 +# Authentication and Authorization +# 5.1 Configure time-based job schedulers 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 }} @@ -194,7 +277,9 @@ 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_1_9: {{ rhel9cis_rule_5_1_9 }} +# 5.2 Configure SSH Server 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 }} @@ -215,31 +300,42 @@ 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 }} - +# 5.3 Configure privilege escalation 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_3_4: {{ rhel9cis_rule_5_3_4 }} +rhel9cis_rule_5_3_5: {{ rhel9cis_rule_5_3_5 }} +rhel9cis_rule_5_3_6: {{ rhel9cis_rule_5_3_6 }} +rhel9cis_rule_5_3_7: {{ rhel9cis_rule_5_3_7 }} + +# 5.4 Configure authselect 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 }} +# 5.5 Configure PAM +rhel9cis_rule_5_5_1: {{ rhel9cis_rule_5_5_1 }} 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 }} +# 5.6 User Accounts and Environment +# 5.6.1 Set Shadow Password Suite Parameters +rhel9cis_rule_5_6_1_1: {{ rhel9cis_rule_5_6_1_1 }} +rhel9cis_rule_5_6_1_2: {{ rhel9cis_rule_5_6_1_2 }} +rhel9cis_rule_5_6_1_3: {{ rhel9cis_rule_5_6_1_3 }} +rhel9cis_rule_5_6_1_4: {{ rhel9cis_rule_5_6_1_4 }} +rhel9cis_rule_5_6_1_5: {{ rhel9cis_rule_5_6_1_5 }} +rhel9cis_rule_5_6_2: {{ rhel9cis_rule_5_6_2 }} +rhel9cis_rule_5_6_3: {{ rhel9cis_rule_5_6_3 }} +rhel9cis_rule_5_6_4: {{ rhel9cis_rule_5_6_4 }} +rhel9cis_rule_5_6_5: {{ rhel9cis_rule_5_6_5 }} +rhel9cis_rule_5_6_6: {{ rhel9cis_rule_5_6_6 }} # Section 6 +# 6 System Maintenance +# 6.1 System File Permissions 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 }} @@ -254,7 +350,9 @@ 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_1_15: {{ rhel9cis_rule_6_1_15 }} +# 6.2 User and Group Settings 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 }} @@ -271,204 +369,143 @@ 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 }} +# Section 1 # 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 }} +# Whether or not to run tasks related to auditing/patching the desktop environment +rhel9cis_gui: {{ rhel9cis_gui }} # Warning Banner Content (issue, issue.net, motd) rhel9cis_warning_banner: {{ rhel9cis_warning_banner }} # End Banner +# aide setup via - cron, timer +rhel9_aide_scan: cron + +# 1.8 Gnome Desktop +rhel9cis_dconf_db_name: {{ rhel9cis_dconf_db_name }} +rhel9cis_screensaver_idle_delay: {{ rhel9cis_screensaver_idle_delay }} # Set max value for idle-delay in seconds (between 1 and 900) +rhel9cis_screensaver_lock_delay: {{ rhel9cis_screensaver_lock_delay }} # Set max value for lock-delay in seconds (between 0 and 5) + +# Section 2 +## 2.2 Special Purposes # Set to 'true' if X Windows is needed in your environment -rhel9cis_xwindows_required: {{ rhel9cis_xwindows_required }} +rhel9cis_xwindows_required: false +### 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_dns_server: {{ rhel9cis_dns_server }} +rhel9cis_dnsmasq_server: {{ rhel9cis_dnsmasq_server }} +rhel9cis_vsftpd_server: {{ rhel9cis_vsftpd_server }} +rhel9cis_tftp_server: {{ rhel9cis_tftp_server }} +rhel9cis_httpd_server: {{ rhel9cis_httpd_server }} +rhel9cis_nginx_server: {{ rhel9cis_nginx_server }} +rhel9cis_dovecot_server: {{ rhel9cis_dovecot_server }} +rhel9cis_imap_server: {{ rhel9cis_imap_server }} +rhel9cis_samba_server: {{ rhel9cis_samba_server }} +rhel9cis_squid_server: {{ rhel9cis_squid_server }} +rhel9cis_snmp_server: {{ rhel9cis_snmp_server }} +rhel9cis_telnet_server: {{ rhel9cis_telnet_server }} +rhel9cis_is_mail_server: {{ rhel9cis_is_mail_server }} -# Whether or not to run tasks related to auditing/patching the desktop environment -rhel9cis_gui: {{ rhel9cis_gui }} +# Note the options +# Packages are used for client services and Server- only remove if you dont use the client service +# +rhel9cis_use_nfs_server: {{ rhel9cis_use_nfs_server }} +rhel9cis_use_nfs_service: {{ rhel9cis_use_nfs_service }} +rhel9cis_use_rpc_server: {{ rhel9cis_use_rpc_server }} +rhel9cis_use_rpc_service: {{ rhel9cis_use_rpc_service }} +rhel9cis_use_rsync_server: {{ rhel9cis_use_rsync_server }} +rhel9cis_use_rsync_service: {{ rhel9cis_use_rsync_service }} + +#### 2.3 Service clients +rhel9cis_telnet_required: {{ rhel9cis_telnet_required }} +rhel9cis_openldap_clients_required: {{ rhel9cis_openldap_clients_required }} +rhel9cis_tftp_client: {{ rhel9cis_tftp_client }} +rhel9cis_ftp_client: {{ rhel9cis_ftp_client }} -# xinetd required -rhel9cis_xinetd_required: {{ rhel9cis_xinetd_required }} +# Section 3 -# IPv6 required +## IPv6 required rhel9cis_ipv6_required: {{ rhel9cis_ipv6_required }} -# System network parameters (host only OR host and router) +## 3.2 System network parameters (host only OR host and router) rhel9cis_is_router: {{ rhel9cis_is_router }} -# Time Synchronization -rhel9cis_time_synchronization: {{ rhel9cis_time_synchronization }} +## Section 3.4 +### Firewall +rhel9cis_firewall: {{ rhel9cis_firewall }} +##### firewalld +rhel9cis_default_zone: {{ rhel9cis_default_zone }} -rhel9cis_varlog_location: {{ rhel9cis_varlog_location }} +#### nftables -rhel9cis_firewall: {{ rhel9cis_firewall }} -#rhel9cis_firewall: iptables -rhel9cis_default_firewall_zone: {{ rhel9cis_default_zone }} -rhel9cis_firewall_interface: -- enp0s3 -- enp0s8 +rhel9cis_nft_tables_autonewtable: {{ rhel9cis_nft_tables_autonewtable }} +rhel9cis_nft_tables_tablename: {{ rhel9cis_nft_tables_tablename }} +rhel9cis_nft_tables_autochaincreate: {{ rhel9cis_nft_tables_autochaincreate }} -rhel9cis_firewall_services: {{ rhel9cis_firewall_services }} +# Section 4 +## Set if host is a logserver +rhel9cis_remote_log_server: {{ rhel9cis_remote_log_server }} -### 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 }} +# Remote logserver settings +rhel9cis_remote_log_host: {{ rhel9cis_remote_log_host }} +rhel9cis_remote_log_port: {{ rhel9cis_remote_log_port }} +rhel9cis_remote_log_protocol: {{ rhel9cis_remote_log_protocol }} +rhel9cis_remote_log_retrycount: {{ rhel9cis_remote_log_retrycount }} +rhel9cis_remote_log_queuesize: {{ rhel9cis_remote_log_queuesize }} ## syslog -rhel9_cis_rsyslog: true +rhel9cis_syslog: {{ rhel9cis_syslog }} + +# Section 5 +# This will allow use of drop in files when CIS adopts them. +rhel9_cis_sshd_config_file: {{ rhel9_cis_sshd_config_file }} -### Section 5 +## 5.2.4 Note the following to understand precedence and layout 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" + - AllowUser + - AllowGroup + - DenyUser + - DenyGroup + +## 5.3.2 & 5.4.2 Enable automation to select custom profile options, using the settings above +rhel9cis_authselect_custom_profile_select: {{ rhel9cis_authselect_custom_profile_select }} +## 5.3.2 Authselect select false if using AD or RHEL ID mgmt +rhel9cis_authselect: + custom_profile_name: {{ rhel9cis_authselect['custom_profile_name'] }} + default_file_to_copy: {{ rhel9cis_authselect['default_file_to_copy'] }} + + +## 5.4.1 Enable automation to create custom profile settings, using the setings above +rhel9cis_authselect_custom_profile_create: {{ rhel9cis_authselect_custom_profile_create }} + +# 5.5.1 ## PAM rhel9cis_pam_password: - minlen: {{ rhel9cis_pam_password.minlen }} - minclass: {{ rhel9cis_pam_password.minclass }} + 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 +## 5.5.3 choose one of below rhel9cis_pwhistory_so: "14" -rhel9cis_unix_so: false rhel9cis_passwd_remember: "5" -# logins.def password settings +## 5.6.x login.defs 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 }} + max_days: {{ rhel9cis_pass['max_days'] }} + min_days: {{ rhel9cis_pass['min_days'] }} + warn_age: {{ rhel9cis_pass['warn_age'] }} -# 5.7 -rhel9cis_sugroup: {{ rhel9cis_sugroup| default('wheel') }} -rhel9cis_sugroup_users: {{ rhel9cis_sugroup_users }} +## 5.3.7 set sugroup if differs from wheel +rhel9cis_sugroup: {{ rhel9cis_sugroup }} diff --git a/templates/audit/98_auditd_exception.rules.j2 b/templates/audit/98_auditd_exception.rules.j2 new file mode 100644 index 00000000..2f76269e --- /dev/null +++ b/templates/audit/98_auditd_exception.rules.j2 @@ -0,0 +1,11 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC +### YOUR CHANGES WILL BE LOST! + +# This file contains users whose actions are not logged by auditd +{% if rhel9cis_allow_auditd_uid_user_exclusions %} +{% for user in rhel9cis_auditd_uid_exclude %} +-a never,user -F uid!={{ user }} -F auid!={{ user }} +{% endfor %} +{% endif %} diff --git a/templates/audit/99_auditd.rules.j2 b/templates/audit/99_auditd.rules.j2 index da5664ba..c48782c8 100644 --- a/templates/audit/99_auditd.rules.j2 +++ b/templates/audit/99_auditd.rules.j2 @@ -1,79 +1,98 @@ -# File created initially via RHEL9 CIS ansible-lockdown remdiation role -{% if rhel9cis_rule_4_1_3 %} +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC +### YOUR CHANGES WILL BE LOST! + +# This template will set all of the auditd configurations via a handler in the role in one task instead of individually +{% if rhel9cis_rule_4_1_3_1 %} -w /etc/sudoers -p wa -k scope --w /etc/sudoers.d/ -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 +{% if rhel9cis_rule_4_1_3_2 %} +-a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation +-a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation {% 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 +{% if rhel9cis_rule_4_1_3_3 %} +-w {{ rhel9cis_sudolog_location }} -p wa -k sudo_log_file {% endif %} -{% if rhel9cis_rule_4_1_7 %} --w /etc/selinux/ -p wa -k MAC-policy --w /usr/share/selinux/ -p wa -k MAC-policy +{% if rhel9cis_rule_4_1_3_4 %} +-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change +-a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time-change +-w /etc/localtime -p wa -k time-change {% 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 +{% if rhel9cis_rule_4_1_3_5 %} +-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale +-a always,exit -F arch=b32 -S sethostname,setdomainname -F key=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 +-w /etc/sysconfig/network-scripts -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 %} +{% if rhel9cis_rule_4_1_3_6 %} +{% for proc in priv_procs.stdout_lines -%} +-a always,exit -F path={{ proc }} -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k privileged +{% endfor %} +{% endif %} +{% if rhel9cis_rule_4_1_3_7 %} +-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ min_int_uid }} -F auid!=unset -k access +-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ min_int_uid }} -F auid!=unset -k access +-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ min_int_uid }} -F auid!=unset -k access +-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ min_int_uid }} -F auid!=unset -k access +{% endif %} +{% if rhel9cis_rule_4_1_3_8 %} -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 +{% if rhel9cis_rule_4_1_3_9 %} +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod {% 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 %} +{% if rhel9cis_rule_4_1_3_10 %} +-a always,exit -F arch=b32 -S mount -F auid>={{ min_int_uid }} -F auid!=unset -k mounts +-a always,exit -F arch=b64 -S mount -F auid>={{ min_int_uid }} -F auid!=unset -k mounts +{% endif %} +{% if rhel9cis_rule_4_1_3_11 %} +-w /var/run/utmp -p wa -k session +-w /var/log/wtmp -p wa -k session +-w /var/log/btmp -p wa -k session +{% endif %} +{% if rhel9cis_rule_4_1_3_12 %} +-w /var/log/lastlog -p wa -k logins +-w /var/run/faillock -p wa -k logins +{% endif %} +{% if rhel9cis_rule_4_1_3_13 %} +-a always,exit -F arch=b64 -S rename,unlink,unlinkat,renameat -F auid>={{ min_int_uid }} -F auid!=unset -F key=delete +-a always,exit -F arch=b32 -S rename,unlink,unlinkat,renameat -F auid>={{ min_int_uid }} -F auid!=unset -F key=delete +{% endif %} +{% if rhel9cis_rule_4_1_3_14 %} +-w /etc/selinux -p wa -k MAC-policy +-w /usr/share/selinux -p wa -k MAC-policy +{% endif %} +{% if rhel9cis_rule_4_1_3_15 %} +-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k perm_chng +{% endif %} +{% if rhel9cis_rule_4_1_3_16 %} +-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k perm_chng {% 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 +{% if rhel9cis_rule_4_1_3_17 %} +-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k priv_cmd {% 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 +{% if rhel9cis_rule_4_1_3_18 %} +-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k usermod {% endif %} -{% if rhel9cis_rule_4_1_16 %} --w /var/log/sudo.log -p wa -k actions +{% if rhel9cis_rule_4_1_3_19 %} +-a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -F auid>={{ min_int_uid }} -F auid!=unset -k kernel_modules +-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k kernel_modules {% endif %} -{% if rhel9cis_rule_4_1_17 %} +{% if rhel9cis_rule_4_1_3_20 %} -e 2 + {% endif %} diff --git a/templates/etc/99-sysctl.conf.j2 b/templates/etc/99-sysctl.conf.j2 deleted file mode 100644 index 61f4dfa4..00000000 --- a/templates/etc/99-sysctl.conf.j2 +++ /dev/null @@ -1,75 +0,0 @@ -# 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/aide.conf.d/crypt_audit_procs.conf.j2 b/templates/etc/aide.conf.d/crypt_audit_procs.conf.j2 new file mode 100644 index 00000000..fb12b297 --- /dev/null +++ b/templates/etc/aide.conf.d/crypt_audit_procs.conf.j2 @@ -0,0 +1,7 @@ +# Audit Tools +/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 diff --git a/templates/chrony.conf.j2 b/templates/etc/chrony.conf.j2 similarity index 98% rename from templates/chrony.conf.j2 rename to templates/etc/chrony.conf.j2 index 6513faac..54c1b6c7 100644 --- a/templates/chrony.conf.j2 +++ b/templates/etc/chrony.conf.j2 @@ -1,3 +1,5 @@ +## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! + # This the default chrony.conf file for the Debian chrony package. After # editing this file use the command 'invoke-rc.d chrony restart' to make # your changes take effect. John Hasler 1998-2008 diff --git a/templates/etc/cron.d/aide.cron.j2 b/templates/etc/cron.d/aide.cron.j2 new file mode 100644 index 00000000..21270eb8 --- /dev/null +++ b/templates/etc/cron.d/aide.cron.j2 @@ -0,0 +1,8 @@ +# Run AIDE integrity check +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC +### YOUR CHANGES WILL BE LOST! +# CIS 1.3.2 + +{{ rhel9cis_aide_cron['aide_minute'] }} {{ rhel9cis_aide_cron['aide_hour'] }} {{ rhel9cis_aide_cron['aide_month'] }} {{ rhel9cis_aide_cron['aide_weekday'] }} {{ rhel9cis_aide_cron['aide_job'] }} diff --git a/templates/etc/dconf/db/00-automount_lock.j2 b/templates/etc/dconf/db/00-automount_lock.j2 new file mode 100644 index 00000000..d92c56b6 --- /dev/null +++ b/templates/etc/dconf/db/00-automount_lock.j2 @@ -0,0 +1,9 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +# Lock desktop media-handling automount setting +/org/gnome/desktop/media-handling/automount + +# Lock desktop media-handling automount-open +/org/gnome/desktop/media-handling/automount-open diff --git a/templates/etc/dconf/db/00-autorun_lock.j2 b/templates/etc/dconf/db/00-autorun_lock.j2 new file mode 100644 index 00000000..503069c9 --- /dev/null +++ b/templates/etc/dconf/db/00-autorun_lock.j2 @@ -0,0 +1,6 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +# Lock desktop media-handling settings +/org/gnome/desktop/media-handling/autorun-never diff --git a/templates/etc/dconf/db/00-media-automount.j2 b/templates/etc/dconf/db/00-media-automount.j2 new file mode 100644 index 00000000..32192c38 --- /dev/null +++ b/templates/etc/dconf/db/00-media-automount.j2 @@ -0,0 +1,7 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +[org/gnome/desktop/media-handling] +automount=false +automount-open=false diff --git a/templates/etc/dconf/db/00-media-autorun.j2 b/templates/etc/dconf/db/00-media-autorun.j2 new file mode 100644 index 00000000..16ded9d1 --- /dev/null +++ b/templates/etc/dconf/db/00-media-autorun.j2 @@ -0,0 +1,6 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +[org/gnome/desktop/media-handling] +autorun-never=true diff --git a/templates/etc/dconf/db/00-screensaver.j2 b/templates/etc/dconf/db/00-screensaver.j2 new file mode 100644 index 00000000..0b9f6862 --- /dev/null +++ b/templates/etc/dconf/db/00-screensaver.j2 @@ -0,0 +1,17 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + + +# Specify the dconf path +[org/gnome/desktop/session] + +# Number of seconds of inactivity before the screen goes blank +# Set to 0 seconds if you want to deactivate the screensaver. +idle-delay=uint32 {{ rhel9cis_screensaver_idle_delay }} + +# Specify the dconf path +[org/gnome/desktop/screensaver] + +# Number of seconds after the screen is blank before locking the screen +lock-delay=uint32 {{ rhel9cis_screensaver_lock_delay }} diff --git a/templates/etc/dconf/db/00-screensaver_lock.j2 b/templates/etc/dconf/db/00-screensaver_lock.j2 new file mode 100644 index 00000000..fae6e82b --- /dev/null +++ b/templates/etc/dconf/db/00-screensaver_lock.j2 @@ -0,0 +1,9 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +# Lock desktop screensaver idle-delay setting +/org/gnome/desktop/session/idle-delay + +# Lock desktop screensaver lock-delay setting +/org/gnome/desktop/screensaver/lock-delay diff --git a/templates/etc/dconf/db/gdm.d/01-banner-message.j2 b/templates/etc/dconf/db/gdm.d/01-banner-message.j2 new file mode 100644 index 00000000..73b45056 --- /dev/null +++ b/templates/etc/dconf/db/gdm.d/01-banner-message.j2 @@ -0,0 +1,7 @@ +## Ansible controlled file +# Added as part of ansible-lockdown CIS baseline +# provided by MindPointGroup LLC + +[org/gnome/login-screen] +banner-message-enable=true +banner-message-text="{{ rhel9cis_warning_banner }}" diff --git a/templates/etc/modprobe.d/modprobe.conf.j2 b/templates/etc/modprobe.d/modprobe.conf.j2 new file mode 100644 index 00000000..77b8cd59 --- /dev/null +++ b/templates/etc/modprobe.d/modprobe.conf.j2 @@ -0,0 +1,6 @@ +# Disable usage of protocol {{ item }} +# Set by ansible {{ benchmark }} remediation role +# https://github.com/ansible-lockdown +## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! + +install {{ item }} /bin/true diff --git a/templates/etc/sysctl.d/60-disable_ipv6.conf.j2 b/templates/etc/sysctl.d/60-disable_ipv6.conf.j2 new file mode 100644 index 00000000..732cbcc0 --- /dev/null +++ b/templates/etc/sysctl.d/60-disable_ipv6.conf.j2 @@ -0,0 +1,7 @@ +## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! + +# IPv6 disable +{% if rhel9cis_rule_3_1_1 and rhel9cis_ipv6_required %} +net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 +{% endif %} diff --git a/templates/etc/sysctl.d/60-kernel_sysctl.conf.j2 b/templates/etc/sysctl.d/60-kernel_sysctl.conf.j2 new file mode 100644 index 00000000..8bd01572 --- /dev/null +++ b/templates/etc/sysctl.d/60-kernel_sysctl.conf.j2 @@ -0,0 +1,8 @@ +## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! + + +{% if rhel9cis_rule_1_5_3 %} +# Kernel sysctl +# CIS 1.5.3 +kernel.randomize_va_space = 2 +{% endif %} \ No newline at end of file diff --git a/templates/etc/sysctl.d/60-netipv4_sysctl.conf.j2 b/templates/etc/sysctl.d/60-netipv4_sysctl.conf.j2 new file mode 100644 index 00000000..8bafbf9b --- /dev/null +++ b/templates/etc/sysctl.d/60-netipv4_sysctl.conf.j2 @@ -0,0 +1,49 @@ +## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! + +# IPv4 Network sysctl +{% if rhel9cis_rule_3_2_1 %} +# CIS 3.2.1 +net.ipv4.ip_forward = 0 +{% endif %} +{% if rhel9cis_rule_3_2_2 %} +# CIS 3.2.2 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_3_1 %} +# CIS 3.3.1 +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +{% endif %} +{% if rhel9cis_rule_3_3_2 %} +# CIS 3.3.2 +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_3_3 %} +# CIS 3.3.3 +net.ipv4.conf.all.secure_redirects = 0 +net.ipv4.conf.default.secure_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_3_4 %} +# CIS 3.3.4 +net.ipv4.conf.all.log_martians = 1 +net.ipv4.conf.default.log_martians = 1 +{% endif %} +{% if rhel9cis_rule_3_3_5 %} +# CIS 3.3.5 +net.ipv4.icmp_echo_ignore_broadcasts = 1 +{% endif %} +{% if rhel9cis_rule_3_3_6 %} +# CIS 3.3.6 +net.ipv4.icmp_ignore_bogus_error_responses = 1 +{% endif %} +{% if rhel9cis_rule_3_3_7 %} +# CIS 3.3.7 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.default.rp_filter = 1 +{% endif %} +{% if rhel9cis_rule_3_3_8 %} +# CIS 3.3.8 +net.ipv4.tcp_syncookies = 1 +{% endif %} diff --git a/templates/etc/sysctl.d/60-netipv6_sysctl.conf.j2 b/templates/etc/sysctl.d/60-netipv6_sysctl.conf.j2 new file mode 100644 index 00000000..e85fae98 --- /dev/null +++ b/templates/etc/sysctl.d/60-netipv6_sysctl.conf.j2 @@ -0,0 +1,21 @@ +## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! + +# IPv6 Network sysctl +{% if rhel9cis_ipv6_required %} +{% if rhel9cis_rule_3_2_1 %} +net.ipv6.conf.all.forwarding = 0 +{% endif %} +{% if rhel9cis_rule_3_3_1 %} +net.ipv6.conf.all.accept_source_route = 0 +net.ipv6.conf.default.accept_source_route = 0 +{% endif %} +{% if rhel9cis_rule_3_3_2 %} +net.ipv6.conf.all.accept_redirects = 0 +net.ipv6.conf.default.accept_redirects = 0 +{% endif %} +{% if rhel9cis_rule_3_3_9 %} +# CIS 3.3.9 +net.ipv6.conf.all.accept_ra = 0 +net.ipv6.conf.default.accept_ra = 0 +{% endif %} +{% endif %} \ No newline at end of file diff --git a/templates/etc/systemd/system/tmp.mount.j2 b/templates/etc/systemd/system/tmp.mount.j2 index 2a97a56b..3f689eef 100644 --- a/templates/etc/systemd/system/tmp.mount.j2 +++ b/templates/etc/systemd/system/tmp.mount.j2 @@ -7,6 +7,8 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. +## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! + [Unit] Description=Temporary Directory (/tmp) Documentation=man:hier(7) @@ -21,8 +23,8 @@ After=swap.target 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 %} +Options=mode=1777,strictatime,{% if rhel9cis_rule_1_1_2_2 %}nodev,{% endif %}{% if rhel9cis_rule_1_1_2_4 %}nosuid,{% endif %}{% if rhel9cis_rule_1_1_2_3 %}noexec{% endif %} # Make 'systemctl enable tmp.mount' work: [Install] -WantedBy=local-fs.target \ No newline at end of file +WantedBy=local-fs.target diff --git a/templates/hosts.allow.j2 b/templates/hosts.allow.j2 deleted file mode 100644 index 4bab3d1f..00000000 --- a/templates/hosts.allow.j2 +++ /dev/null @@ -1,11 +0,0 @@ -# -# 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 deleted file mode 100644 index c745ab14..00000000 --- a/templates/ntp.conf.j2 +++ /dev/null @@ -1,59 +0,0 @@ -# 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/AlmaLinux.yml b/vars/AlmaLinux.yml new file mode 100644 index 00000000..c460fb0a --- /dev/null +++ b/vars/AlmaLinux.yml @@ -0,0 +1,5 @@ +--- +# OS Specific Settings + +os_gpg_key_pubkey_name: gpg-pubkey-b86b3716-61e69f29 +os_gpg_key_pubkey_content: "AlmaLinux OS 9 b86b3716" diff --git a/vars/OracleLinux.yml b/vars/OracleLinux.yml new file mode 100644 index 00000000..d9161786 --- /dev/null +++ b/vars/OracleLinux.yml @@ -0,0 +1,4 @@ +--- +# OS Specific Settings +os_gpg_key_pubkey_name: gpg-pubkey-8d8b756f-629e59ec +os_gpg_key_pubkey_content: "Oracle Linux (release key 1) " diff --git a/vars/RedHat.yml b/vars/RedHat.yml new file mode 100644 index 00000000..d33b0bcf --- /dev/null +++ b/vars/RedHat.yml @@ -0,0 +1,5 @@ +--- +# OS Specific Settings + +os_gpg_key_pubkey_name: gpg-pubkey-fd431d51-4ae0493b +os_gpg_key_pubkey_content: "Red Hat, Inc. (release key 2) fd431d51" diff --git a/vars/Rocky.yml b/vars/Rocky.yml new file mode 100644 index 00000000..77af29c8 --- /dev/null +++ b/vars/Rocky.yml @@ -0,0 +1,5 @@ +--- +# OS Specific Settings + +os_gpg_key_pubkey_name: gpg-pubkey-350d275d-6279464b +os_gpg_key_pubkey_content: "Rocky Enterprise Software Foundation - Release key 2022 350d275d" diff --git a/vars/is_container.yml b/vars/is_container.yml new file mode 100644 index 00000000..1a697845 --- /dev/null +++ b/vars/is_container.yml @@ -0,0 +1,89 @@ +--- + +# File to skip controls if container +# Based on standard image no changes +# it expected all pkgs required for the container are alreday installed + +## controls + +# Firewall +rhel9cis_firewall: None + +# SElinux +rhel9cis_selinux_disable: true + +## Related individual rules +# Aide +rhel9cis_rule_1_4_1: false +rhel9cis_rule_1_4_2: false + +# auditd +rhel9cis_rule_4_1_1_1: false +rhel9cis_rule_4_1_2_1: false +rhel9cis_rule_4_1_2_2: false +rhel9cis_rule_4_1_2_3: false + +# time sync +rhel9cis_rule_2_2_1_1: false +rhel9cis_rule_2_2_1_2: false + +# cron +rhel9cis_rule_5_1_1: false +rhel9cis_rule_5_1_2: false +rhel9cis_rule_5_1_3: false +rhel9cis_rule_5_1_4: false +rhel9cis_rule_5_1_5: false +rhel9cis_rule_5_1_6: false +rhel9cis_rule_5_1_7: false +rhel9cis_rule_5_1_8: false + +# crypto +rhel9cis_rule_1_10: false + +# grub +rhel9cis_rule_1_5_1: false +rhel9cis_rule_1_5_2: false +rhel9cis_rule_1_5_3: false + +## mounts +# /tmp +rhel9cis_rule_1_1_2: false +rhel9cis_rule_1_1_3: false +rhel9cis_rule_1_1_4: false +rhel9cis_rule_1_1_5: false +# /var +rhel9cis_rule_1_1_6: false +# /var/tmp +rhel9cis_rule_1_1_7: false +rhel9cis_rule_1_1_8: false +rhel9cis_rule_1_1_9: false +rhel9cis_rule_1_1_10: false +# /var/log +rhel9cis_rule_1_1_11: false +# /var/log/audit +rhel9cis_rule_1_1_12: false +# /home +rhel9cis_rule_1_1_13: false +rhel9cis_rule_1_1_14: false +# /dev/shm +rhel9cis_rule_1_1_15: false +rhel9cis_rule_1_1_16: false +rhel9cis_rule_1_1_17: false +# usb-storage +rhel9cis_rule_1_1_23: false + +# logging +rhel9cis_rule_4_2_1_1: false +rhel9cis_rule_4_2_1_2: false +rhel9cis_rule_4_2_1_3: false +rhel9cis_rule_4_2_1_4: false +rhel9cis_rule_4_2_1_5: false +rhel9cis_rule_4_2_1_6: false +rhel9cis_rule_4_2_2_1: false +rhel9cis_rule_4_2_2_2: false +rhel9cis_rule_4_2_2_3: false + +# systemd + +# Users/passwords/accounts +rhel9cis_rule_5_5_2: false diff --git a/vars/main.yml b/vars/main.yml index 83b0489f..2a931845 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,5 +1,14 @@ --- # vars file for RHEL9-CIS + +min_ansible_version: 2.10.1 rhel9cis_allowed_crypto_policies: + - 'DEFAULT' - 'FUTURE' - 'FIPS' + +# Used to control warning summary +warn_control_list: "" +warn_count: 0 + +gpg_key_package: "{{ ansible_distribution | lower }}-gpg-keys"