├── .ansible-lint ├── .gitattributes ├── .github └── workflows │ ├── devel_pipeline_validation.yml │ ├── main_pipeline_validation.yml │ └── update_galaxy.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .yamllint ├── CONTRIBUTING.rst ├── Changelog.md ├── LICENSE ├── README.md ├── collections └── requirements.yml ├── defaults └── main.yml ├── files └── etc │ └── systemd │ └── system │ └── tmp.mount ├── handlers └── main.yml ├── meta └── main.yml ├── molecule ├── default │ ├── converge.yml │ ├── molecule.yml │ └── verify.yml ├── localhost │ ├── converge.yml │ ├── molecule.yml │ └── verify.yml └── wsl │ ├── converge.yml │ ├── molecule.yml │ └── verify.yml ├── site.yml ├── tasks ├── LE_audit_setup.yml ├── audit_only.yml ├── auditd.yml ├── fetch_audit_output.yml ├── main.yml ├── parse_etc_password.yml ├── post.yml ├── post_remediation_audit.yml ├── pre_remediation_audit.yml ├── prelim.yml ├── section_1 │ ├── cis_1.1.1.x.yml │ ├── cis_1.1.2.1.x.yml │ ├── cis_1.1.2.2.x.yml │ ├── cis_1.1.2.3.x.yml │ ├── cis_1.1.2.4.x.yml │ ├── cis_1.1.2.5.x.yml │ ├── cis_1.1.2.6.x.yml │ ├── cis_1.1.2.7.x.yml │ ├── cis_1.2.x.yml │ ├── cis_1.3.x.yml │ ├── cis_1.4.x.yml │ ├── cis_1.5.1.x.yml │ ├── cis_1.6.x.yml │ ├── cis_1.7.x.yml │ ├── cis_1.8.x.yml │ ├── cis_1.9.yml │ └── main.yml ├── section_2 │ ├── cis_2.1.x.yml │ ├── cis_2.2.x.yml │ ├── cis_2.3.x.yml │ └── main.yml ├── section_3 │ ├── cis_3.1.x.yml │ ├── cis_3.2.x.yml │ ├── cis_3.3.x.yml │ ├── cis_3.4.1.x.yml │ ├── cis_3.4.2.x.yml │ └── main.yml ├── section_4 │ ├── cis_4.1.1.x.yml │ ├── cis_4.1.2.x.yml │ ├── cis_4.2.x.yml │ ├── cis_4.3.x.yml │ ├── cis_4.4.1.x.yml │ ├── cis_4.4.2.x.yml │ ├── cis_4.4.3.1.x.yml │ ├── cis_4.4.3.2.x.yml │ ├── cis_4.4.3.3.x.yml │ ├── cis_4.4.3.4.x.yml │ ├── cis_4.5.1.x.yml │ ├── cis_4.5.2.x.yml │ ├── cis_4.5.3.x.yml │ └── main.yml ├── section_5 │ ├── cis_5.1.1.x.yml │ ├── cis_5.1.2.x.yml │ ├── cis_5.1.3.yml │ ├── cis_5.1.4.yml │ ├── cis_5.2.1.x.yml │ ├── cis_5.2.2.x.yml │ ├── cis_5.2.3.x.yml │ ├── cis_5.2.4.x.yml │ ├── cis_5.3.x.yml │ └── main.yml ├── section_6 │ ├── cis_6.1.x.yml │ ├── cis_6.2.x.yml │ └── main.yml ├── verify.yml └── warning_facts.yml ├── templates ├── ansible_vars_goss.yml.j2 ├── audit │ └── 99_auditd.rules.j2 ├── chrony.conf.j2 ├── etc │ ├── ansible │ │ └── compliance_facts.j2 │ ├── crypto-policies │ │ └── policies │ │ │ └── modules │ │ │ ├── NO-SHA1.pmod.j2 │ │ │ ├── NO-SSHCBC.pmod.j2 │ │ │ └── NO-WEAKMAC.pmod.j2 │ ├── dconf │ │ └── db │ │ │ ├── 00-media-automount.j2 │ │ │ ├── 00-media-autorun.j2 │ │ │ ├── 00-screensaver.j2 │ │ │ └── locks │ │ │ ├── 00-automount_lock.j2 │ │ │ ├── 00-autorun_lock.j2 │ │ │ └── 00-screensaver_lock.j2 │ ├── issue.j2 │ ├── issue.net.j2 │ ├── motd.j2 │ └── systemd │ │ └── system │ │ └── tmp.mount.j2 └── ntp.conf.j2 └── vars ├── AlmaLinux.yml ├── CentOS.yml ├── OracleLinux.yml ├── RedHat.yml ├── Rocky.yml ├── audit.yml ├── is_container.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parseable: true 4 | quiet: true 5 | skip_list: 6 | - 'schema' 7 | - 'no-changed-when' 8 | - 'var-spacing' 9 | - 'experimental' 10 | - 'name[play]' 11 | - 'name[casing]' 12 | - 'name[template]' 13 | - 'key-order[task]' 14 | - '204' 15 | - '305' 16 | - '303' 17 | - '403' 18 | - '306' 19 | - '602' 20 | - '208' 21 | use_default_rules: true 22 | verbosity: 0 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github 2 | # Default behaviour 3 | * text=auto 4 | 5 | # https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings 6 | # Ensure to read artcile prior to adding 7 | # Scripts should have Unix endings 8 | *.py text eol=lf 9 | *.sh text eol=lf 10 | 11 | # Windows Batch or PowerShell scripts should have CRLF endings 12 | *.bat text eol=crlf 13 | *.ps1 text eol=crlf 14 | 15 | # adding github settings to show correct language 16 | *.sh linguist-detectable=true 17 | *.yml linguist-detectable=true 18 | *.ps1 linguist-detectable=true 19 | *.j2 linguist-detectable=true 20 | *.md linguist-documentation 21 | -------------------------------------------------------------------------------- /.github/workflows/devel_pipeline_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Devel pipeline 4 | 5 | on: # yamllint disable-line rule:truthy 6 | pull_request_target: 7 | types: [opened, reopened, synchronize] 8 | branches: 9 | - devel 10 | - benchmark* 11 | paths: 12 | - '**.yml' 13 | - '**.sh' 14 | - '**.j2' 15 | - '**.ps1' 16 | - '**.cfg' 17 | # Allow manual running of workflow 18 | workflow_dispatch: 19 | 20 | # Allow permissions for AWS auth 21 | permissions: 22 | id-token: write 23 | contents: read 24 | pull-requests: read 25 | 26 | # A workflow run is made up of one or more jobs 27 | # that can run sequentially or in parallel 28 | jobs: 29 | # This will create messages for first time contributers and direct them to the Discord server 30 | welcome: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/first-interaction@main 35 | with: 36 | repo-token: ${{ secrets.GITHUB_TOKEN }} 37 | pr-message: |- 38 | Congrats on opening your first pull request and thank you for taking the time to help improve Ansible-Lockdown! 39 | Please join in the conversation happening on the [Discord Server](https://www.lockdownenterprise.com/discord) as well. 40 | 41 | # This workflow contains a single job that tests the playbook 42 | playbook-test: 43 | # The type of runner that the job will run on 44 | runs-on: self-hosted 45 | env: 46 | ENABLE_DEBUG: ${{ vars.ENABLE_DEBUG }} 47 | # Imported as a variable by terraform 48 | TF_VAR_repository: ${{ github.event.repository.name }} 49 | AWS_REGION: "us-east-1" 50 | ANSIBLE_VERSION: ${{ vars.ANSIBLE_RUNNER_VERSION }} 51 | defaults: 52 | run: 53 | shell: bash 54 | working-directory: .github/workflows/github_linux_IaC 55 | # working-directory: .github/workflows 56 | 57 | steps: 58 | 59 | - name: Git clone the lockdown repository to test 60 | uses: actions/checkout@v4 61 | with: 62 | ref: ${{ github.event.pull_request.head.sha }} 63 | 64 | - name: If a variable for IAC_BRANCH is set use that branch 65 | working-directory: .github/workflows 66 | run: | 67 | if [ ${{ vars.IAC_BRANCH }} != '' ]; then 68 | echo "IAC_BRANCH=${{ vars.IAC_BRANCH }}" >> $GITHUB_ENV 69 | echo "Pipeline using the following IAC branch ${{ vars.IAC_BRANCH }}" 70 | else 71 | echo IAC_BRANCH=main >> $GITHUB_ENV 72 | fi 73 | 74 | # Pull in terraform code for linux servers 75 | - name: Clone GitHub IaC plan 76 | uses: actions/checkout@v4 77 | with: 78 | repository: ansible-lockdown/github_linux_IaC 79 | path: .github/workflows/github_linux_IaC 80 | ref: ${{ env.IAC_BRANCH }} 81 | 82 | # Uses dedicated restricted role and policy to enable this only for this task 83 | # No credentials are part of github for AWS auth 84 | - name: configure aws credentials 85 | uses: aws-actions/configure-aws-credentials@main 86 | with: 87 | role-to-assume: ${{ secrets.AWS_ASSUME_ROLE }} 88 | role-session-name: ${{ secrets.AWS_ROLE_SESSION }} 89 | aws-region: ${{ env.AWS_REGION }} 90 | 91 | - name: DEBUG - Show IaC files 92 | if: env.ENABLE_DEBUG == 'true' 93 | run: | 94 | echo "OSVAR = $OSVAR" 95 | echo "benchmark_type = $benchmark_type" 96 | echo "PRIVSUBNET_ID = $AWS_PRIVSUBNET_ID" 97 | echo "VPC_ID" = $AWS_VPC_SECGRP_ID" 98 | pwd 99 | ls 100 | env: 101 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 102 | OSVAR: ${{ vars.OSVAR }} 103 | benchmark_type: ${{ vars.BENCHMARK_TYPE }} 104 | PRIVSUBNET_ID: ${{ secrets.AWS_PRIVSUBNET_ID }} 105 | VPC_ID: ${{ secrets.AWS_VPC_SECGRP_ID }} 106 | 107 | - name: Tofu init 108 | id: init 109 | run: tofu init 110 | env: 111 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 112 | OSVAR: ${{ vars.OSVAR }} 113 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 114 | 115 | - name: Tofu validate 116 | id: validate 117 | run: tofu validate 118 | env: 119 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 120 | OSVAR: ${{ vars.OSVAR }} 121 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 122 | 123 | - name: Tofu apply 124 | id: apply 125 | env: 126 | OSVAR: ${{ vars.OSVAR }} 127 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 128 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 129 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 130 | run: tofu apply -var-file "${OSVAR}.tfvars" --auto-approve -input=false 131 | 132 | ## Debug Section 133 | - name: DEBUG - Show Ansible hostfile 134 | if: env.ENABLE_DEBUG == 'true' 135 | run: cat hosts.yml 136 | 137 | # Aws deployments taking a while to come up insert sleep or playbook fails 138 | 139 | - name: Sleep to allow system to come up 140 | run: sleep ${{ vars.BUILD_SLEEPTIME }} 141 | 142 | # Run the Ansible playbook 143 | - name: Run_Ansible_Playbook 144 | env: 145 | ANSIBLE_HOST_KEY_CHECKING: "false" 146 | ANSIBLE_DEPRECATION_WARNINGS: "false" 147 | run: | 148 | /opt/ansible_${{ env.ANSIBLE_VERSION }}_venv/bin/ansible-playbook -i hosts.yml --private-key ~/.ssh/le_runner ../../../site.yml 149 | 150 | # Remove test system - User secrets to keep if necessary 151 | 152 | - name: Tofu Destroy 153 | if: always() && env.ENABLE_DEBUG == 'false' 154 | env: 155 | OSVAR: ${{ vars.OSVAR }} 156 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 157 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 158 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 159 | run: tofu destroy -var-file "${OSVAR}.tfvars" --auto-approve -input=false 160 | -------------------------------------------------------------------------------- /.github/workflows/main_pipeline_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Main pipeline 4 | 5 | on: # yamllint disable-line rule:truthy 6 | pull_request_target: 7 | types: [opened, reopened, synchronize] 8 | branches: 9 | - main 10 | - latest 11 | paths: 12 | - '**.yml' 13 | - '**.sh' 14 | - '**.j2' 15 | - '**.ps1' 16 | - '**.cfg' 17 | 18 | # Allow permissions for AWS auth 19 | permissions: 20 | id-token: write 21 | contents: read 22 | pull-requests: read 23 | 24 | # A workflow run is made up of one or more jobs 25 | # that can run sequentially or in parallel 26 | jobs: 27 | 28 | # This workflow contains a single job that tests the playbook 29 | playbook-test: 30 | # The type of runner that the job will run on 31 | runs-on: self-hosted 32 | env: 33 | ENABLE_DEBUG: ${{ vars.ENABLE_DEBUG }} 34 | # Imported as a variable by terraform 35 | TF_VAR_repository: ${{ github.event.repository.name }} 36 | AWS_REGION : "us-east-1" 37 | ANSIBLE_VERSION: ${{ vars.ANSIBLE_RUNNER_VERSION }} 38 | defaults: 39 | run: 40 | shell: bash 41 | working-directory: .github/workflows/github_linux_IaC 42 | # working-directory: .github/workflows 43 | 44 | steps: 45 | 46 | - name: Git clone the lockdown repository to test 47 | uses: actions/checkout@v4 48 | with: 49 | ref: ${{ github.event.pull_request.head.sha }} 50 | 51 | - name: If a variable for IAC_BRANCH is set use that branch 52 | working-directory: .github/workflows 53 | run: | 54 | if [ ${{ vars.IAC_BRANCH }} != '' ]; then 55 | echo "IAC_BRANCH=${{ vars.IAC_BRANCH }}" >> $GITHUB_ENV 56 | echo "Pipeline using the following IAC branch ${{ vars.IAC_BRANCH }}" 57 | else 58 | echo IAC_BRANCH=main >> $GITHUB_ENV 59 | fi 60 | 61 | # Pull in terraform code for linux servers 62 | - name: Clone GitHub IaC plan 63 | uses: actions/checkout@v4 64 | with: 65 | repository: ansible-lockdown/github_linux_IaC 66 | path: .github/workflows/github_linux_IaC 67 | ref: ${{ env.IAC_BRANCH }} 68 | 69 | # Uses dedicated restricted role and policy to enable this only for this task 70 | # No credentials are part of github for AWS auth 71 | - name: configure aws credentials 72 | uses: aws-actions/configure-aws-credentials@main 73 | with: 74 | role-to-assume: ${{ secrets.AWS_ASSUME_ROLE }} 75 | role-session-name: ${{ secrets.AWS_ROLE_SESSION }} 76 | aws-region: ${{ env.AWS_REGION }} 77 | 78 | - name: DEBUG - Show IaC files 79 | if: env.ENABLE_DEBUG == 'true' 80 | run: | 81 | echo "OSVAR = $OSVAR" 82 | echo "benchmark_type = $benchmark_type" 83 | echo "PRIVSUBNET_ID = $AWS_PRIVSUBNET_ID" 84 | echo "VPC_ID" = $AWS_VPC_SECGRP_ID" 85 | pwd 86 | ls 87 | env: 88 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 89 | OSVAR: ${{ vars.OSVAR }} 90 | benchmark_type: ${{ vars.BENCHMARK_TYPE }} 91 | PRIVSUBNET_ID: ${{ secrets.AWS_PRIVSUBNET_ID }} 92 | VPC_ID: ${{ secrets.AWS_VPC_SECGRP_ID }} 93 | 94 | - name: Tofu init 95 | id: init 96 | run: tofu init 97 | env: 98 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 99 | OSVAR: ${{ vars.OSVAR }} 100 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 101 | 102 | - name: Tofu validate 103 | id: validate 104 | run: tofu validate 105 | env: 106 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 107 | OSVAR: ${{ vars.OSVAR }} 108 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 109 | 110 | - name: Tofu apply 111 | id: apply 112 | env: 113 | OSVAR: ${{ vars.OSVAR }} 114 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 115 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 116 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 117 | run: tofu apply -var-file "${OSVAR}.tfvars" --auto-approve -input=false 118 | 119 | ## Debug Section 120 | - name: DEBUG - Show Ansible hostfile 121 | if: env.ENABLE_DEBUG == 'true' 122 | run: cat hosts.yml 123 | 124 | # Aws deployments taking a while to come up insert sleep or playbook fails 125 | 126 | - name: Sleep to allow system to come up 127 | run: sleep ${{ vars.BUILD_SLEEPTIME }} 128 | 129 | # Run the Ansible playbook 130 | - name: Run_Ansible_Playbook 131 | env: 132 | ANSIBLE_HOST_KEY_CHECKING: "false" 133 | ANSIBLE_DEPRECATION_WARNINGS: "false" 134 | run: | 135 | /opt/ansible_${{ env.ANSIBLE_VERSION }}_venv/bin/ansible-playbook -i hosts.yml --private-key ~/.ssh/le_runner ../../../site.yml 136 | 137 | # Remove test system - User secrets to keep if necessary 138 | 139 | - name: Tofu Destroy 140 | if: always() && env.ENABLE_DEBUG == 'false' 141 | env: 142 | OSVAR: ${{ vars.OSVAR }} 143 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 144 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 145 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 146 | run: tofu destroy -var-file "${OSVAR}.tfvars" --auto-approve -input=false 147 | -------------------------------------------------------------------------------- /.github/workflows/update_galaxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: update galaxy 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | update_role: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repo 14 | uses: actions/checkout@v4 15 | 16 | - name: Action Ansible Galaxy Release ${{ github.ref_name }} 17 | uses: ansible-actions/ansible-galaxy-action@main 18 | with: 19 | galaxy_api_key: ${{ secrets.GALAXY_API_KEY }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | *.log 3 | *.retry 4 | .vagrant 5 | tests/*redhat-subscription 6 | tests/Dockerfile 7 | *.iso 8 | *.box 9 | packer_cache 10 | delete* 11 | ignore* 12 | # VSCode 13 | .vscode 14 | vagrant 15 | 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # DS_Store 22 | .DS_Store 23 | ._* 24 | 25 | # Linux Editors 26 | *~ 27 | \#*\# 28 | /.emacs.desktop 29 | /.emacs.desktop.lock 30 | .elc 31 | auto-save-list 32 | tramp 33 | .\#* 34 | *.swp 35 | *.swo 36 | rh-creds.env 37 | travis.env 38 | 39 | # Lockdown-specific 40 | benchparse/ 41 | *xccdf.xml 42 | *.retry 43 | 44 | # GitHub Action/Workflow files 45 | .github/ 46 | 47 | # ansible-lint cache 48 | .ansible/ 49 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ##### CI for use by github no need for action to be added 3 | ##### Inherited 4 | ci: 5 | autofix_prs: false 6 | skip: [detect-aws-credentials, ansible-lint ] 7 | 8 | repos: 9 | - repo: https://github.com/pre-commit/pre-commit-hooks 10 | rev: v5.0.0 11 | hooks: 12 | # Safety 13 | - id: detect-aws-credentials 14 | - id: detect-private-key 15 | 16 | # git checks 17 | - id: check-merge-conflict 18 | - id: check-added-large-files 19 | - id: check-case-conflict 20 | 21 | # General checks 22 | - id: trailing-whitespace 23 | name: Trim Trailing Whitespace 24 | description: This hook trims trailing whitespace. 25 | entry: trailing-whitespace-fixer 26 | language: python 27 | types: [text] 28 | args: [--markdown-linebreak-ext=md] 29 | - id: end-of-file-fixer 30 | 31 | # Scan for passwords 32 | - repo: https://github.com/Yelp/detect-secrets 33 | rev: v1.5.0 34 | hooks: 35 | - id: detect-secrets 36 | 37 | - repo: https://github.com/gitleaks/gitleaks 38 | rev: v8.26.0 39 | hooks: 40 | - id: gitleaks 41 | 42 | - repo: https://github.com/ansible-community/ansible-lint 43 | rev: v25.5.0 44 | hooks: 45 | - id: ansible-lint 46 | name: Ansible-lint 47 | description: This hook runs ansible-lint. 48 | entry: python3 -m ansiblelint --force-color site.yml -c .ansible-lint 49 | language: python 50 | # do not pass files to ansible-lint, see: 51 | # https://github.com/ansible/ansible-lint/issues/611 52 | pass_filenames: false 53 | always_run: true 54 | additional_dependencies: 55 | # https://github.com/pre-commit/pre-commit/issues/1526 56 | # If you want to use specific version of ansible-core or ansible, feel 57 | # free to override `additional_dependencies` in your own hook config 58 | # file. 59 | - ansible-core>=2.10.1 60 | 61 | - repo: https://github.com/adrienverge/yamllint.git 62 | rev: v1.37.1 # or higher tag 63 | hooks: 64 | - id: yamllint 65 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | ignore: | 5 | tests/ 6 | molecule/ 7 | .github/ 8 | .gitlab-ci.yml 9 | *molecule.yml 10 | 11 | rules: 12 | indentation: 13 | # Requiring 4 space indentation 14 | spaces: 4 15 | # Requiring consistent indentation within a file, either indented or not 16 | indent-sequences: consistent 17 | braces: 18 | max-spaces-inside: 1 19 | level: error 20 | brackets: 21 | max-spaces-inside: 1 22 | level: error 23 | empty-lines: 24 | max: 1 25 | line-length: disable 26 | key-duplicates: enable 27 | new-line-at-end-of-file: enable 28 | new-lines: 29 | type: unix 30 | trailing-spaces: enable 31 | truthy: 32 | allowed-values: ['true', 'false'] 33 | check-keys: true 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing to MindPoint Group Projects 2 | ======================================== 3 | 4 | Rules 5 | ----- 6 | 1) All commits must be GPG signed (details in Signing section) 7 | 2) All commits must have Signed-off-by (Signed-off-by: Joan Doe ) in the commit message (details in Signing section) 8 | 3) All work is done in your own branch 9 | 4) All pull requests go into the devel branch. There are automated checks for signed commits, signoff in commit message, and functional testing) 10 | 5) Be open and nice to eachother 11 | 12 | Workflow 13 | -------- 14 | - Your work is done in your own individual branch. Make sure to to Signed-off and GPG sign all commits you intend to merge 15 | - All community Pull Requests are into the devel branch. There are automated checks for GPG signed, Signed-off in commits, and functional tests before being approved. If your pull request comes in from outside of our repo, the pull request will go into a staging branch. There is info needed from our repo for our CI/CD testing. 16 | - 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 17 | 18 | Signing your contribution 19 | ------------------------- 20 | 21 | We've chosen to use the Developer's Certificate of Origin (DCO) method 22 | that is employed by the Linux Kernel Project, which provides a simple 23 | way to contribute to MindPoint Group projects. 24 | 25 | The process is to certify the below DCO 1.1 text 26 | :: 27 | 28 | Developer's Certificate of Origin 1.1 29 | 30 | By making a contribution to this project, I certify that: 31 | 32 | (a) The contribution was created in whole or in part by me and I 33 | have the right to submit it under the open source license 34 | indicated in the file; or 35 | 36 | (b) The contribution is based upon previous work that, to the best 37 | of my knowledge, is covered under an appropriate open source 38 | license and I have the right under that license to submit that 39 | work with modifications, whether created in whole or in part 40 | by me, under the same open source license (unless I am 41 | permitted to submit under a different license), as indicated 42 | in the file; or 43 | 44 | (c) The contribution was provided directly to me by some other 45 | person who certified (a), (b) or (c) and I have not modified 46 | it. 47 | 48 | (d) I understand and agree that this project and the contribution 49 | are public and that a record of the contribution (including all 50 | personal information I submit with it, including my sign-off) is 51 | maintained indefinitely and may be redistributed consistent with 52 | this project or the open source license(s) involved. 53 | :: 54 | 55 | Then, when it comes time to submit a contribution, include the 56 | following text in your contribution commit message: 57 | 58 | :: 59 | 60 | Signed-off-by: Joan Doe 61 | 62 | :: 63 | 64 | This message can be entered manually, or if you have configured git 65 | with the correct `user.name` and `user.email`, you can use the `-s` 66 | option to `git commit` to automatically include the signoff message. 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mindpoint Group / Lockdown Enterprise / Lockdown Enterprise Releases 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /collections/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | collections: 4 | - name: community.general 5 | source: https://github.com/ansible-collections/community.general 6 | type: git 7 | 8 | - name: community.crypto 9 | source: https://github.com/ansible-collections/community.crypto 10 | type: git 11 | 12 | - name: ansible.posix 13 | source: https://github.com/ansible-collections/ansible.posix 14 | type: git 15 | -------------------------------------------------------------------------------- /files/etc/systemd/system/tmp.mount: -------------------------------------------------------------------------------- 1 | # This file is part of systemd. 2 | # 3 | # systemd is free software; you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation; either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | 8 | [Unit] 9 | Description=Temporary Directory 10 | Documentation=man:hier(7) 11 | Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems 12 | ConditionPathIsSymbolicLink=!/tmp 13 | DefaultDependencies=no 14 | Conflicts=umount.target 15 | Before=local-fs.target umount.target 16 | 17 | [Mount] 18 | What=tmpfs 19 | Where=/tmp 20 | Type=tmpfs 21 | Options=mode=1777,strictatime,noexec,nodev,nosuid 22 | 23 | # Make 'systemctl enable tmp.mount' work: 24 | [Install] 25 | WantedBy=local-fs.target 26 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for RHEL8-CIS 3 | 4 | - name: Update_authselect 5 | ansible.builtin.shell: authselect apply-changes 6 | 7 | - name: Remount_tmp 8 | ansible.builtin.shell: mount -o remount /tmp 9 | 10 | - name: Systemd_restart_tmp.mount 11 | ansible.builtin.systemd: 12 | name: tmp.mount 13 | daemon_reload: true 14 | enabled: true 15 | masked: false 16 | state: reloaded 17 | 18 | - name: Reload_sysctl 19 | ansible.builtin.shell: sysctl --system 20 | 21 | - name: Sysctl_flush_ipv4_routes 22 | ansible.posix.sysctl: 23 | name: net.ipv4.route.flush 24 | value: '1' 25 | sysctl_set: true 26 | ignore_errors: true # noqa ignore-errors 27 | when: 28 | - not system_is_container 29 | 30 | - name: Sysctl_flush_ipv6_routes 31 | ansible.posix.sysctl: 32 | name: net.ipv6.route.flush 33 | value: '1' 34 | sysctl_set: true 35 | ignore_errors: true # noqa ignore-errors 36 | when: 37 | - not system_is_container 38 | 39 | - name: Restart_sshd 40 | ansible.builtin.service: 41 | name: sshd 42 | state: restarted 43 | 44 | - name: Restart_chronyd 45 | ansible.builtin.service: 46 | name: chronyd 47 | state: restarted 48 | 49 | - name: Restart_postfix 50 | ansible.builtin.service: 51 | name: postfix 52 | state: restarted 53 | 54 | - name: Reload_dconf 55 | ansible.builtin.shell: dconf update 56 | 57 | - name: Update_auditd_rules 58 | ansible.builtin.template: 59 | src: audit/99_auditd.rules.j2 60 | dest: /etc/audit/rules.d/99_auditd.rules 61 | owner: root 62 | group: root 63 | mode: '0600' 64 | notify: Restart_auditd 65 | 66 | - name: Restart_journald 67 | ansible.builtin.service: 68 | name: systemd-journald 69 | state: restarted 70 | 71 | - name: Restart_systemd_journal_upload 72 | ansible.builtin.service: 73 | name: systemd-journal-upload 74 | state: restarted 75 | 76 | - name: Rebuild_grub 77 | ansible.builtin.shell: "grub2-mkconfig -o {{ prelim_grub_cfg.stat.lnk_source }}" 78 | ignore_errors: true # noqa ignore-errors 79 | notify: Change_requires_reboot 80 | tags: 81 | - skip_ansible_lint 82 | 83 | - name: Restart_rsyslog 84 | ansible.builtin.service: 85 | name: rsyslog 86 | state: restarted 87 | 88 | - name: Systemd_daemon_reload 89 | ansible.builtin.systemd: 90 | daemon-reload: true 91 | 92 | ## Auditd tasks note order for handlers to run 93 | 94 | - name: Auditd_immutable_check 95 | ansible.builtin.shell: grep -c "^-e 2" /etc/audit/rules.d/99_auditd.rules 96 | changed_when: false 97 | register: discovered_auditd_immutable_check 98 | 99 | - name: Audit_immutable_fact 100 | ansible.builtin.debug: 101 | msg: "Reboot required for auditd to apply new rules as immutable set" 102 | notify: Change_requires_reboot 103 | when: 104 | - discovered_auditd_immutable_check.stdout == '1' 105 | 106 | - name: Restart_auditd 107 | ansible.builtin.shell: /sbin/service auditd restart 108 | 109 | - name: Change_requires_reboot 110 | ansible.builtin.set_fact: 111 | reboot_required: true 112 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: "MindPoint Group" 4 | description: "Apply the DISA RHEL 8 CIS" 5 | company: "MindPoint Group" 6 | license: MIT 7 | namespace: mindpointgroup 8 | role_name: rhel8_cis 9 | min_ansible_version: 2.11.1 10 | platforms: 11 | - name: EL 12 | versions: 13 | - '8' 14 | galaxy_tags: 15 | - system 16 | - security 17 | - cis 18 | - hardening 19 | - rocky 20 | - alma 21 | - oel 22 | - redhat 23 | - rhel 24 | - compliance 25 | - complianceascode 26 | collections: 27 | - community.general 28 | - community.crypto 29 | - ansible.posix 30 | dependencies: [] 31 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is a playbook to test the tasks. 3 | - name: Converge 4 | hosts: all 5 | gather_facts: true 6 | 7 | vars: 8 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 9 | ansible_user: root 10 | system_is_container: true 11 | rhel8cis_selinux_disable: true 12 | rhel8cis_rule_5_3_4: false 13 | rhel8cis_rule_1_1_10: false 14 | rhel8cis_rsyslog_ansiblemanaged: false 15 | rhel8cis_rule_3_4_1_3: false 16 | rhel8cis_rule_3_4_1_4: false 17 | rhel8cis_rule_4_1_1_1: false 18 | rhel8cis_rule_4_1_1_2: false 19 | rhel8cis_rule_4_1_1_3: false 20 | rhel8cis_rule_4_1_1_4: false 21 | rhel8cis_rule_4_2_1_2: false 22 | rhel8cis_rule_4_2_1_4: false 23 | rhel8cis_rule_5_1_1: false 24 | 25 | pre_tasks: 26 | tasks: 27 | - name: "Include tasks" 28 | ansible.builtin.include_role: 29 | name: "{{ role_name }}" 30 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Molecule configuration 3 | # https://molecule.readthedocs.io/en/latest/ 4 | 5 | driver: 6 | name: docker 7 | 8 | platforms: 9 | - name: ubi8 10 | image: registry.access.redhat.com/ubi8/ubi-init 11 | pre_build_image: true 12 | volumes: 13 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 14 | privileged: true 15 | command: "/usr/sbin/init" 16 | capabilities: 17 | - SYS_ADMIN 18 | 19 | provisioner: 20 | name: ansible 21 | config_options: 22 | defaults: 23 | interpreter_python: auto_silent 24 | callbacks_enabled: profile_tasks, timer 25 | 26 | lint: | 27 | set -e 28 | yamllint . 29 | ansible-lint 30 | flake8 31 | 32 | verifier: 33 | name: ansible 34 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: all 4 | gather_facts: false 5 | 6 | vars: 7 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 8 | 9 | tasks: 10 | - name: "Include verify tasks" 11 | ansible.builtin.include_role: 12 | name: "{{ role_name }}" 13 | tasks_from: verify 14 | -------------------------------------------------------------------------------- /molecule/localhost/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is a playbook to test the tasks. 3 | - name: Converge 4 | hosts: all 5 | become: true 6 | gather_facts: true 7 | 8 | vars: 9 | ansible_user: "{{ lookup('env', 'USER') }}" 10 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 11 | rhel8cis_rule_5_3_4: false 12 | 13 | pre_tasks: 14 | tasks: 15 | - name: "Include tasks" 16 | ansible.builtin.include_role: 17 | name: "{{ role_name }}" 18 | -------------------------------------------------------------------------------- /molecule/localhost/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Molecule configuration 3 | # https://molecule.readthedocs.io/en/latest/ 4 | 5 | driver: 6 | name: delegated 7 | options: 8 | managed: false 9 | ansible_connection_options: 10 | ansible_connection: local 11 | platforms: 12 | - name: localhost 13 | 14 | provisioner: 15 | name: ansible 16 | config_options: 17 | defaults: 18 | interpreter_python: auto_silent 19 | stdout_callback: yaml 20 | callbacks_enabled: profile_tasks, timer 21 | 22 | lint: | 23 | set -e 24 | yamllint . 25 | ansible-lint 26 | flake8 27 | 28 | verifier: 29 | name: ansible 30 | -------------------------------------------------------------------------------- /molecule/localhost/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: all 4 | gather_facts: false 5 | become: true 6 | 7 | vars: 8 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 9 | 10 | tasks: 11 | - name: "Include verify tasks" 12 | ansible.builtin.include_role: 13 | name: "{{ role_name }}" 14 | tasks_from: verify 15 | -------------------------------------------------------------------------------- /molecule/wsl/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is a playbook to test the tasks. 3 | - name: Converge 4 | hosts: all 5 | become: true 6 | gather_facts: true 7 | 8 | vars: 9 | ansible_user: "{{ lookup('env', 'USER') }}" 10 | system_is_container: true 11 | rhel8cis_selinux_disable: true 12 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 13 | rhel8cis_rule_5_3_4: false 14 | rhel8cis_rule_1_1_10: false 15 | rhel8cis_rsyslog_ansiblemanaged: false 16 | rhel8cis_rule_3_4_1_3: false 17 | rhel8cis_rule_3_4_1_4: false 18 | rhel8cis_rule_4_2_1_2: false 19 | rhel8cis_rule_4_2_1_4: false 20 | rhel8cis_rule_5_1_1: false 21 | 22 | pre_tasks: 23 | tasks: 24 | - name: "Include tasks" 25 | ansible.builtin.include_role: 26 | name: "{{ role_name }}" 27 | -------------------------------------------------------------------------------- /molecule/wsl/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Molecule configuration 3 | # https://molecule.readthedocs.io/en/latest/ 4 | 5 | driver: 6 | name: delegated 7 | options: 8 | managed: false 9 | ansible_connection_options: 10 | ansible_connection: local 11 | platforms: 12 | - name: localhost 13 | 14 | provisioner: 15 | name: ansible 16 | config_options: 17 | defaults: 18 | interpreter_python: auto_silent 19 | callbacks_enabled: profile_tasks, timer 20 | 21 | lint: | 22 | set -e 23 | yamllint . 24 | ansible-lint 25 | flake8 26 | 27 | verifier: 28 | name: ansible 29 | -------------------------------------------------------------------------------- /molecule/wsl/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: all 4 | gather_facts: false 5 | 6 | vars: 7 | role_name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" 8 | 9 | tasks: 10 | - name: "Include verify tasks" 11 | ansible.builtin.include_role: 12 | name: "{{ role_name }}" 13 | tasks_from: verify 14 | -------------------------------------------------------------------------------- /site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run RHEL8 CIS hardening 3 | hosts: all 4 | become: true 5 | 6 | roles: 7 | 8 | - role: "{{ playbook_dir }}" 9 | -------------------------------------------------------------------------------- /tasks/LE_audit_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Pre Audit Setup | Set audit package name 4 | block: 5 | - name: Pre Audit Setup | Set audit package name | 64bit 6 | when: ansible_facts.machine == "x86_64" 7 | ansible.builtin.set_fact: 8 | audit_pkg_arch_name: AMD64 9 | 10 | - name: Pre Audit Setup | Set audit package name | ARM64 11 | when: ansible_facts.machine == "aarch64" 12 | ansible.builtin.set_fact: 13 | audit_pkg_arch_name: ARM64 14 | 15 | - name: Pre Audit Setup | Download audit binary 16 | when: 17 | - get_audit_binary_method == 'download' 18 | ansible.builtin.get_url: 19 | url: "{{ audit_bin_url }}{{ audit_pkg_arch_name }}" 20 | dest: "{{ audit_bin }}" 21 | owner: root 22 | group: root 23 | checksum: "{{ audit_bin_version[audit_pkg_arch_name + '_checksum'] }}" 24 | mode: '0555' 25 | 26 | - name: Pre Audit Setup | Copy audit binary 27 | when: 28 | - get_audit_binary_method == 'copy' 29 | ansible.builtin.copy: 30 | src: "{{ audit_bin_copy_location }}" 31 | dest: "{{ audit_bin }}" 32 | mode: '0555' 33 | owner: root 34 | group: root 35 | -------------------------------------------------------------------------------- /tasks/audit_only.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Audit_only | Show Audit Summary 4 | when: 5 | - audit_only 6 | ansible.builtin.debug: 7 | msg: "{{ audit_results.split('\n') }}" 8 | 9 | - name: Audit_only | Stop Playbook Audit Only selected 10 | when: 11 | - audit_only 12 | ansible.builtin.meta: end_play 13 | -------------------------------------------------------------------------------- /tasks/auditd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: POST | AUDITD | Apply auditd template will for section 5.2.3 - only required rules will be added 4 | ansible.builtin.template: 5 | src: audit/99_auditd.rules.j2 6 | dest: /etc/audit/rules.d/99_auditd.rules 7 | owner: root 8 | group: root 9 | mode: '0600' 10 | notify: 11 | - Auditd_immutable_check 12 | - Audit_immutable_fact 13 | - Restart_auditd 14 | -------------------------------------------------------------------------------- /tasks/fetch_audit_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Stage to copy audit output to a centralised location 4 | 5 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller" 6 | when: audit_output_collection_method == "fetch" 7 | ansible.builtin.fetch: 8 | src: "{{ item }}" 9 | dest: "{{ audit_output_destination }}" 10 | flat: true 11 | failed_when: false 12 | register: discovered_audit_fetch_state 13 | loop: 14 | - "{{ pre_audit_outfile }}" 15 | - "{{ post_audit_outfile }}" 16 | become: false 17 | 18 | # Added this option for continuity but could be changed by adjusting the variable audit_conf_dest 19 | # Allowing backup to one location 20 | - name: "FETCH_AUDIT_FILES | Copy files to location available to managed node" 21 | when: audit_output_collection_method == "copy" 22 | ansible.builtin.copy: 23 | src: "{{ item }}" 24 | dest: "{{ audit_output_destination }}" 25 | mode: 'u-x,go-wx' 26 | flat: true 27 | failed_when: false 28 | register: discovered_audit_fetch_copy_state 29 | loop: 30 | - pre_audit_outfile 31 | - post_audit_outfile 32 | 33 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 34 | when: 35 | - (audit_output_collection_method == "fetch" and not discovered_audit_fetch_state.changed) or 36 | (audit_output_collection_method == "copy" and not discovered_audit_copy_state.changed) 37 | block: 38 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 39 | ansible.builtin.debug: 40 | msg: "Warning!! Unable to write to localhost {{ audit_output_destination }} for audit file copy" 41 | 42 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 43 | vars: 44 | warn_control_id: "FETCH_AUDIT_FILES" 45 | ansible.builtin.import_tasks: 46 | file: warning_facts.yml 47 | -------------------------------------------------------------------------------- /tasks/parse_etc_password.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" 4 | block: 5 | - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" 6 | ansible.builtin.shell: cat /etc/passwd | grep -v '^#' 7 | changed_when: false 8 | check_mode: false 9 | register: discovered_passwd_file_audit 10 | 11 | - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" 12 | ansible.builtin.set_fact: 13 | rhel8cis_passwd: "{{ discovered_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" 14 | with_items: "{{ discovered_passwd_file_audit.stdout_lines }}" 15 | vars: 16 | ld_passwd_regex: >- 17 | ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) 18 | ld_passwd_yaml: | # pragma: allowlist secret 19 | id: >-4 20 | \g 21 | password: >-4 22 | \g 23 | uid: \g 24 | gid: \g 25 | gecos: >-4 26 | \g 27 | dir: >-4 28 | \g 29 | shell: >-4 30 | \g 31 | tags: 32 | - always 33 | -------------------------------------------------------------------------------- /tasks/post.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Post tasks 3 | 4 | - name: POST | Perform DNF package cleanup 5 | ansible.builtin.dnf: 6 | autoremove: true 7 | 8 | - name: POST | flush handlers 9 | ansible.builtin.meta: flush_handlers 10 | 11 | - name: POST | reboot system if changes require it and not skipped 12 | block: 13 | - name: POST | Reboot system if changes require it and not skipped 14 | when: 15 | - reboot_required 16 | - not skip_reboot 17 | ansible.builtin.reboot: 18 | 19 | - name: POST | Warning a reboot required but skip option set 20 | when: 21 | - reboot_required 22 | - skip_reboot 23 | ansible.builtin.debug: 24 | msg: "Warning!! changes have been made that require a reboot to be implemented but skip reboot was set - Can affect compliance check results" 25 | changed_when: reboot_warning_changed_when 26 | 27 | - name: "POST | Warning a reboot required but skip option set | warning count" 28 | when: 29 | - reboot_required 30 | - skip_reboot 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | vars: 34 | warn_control_id: Reboot_required 35 | tags: 36 | - always 37 | 38 | - name: If Warning count is 0 set fact 39 | when: 40 | - warn_count == '0' 41 | ansible.builtin.set_fact: 42 | control_number: "Congratulation None Found" 43 | -------------------------------------------------------------------------------- /tasks/post_remediation_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Post Audit | Run post_remediation {{ benchmark }} audit 4 | ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ post_audit_outfile }} -g \"{{ group_names }}\"" 5 | changed_when: true 6 | environment: 7 | AUDIT_BIN: "{{ audit_bin }}" 8 | AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" 9 | AUDIT_FILE: goss.yml 10 | 11 | - name: Post Audit | Ensure audit files readable by users 12 | ansible.builtin.file: 13 | path: "{{ item }}" 14 | mode: '0644' 15 | state: file 16 | loop: 17 | - "{{ post_audit_outfile }}" 18 | - "{{ pre_audit_outfile }}" 19 | 20 | - name: Post Audit | Capture audit data if json format 21 | when: 22 | - audit_format == "json" 23 | block: 24 | - name: Post Audit | Capture audit data if json format 25 | ansible.builtin.shell: grep -E '"summary-line.*Count:.*Failed' "{{ post_audit_outfile }}" | cut -d'"' -f4 26 | register: post_audit_summary 27 | changed_when: false 28 | 29 | - name: Post Audit | Set Fact for audit summary 30 | ansible.builtin.set_fact: 31 | post_audit_results: "{{ post_audit_summary.stdout }}" 32 | 33 | - name: Post Audit | Capture audit data if documentation format 34 | when: 35 | - audit_format == "documentation" 36 | block: 37 | - name: Post Audit | Capture audit data if documentation format 38 | ansible.builtin.shell: "tail -2 {{ post_audit_outfile }}" 39 | register: post_audit_summary 40 | changed_when: false 41 | 42 | - name: Post Audit | Set Fact for audit summary 43 | ansible.builtin.set_fact: 44 | post_audit_results: "{{ post_audit_summary.stdout }}" 45 | -------------------------------------------------------------------------------- /tasks/pre_remediation_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Pre Audit Setup | Setup the LE audit 4 | when: 5 | - setup_audit 6 | tags: 7 | - setup_audit 8 | ansible.builtin.include_tasks: 9 | file: LE_audit_setup.yml 10 | 11 | - name: Pre Audit Setup | Ensure {{ audit_conf_dir }} exists 12 | ansible.builtin.file: 13 | path: "{{ audit_conf_dir }}" 14 | state: directory 15 | mode: '0755' 16 | 17 | - name: Pre Audit Setup | If using git for content set up 18 | when: 19 | - audit_content == 'git' 20 | block: 21 | - name: Pre Audit Setup | Install git 22 | ansible.builtin.package: 23 | name: git 24 | state: present 25 | 26 | - name: Pre Audit Setup | Retrieve audit content files from git 27 | ansible.builtin.git: 28 | repo: "{{ audit_file_git }}" 29 | dest: "{{ audit_conf_dir }}" 30 | version: "{{ audit_git_version }}" 31 | 32 | - name: Pre Audit Setup | Copy to audit content files to server 33 | when: 34 | - audit_content == 'copy' 35 | ansible.builtin.copy: 36 | src: "{{ audit_conf_source }}" 37 | dest: "{{ audit_conf_dest }}" 38 | mode: preserve 39 | 40 | - name: Pre Audit Setup | Unarchive audit content files on server 41 | when: 42 | - audit_content == 'archive' 43 | ansible.builtin.unarchive: 44 | src: "{{ audit_conf_source }}" 45 | dest: "{{ audit_conf_dest }}" 46 | 47 | - name: Pre Audit Setup | Get audit content from url 48 | when: 49 | - audit_content == 'get_url' 50 | ansible.builtin.unarchive: 51 | src: "{{ audit_conf_source }}" 52 | dest: "{{ audit_conf_dest }}/{{ benchmark }}-Audit" 53 | remote_src: "{{ ( audit_conf_source is contains ('http'))| ternary(true, false ) }}" 54 | extra_opts: "{{ (audit_conf_source is contains ('github')) | ternary('--strip-components=1', [] ) }}" 55 | 56 | - name: Pre Audit Setup | Check Goss is available 57 | when: 58 | - run_audit 59 | block: 60 | - name: Pre Audit Setup | Check for goss file 61 | ansible.builtin.stat: 62 | path: "{{ audit_bin }}" 63 | register: discovered_goss_available 64 | 65 | - name: Pre Audit Setup | If audit ensure goss is available 66 | ansible.builtin.assert: 67 | that: discovered_goss_available.stat.exists 68 | msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" 69 | 70 | - name: Pre Audit Setup | Copy ansible default vars values to test audit 71 | when: 72 | - run_audit 73 | tags: 74 | - goss_template 75 | - run_audit 76 | ansible.builtin.template: 77 | src: ansible_vars_goss.yml.j2 78 | dest: "{{ audit_vars_path }}" 79 | mode: '0600' 80 | 81 | - name: Pre Audit | Run pre_remediation {{ benchmark }} audit 82 | ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ pre_audit_outfile }} -g \"{{ group_names }}\"" 83 | changed_when: true 84 | environment: 85 | AUDIT_BIN: "{{ audit_bin }}" 86 | AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" 87 | AUDIT_FILE: goss.yml 88 | 89 | - name: Pre Audit | Capture audit data if json format 90 | when: 91 | - audit_format == "json" 92 | block: 93 | - name: Pre Audit | Capture audit data if json format 94 | ansible.builtin.shell: grep -E '\"summary-line.*Count:.*Failed' "{{ pre_audit_outfile }}" | cut -d'"' -f4 95 | register: pre_audit_summary 96 | changed_when: false 97 | 98 | - name: Pre Audit | Set Fact for audit summary 99 | ansible.builtin.set_fact: 100 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 101 | 102 | - name: Pre Audit | Capture audit data if documentation format 103 | when: 104 | - audit_format == "documentation" 105 | block: 106 | - name: Pre Audit | Capture audit data if documentation format 107 | ansible.builtin.shell: tail -2 "{{ pre_audit_outfile }}" | tac | tr '\n' ' ' 108 | register: pre_audit_summary 109 | changed_when: false 110 | 111 | - name: Pre Audit | Set Fact for audit summary 112 | ansible.builtin.set_fact: 113 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 114 | 115 | - name: Audit_Only | Run Audit Only 116 | when: 117 | - audit_only 118 | ansible.builtin.import_tasks: 119 | file: audit_only.yml 120 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.1.1 | PATCH | Ensure /tmp is a separate partition" 4 | when: 5 | - rhel8cis_rule_1_1_2_1_1 6 | - "'/tmp' not in mount_names" 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - automated 11 | - audit 12 | - mounts 13 | - NIST800-53R5_AC-3 14 | - NIST800-53R5_MP-2 15 | - rule_1.1.2.1.1 16 | vars: 17 | warn_control_id: '1.1.2.1.1' 18 | required_mount: '/tmp' 19 | block: 20 | - name: "1.1.2.1.1 | PATCH | Ensure /tmp is a separate partition | Absent" 21 | ansible.builtin.debug: 22 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 23 | 24 | - name: "1.1.2.1.1 | PATCH | Ensure /tmp is a separate partition | Present" 25 | ansible.builtin.import_tasks: 26 | file: warning_facts.yml 27 | 28 | # via fstab 29 | - name: | 30 | "1.1.2.1.2 | PATCH | Ensure nodev option set on /tmp partition" 31 | "1.1.2.1.3 | PATCH | Ensure nosuid option set on /tmp partition" 32 | "1.1.2.1.4 | PATCH | Ensure noexec option set on /tmp partition" 33 | when: 34 | - item.mount == "/tmp" 35 | - not rhel8cis_tmp_svc 36 | - rhel8cis_rule_1_1_2_1_2 or 37 | rhel8cis_rule_1_1_2_1_3 or 38 | rhel8cis_rule_1_1_2_1_4 39 | tags: 40 | - level1-server 41 | - level1-workstation 42 | - automated 43 | - patch 44 | - mounts 45 | - NIST800-53R5_AC-3 46 | - NIST800-53R5_MP-2 47 | - rule_1.1.2.1.2 48 | - rule_1.1.2.1.3 49 | - rule_1.1.2.1.4 50 | ansible.posix.mount: 51 | name: /tmp 52 | src: "{{ item.device }}" 53 | fstype: "{{ item.fstype }}" 54 | state: present 55 | opts: defaults,{% if rhel8cis_rule_1_1_2_1_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_1_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_1_4 %}noexec{% endif %} 56 | notify: Remount_tmp 57 | loop: "{{ ansible_facts.mounts }}" 58 | loop_control: 59 | label: "{{ item.device }}" 60 | 61 | # via systemd 62 | - name: | 63 | "1.1.2.1.1 | PATCH | Ensure /tmp is configured" 64 | "1.1.2.1.2 | PATCH | Ensure nodev option set on /tmp partition" 65 | "1.1.2.1.3 | PATCH | Ensure nosuid option set on /tmp partition" 66 | "1.1.2.1.4 | PATCH | Ensure noexec option set on /tmp partition" 67 | when: 68 | - rhel8cis_tmp_svc 69 | - rhel8cis_rule_1_1_2_1_1 or 70 | rhel8cis_rule_1_1_2_1_2 or 71 | rhel8cis_rule_1_1_2_1_3 or 72 | rhel8cis_rule_1_1_2_1_4 73 | tags: 74 | - level1-server 75 | - level1-workstation 76 | - patch 77 | - mounts 78 | - NIST800-53R5_AC-3 79 | - NIST800-53R5_MP-2 80 | - rule_1.1.2.1.1 81 | - rule_1.1.2.1.2 82 | - rule_1.1.2.1.3 83 | - rule_1.1.2.1.4 84 | notify: Systemd_restart_tmp.mount 85 | ansible.builtin.template: 86 | src: etc/systemd/system/tmp.mount.j2 87 | dest: /etc/systemd/system/tmp.mount 88 | owner: root 89 | group: root 90 | mode: '0644' 91 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.2.1 | PATCH | Ensure /dev/shm is a separate partition" 4 | when: 5 | - rhel8cis_rule_1_1_2_2_1 6 | - "'/tmp' not in mount_names" 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - automated 11 | - audit 12 | - mounts 13 | - NIST800-53R5_CM-7 14 | - rule_1.1.2.2.1 15 | vars: 16 | warn_control_id: '1.1.2.2.1' 17 | required_mount: '/dev/shm' 18 | block: 19 | 20 | - name: "1.1.2.2.1 | PATCH | Ensure /dev/shm is a separate partition | Absent" 21 | when: prelim_dev_shm_present is undefined 22 | ansible.builtin.debug: 23 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 24 | 25 | - name: "1.1.2.2.1 | PATCH | Ensure /dev/shm is a separate partition | Present" 26 | when: prelim_dev_shm_present is undefined 27 | ansible.builtin.import_tasks: 28 | file: warning_facts.yml 29 | 30 | - name: | 31 | "1.1.2.2.2 | PATCH | Ensure nodev option set on /dev/shm partition 32 | 1.1.2.2.3 | PATCH | Ensure nosuid option set on /dev/shm partition 33 | 1.1.2.2.4 | PATCH | Ensure noexec option set on /dev/shm partition" 34 | when: 35 | - prelim_dev_shm_present is defined 36 | - rhel8cis_rule_1_1_2_2_2 or 37 | rhel8cis_rule_1_1_2_2_3 or 38 | rhel8cis_rule_1_1_2_2_4 39 | tags: 40 | - level1-server 41 | - level1-workstation 42 | - automated 43 | - patch 44 | - mounts 45 | - NIST800-53R5_AC-3 46 | - NIST800-53R5_MP-2 47 | - rule_1.1.2.2.1 48 | - rule_1.1.2.2.2 49 | - rule_1.1.2.2.3 50 | notify: Change_requires_reboot 51 | ansible.posix.mount: 52 | name: /dev/shm 53 | src: tmpfs 54 | fstype: tmpfs 55 | state: mounted 56 | opts: defaults,{% if rhel8cis_rule_1_1_2_2_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_2_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_2_4 %}noexec{% endif %} 57 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.3.1 | AUDIT | Ensure separate partition exists for /home" 4 | when: 5 | - rhel8cis_rule_1_1_2_3_1 6 | - "'/home' not in mount_names" 7 | tags: 8 | - level2-server 9 | - level2-workstation 10 | - automated 11 | - audit 12 | - mounts 13 | - rule_1.1.2.3.1 14 | - NIST800-53R5_CM-7 15 | - skip_ansible_lint 16 | vars: 17 | warn_control_id: '1.1.2.3.1' 18 | required_mount: '/home' 19 | block: 20 | - name: "1.1.2.3.1 | AUDIT | Ensure separate partition exists for /home | Warn if partition is absent" 21 | ansible.builtin.debug: 22 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 23 | register: discovered_home_mount_absent 24 | changed_when: discovered_home_mount_absent.skipped is undefined 25 | 26 | - name: "1.1.2.3.1 | AUDIT | Ensure separate partition exists for /home | Present" 27 | ansible.builtin.import_tasks: warning_facts.yml 28 | 29 | # skips if mount is absent 30 | - name: | 31 | "1.1.2.3.2 | PATCH | Ensure nodev option set on /home partition 32 | 1.1.2.3.3 | PATCH | Ensure nosuid option set on /home partition 33 | when: 34 | - "'/home' in mount_names" 35 | - item.mount == "/home" 36 | - rhel8cis_rule_1_1_2_3_2 or 37 | rhel8cis_rule_1_1_2_3_3 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - automated 42 | - patch 43 | - mounts 44 | - rule_1.1.2.3.2 45 | - rule_1.1.2.3.3 46 | - NIST800-53R5_AC-3 47 | - NIST800-53R5_MP-2 48 | notify: Change_requires_reboot 49 | ansible.posix.mount: 50 | name: /home 51 | src: "{{ item.device }}" 52 | fstype: "{{ item.fstype }}" 53 | state: present 54 | opts: defaults,{% if rhel8cis_rule_1_1_2_3_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_3_3 %}nosuid{% endif %} 55 | loop: "{{ ansible_facts.mounts }}" 56 | loop_control: 57 | label: "{{ item.device }}" 58 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.4.1 | AUDIT | Ensure separate partition exists for /var" 4 | block: 5 | - name: "1.1.2.4.1 | AUDIT | Ensure separate partition exists for /var | Warn if partition is absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | register: discovered_var_mount_absent 9 | changed_when: discovered_var_mount_absent.skipped is undefined 10 | 11 | - name: "1.1.2.4.1 | AUDIT | Ensure separate partition exists for /var | Present" 12 | ansible.builtin.import_tasks: 13 | file: warning_facts.yml 14 | vars: 15 | warn_control_id: '1.1.2.4.1' 16 | required_mount: '/var' 17 | when: 18 | - "'/var' not in mount_names" 19 | - rhel8cis_rule_1_1_2_4_1 20 | tags: 21 | - level2-server 22 | - level2-workstation 23 | - automated 24 | - patch 25 | - mounts 26 | - NIST800-53R5_CM-7 27 | - rule_1.1.2.4.1 28 | 29 | # skips if mount is absent 30 | - name: | 31 | "1.1.2.4.2 | PATCH | Ensure nodev option set on /var partition" 32 | "1.1.2.4.3 | PATCH | Ensure nosuid option set on /var partition" 33 | when: 34 | - "'/var' in mount_names" 35 | - item.mount == "/var" 36 | - rhel8cis_rule_1_1_2_4_2 or 37 | rhel8cis_rule_1_1_2_4_3 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - automated 42 | - patch 43 | - mounts 44 | - skip_ansible_lint 45 | - rule_1.1.2.4.2 46 | - rule_1.1.2.4.3 47 | - NIST800-53R5_AC-3 48 | - NIST800-53R5_MP-2 49 | notify: Change_requires_reboot 50 | ansible.posix.mount: 51 | name: /var 52 | src: "{{ item.device }}" 53 | fstype: "{{ item.fstype }}" 54 | state: present 55 | opts: defaults,{% if rhel8cis_rule_1_1_2_4_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_4_3 %}nosuid{% endif %} 56 | loop: "{{ ansible_facts.mounts }}" 57 | loop_control: 58 | label: "{{ item.device }}" 59 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.5.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Skips if mount is absent 4 | - name: "1.1.2.5.1 | AUDIT | Ensure separate partition exists for /var/tmp" 5 | when: 6 | - rhel8cis_rule_1_1_2_5_1 7 | - "'/var/tmp' not in mount_names" 8 | tags: 9 | - level2-server 10 | - level2-workstation 11 | - automated 12 | - audit 13 | - mounts 14 | - NIST800-53R5_CM-7 15 | - rule_1.1.2.5.1 16 | vars: 17 | warn_control_id: '1.1.2.5.1' 18 | required_mount: '/var/tmp' 19 | block: 20 | - name: "1.1.2.5.1 | AUDIT | Ensure separate partition exists for /var/tmp | Warn if partition is absent" 21 | ansible.builtin.debug: 22 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 23 | register: discovered_var_tmp_mount_absent 24 | changed_when: discovered_var_tmp_mount_absent.skipped is undefined 25 | 26 | - name: "1.1.2.5.1 | AUDIT | Ensure separate partition exists for /var/tmp | Present" 27 | ansible.builtin.import_tasks: 28 | file: warning_facts.yml 29 | 30 | # skips if mount is absent 31 | - name: | 32 | "1.1.2.5.2 | PATCH | Ensure nodev option set on /var/tmp partition" 33 | "1.1.2.5.3 | PATCH | Ensure nosuid option set on /var/tmp partition" 34 | "1.1.2.5.4 | PATCH | Ensure noexec option set on /var/tmp partition" 35 | when: 36 | - "'/var/tmp' in mount_names" 37 | - item.mount == "/var/tmp" 38 | - rhel8cis_rule_1_1_2_5_2 or 39 | rhel8cis_rule_1_1_2_5_3 or 40 | rhel8cis_rule_1_1_2_5_4 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - automated 45 | - patch 46 | - mounts 47 | - skip_ansible_lint 48 | - rule_1.1.2.5.2 49 | - rule_1.1.2.5.3 50 | - rule_1.1.2.5.4 51 | - NIST800-53R5_AC-3 52 | - NIST800-53R5_MP-2 53 | notify: Change_requires_reboot 54 | ansible.posix.mount: 55 | name: /var/tmp 56 | src: "{{ item.device }}" 57 | fstype: "{{ item.fstype }}" 58 | state: present 59 | opts: defaults,{% if rhel8cis_rule_1_1_2_5_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_5_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_5_4 %}noexec{% endif %} 60 | loop: "{{ ansible_facts.mounts }}" 61 | loop_control: 62 | label: "{{ item.device }}" 63 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.6.1 | AUDIT | Ensure separate partition exists for /var/log" 4 | when: 5 | - rhel8cis_rule_1_1_2_6_1 6 | - "'/var/log' not in mount_names" 7 | tags: 8 | - level2-server 9 | - level2-workstation 10 | - automated 11 | - audit 12 | - mounts 13 | - rule_1.1.2.6.1 14 | - NIST800-53R5_CM-7 15 | vars: 16 | warn_control_id: '1.1.2.6.1' 17 | required_mount: '/var/log' 18 | block: 19 | - name: "1.1.2.6.1 | AUDIT | Ensure separate partition exists for /var/log | Warn if partition is absent" 20 | ansible.builtin.debug: 21 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 22 | register: discovered_var_log_mount_absent 23 | changed_when: discovered_var_log_mount_absent.skipped is undefined 24 | 25 | - name: "1.1.2.6.1 | AUDIT | Ensure separate partition exists for /var/log | Present" 26 | ansible.builtin.import_tasks: 27 | file: warning_facts.yml 28 | 29 | # skips if mount is absent 30 | - name: | 31 | "1.1.2.6.2 | PATCH | Ensure nodev option set on /var/log partition" 32 | "1.1.2.6.3 | PATCH | Ensure nosuid option set on /var/log partition" 33 | "1.1.2.6.4 | PATCH | Ensure noexec option set on /var/log partition" 34 | when: 35 | - "'/var/log' in mount_names" 36 | - item.mount == "/var/log" 37 | - rhel8cis_rule_1_1_2_6_2 or 38 | rhel8cis_rule_1_1_2_6_3 or 39 | rhel8cis_rule_1_1_2_6_4 40 | tags: 41 | - level1-server 42 | - level1-workstation 43 | - automated 44 | - patch 45 | - mounts 46 | - skip_ansible_lint 47 | - rule_1.1.2.6.2 48 | - rule_1.1.2.6.3 49 | - rule_1.1.2.6.4 50 | - NIST800-53R5_AC-3 51 | - NIST800-53R5_MP-2 52 | notify: Change_requires_reboot 53 | ansible.posix.mount: 54 | name: /var/log 55 | src: "{{ item.device }}" 56 | fstype: "{{ item.fstype }}" 57 | state: present 58 | opts: defaults,{% if rhel8cis_rule_1_1_2_6_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_6_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_6_4 %}noexec{% endif %} 59 | loop: "{{ ansible_facts.mounts }}" 60 | loop_control: 61 | label: "{{ item.device }}" 62 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.7.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.7.1 | AUDIT | Ensure separate partition exists for /var/log/audit" 4 | when: 5 | - rhel8cis_rule_1_1_2_7_1 6 | - "'/var/log/audit' not in mount_names" 7 | tags: 8 | - level2-server 9 | - level2-workstation 10 | - automated 11 | - audit 12 | - mounts 13 | - rule_1.1.2.7.1 14 | - NIST800-53R5_CM-7 15 | vars: 16 | warn_control_id: '1.1.2.7.1' 17 | required_mount: '/var/log/audit' 18 | block: 19 | - name: "1.1.2.7.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Warn if partition is absent" 20 | ansible.builtin.debug: 21 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 22 | register: discovered_var_log_audit_mount_absent 23 | changed_when: discovered_var_log_audit_mount_absent.skipped is undefined 24 | 25 | - name: "1.1.2.7.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Present" 26 | ansible.builtin.import_tasks: 27 | file: warning_facts.yml 28 | 29 | # skips if mount is absent 30 | - name: | 31 | "1.1.2.7.2 | PATCH | Ensure nodev option set on /var/log/audit partition" 32 | "1.1.2.7.3 | PATCH | Ensure nosuid option set on /var/log/audit partition" 33 | "1.1.2.7.4 | PATCH | Ensure noexec option set on /var/log/audit partition" 34 | when: 35 | - "'/var/log/audit' in mount_names" 36 | - item.mount == "/var/log/audit" 37 | - rhel8cis_rule_1_1_2_7_2 or 38 | rhel8cis_rule_1_1_2_7_3 or 39 | rhel8cis_rule_1_1_2_7_4 40 | tags: 41 | - level1-server 42 | - level1-workstation 43 | - automated 44 | - patch 45 | - mounts 46 | - rule_1.1.2.7.2 47 | - rule_1.1.2.7.3 48 | - rule_1.1.2.7.4 49 | - NIST800-53R5_AC-3 50 | - NIST800-53R5_MP-2 51 | notify: Change_requires_reboot 52 | ansible.posix.mount: 53 | name: /var/log/audit 54 | src: "{{ item.device }}" 55 | fstype: "{{ item.fstype }}" 56 | state: present 57 | opts: defaults,{% if rhel8cis_rule_1_1_2_7_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_7_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_7_4 %}noexec{% endif %} 58 | loop: "{{ ansible_facts.mounts }}" 59 | loop_control: 60 | label: "{{ item.device }}" 61 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.2.1 | AUDIT | Ensure GPG keys are configured" 4 | when: 5 | - rhel8cis_rule_1_2_1 6 | - ansible_distribution == "RedHat" or 7 | ansible_distribution == "Rocky" or 8 | ansible_distribution == "AlmaLinux" or 9 | ansible_distribution == "Oracle Linux" 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - manual 14 | - patch 15 | - NIST800-53R5_SI-2 16 | - rule_1.2.1 17 | 18 | ansible.builtin.shell: "PKG=`rpm -qf {{ rpm_gpg_key }}` && rpm -q --queryformat \"%{PACKAGER} %{SIGPGP:pgpsig}\\n\" \"${PKG}\" | grep \"^{{ rpm_packager }}.*Key.ID.{{ rpm_key }}\"" 19 | changed_when: false 20 | 21 | - name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated" 22 | when: 23 | - rhel8cis_rule_1_2_2 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - automated 28 | - patch 29 | - NIST800-53R5_SI-2 30 | - rule_1.2.2 31 | block: 32 | - name: "1.2.2 | AUDIT | Ensure gpgcheck is globally activated | Find repos" 33 | ansible.builtin.find: 34 | paths: /etc/yum.repos.d 35 | patterns: "*.repo" 36 | register: discovered_yum_repos 37 | changed_when: false 38 | 39 | - name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated | Update yum.repos" 40 | ansible.builtin.replace: 41 | name: "{{ item.path }}" 42 | regexp: ^gpgcheck\s*=\s*0 43 | replace: "gpgcheck=1" 44 | loop: "{{ discovered_yum_repos.files }}" 45 | loop_control: 46 | label: "{{ item.path }}" 47 | 48 | - name: "1.2.2 | PATCH | Ensure gpgcheck is globally activated | Update dnf.conf" 49 | ansible.builtin.lineinfile: 50 | path: /etc/dnf/dnf.conf 51 | regexp: ^(#|)gpgcheck\s*= 52 | line: gpgcheck=1 53 | 54 | - name: "1.2.3 | PATCH | Ensure repo_gpgcheck is globally activated" 55 | when: 56 | - rhel8cis_rule_1_2_3 57 | tags: 58 | - level2-server 59 | - level2-workstation 60 | - automated 61 | - patch 62 | - NIST800-53R5_SI-2 63 | - rule_1.2.3 64 | block: 65 | - name: "1.2.3 | AUDIT | Ensure repo_gpgcheck is globally activated | Find repos" 66 | ansible.builtin.find: 67 | paths: /etc/yum.repos.d 68 | patterns: "*.repo" 69 | register: discovered_yum_repos 70 | changed_when: false 71 | 72 | - name: "1.2.3 | PATCH | Ensure repo_gpgcheck is globally activated | Update yum.repos" 73 | ansible.builtin.replace: 74 | name: "{{ item.path }}" 75 | regexp: ^repo_gpgcheck\s*=\s*0" 76 | replace: "repo_gpgcheck=1" 77 | loop: "{{ discovered_yum_repos.files }}" 78 | loop_control: 79 | label: "{{ item.path }}" 80 | 81 | - name: "1.2.3 | PATCH | Ensure repo_gpgcheck is globally activated | Update dnf.conf" 82 | ansible.builtin.lineinfile: 83 | path: /etc/dnf/dnf.conf 84 | regexp: ^(#|)repo_gpgcheck\s*= 85 | line: repo_gpgcheck=1 86 | 87 | - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured" 88 | when: 89 | - rhel8cis_rule_1_2_4 90 | tags: 91 | - level1-server 92 | - level1-workstation 93 | - manual 94 | - audit 95 | - NIST800-53R5_SI-2 96 | - rule_1.2.4 97 | vars: 98 | warn_control_id: '1.2.4' 99 | block: 100 | - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Get repo list" 101 | ansible.builtin.shell: dnf repolist 102 | changed_when: false 103 | failed_when: false 104 | register: discovered_dnf_configured 105 | check_mode: false 106 | 107 | - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Display repo list" 108 | ansible.builtin.debug: 109 | msg: 110 | - "Warning!! Below are the configured repos. Please review and make sure all align with site policy" 111 | - "{{ discovered_dnf_configured.stdout_lines }}" 112 | 113 | - name: "1.2.4 | AUDIT | Ensure package manager repositories are configured | Warn Count" 114 | ansible.builtin.import_tasks: 115 | file: warning_facts.yml 116 | 117 | - name: "1.2.5 | PATCH | Ensure updates, patches, and additional security software are installed" 118 | when: 119 | - rhel8cis_rule_1_2_5 120 | - not system_is_ec2 121 | tags: 122 | - level1-server 123 | - level1-workstation 124 | - automated 125 | - patch 126 | - rule_1.2_5 127 | notify: Change_requires_reboot 128 | block: 129 | - name: "1.2.5 | PATCH | Ensure updates, patches, and additional security software are installed | Patch" 130 | ansible.builtin.package: 131 | name: "*" 132 | state: latest 133 | 134 | - name: "1.2.5 | PATCH | Ensure updates, patches, and additional security software are installed | update_facts" 135 | ansible.builtin.package_facts: 136 | manager: auto 137 | 138 | - name: "1.2.5 | PATCH | Ensure updates, patches, and additional security software are installed | limit kernels" 139 | when: 140 | - rhel8cis_rule_1_2_5 141 | - rhel8cis_apply_installed_kernel_limit 142 | tags: 143 | - level1-server 144 | - level1-workstation 145 | - automated 146 | - patch 147 | - kernel_limit 148 | - rule_1.2.5 149 | ansible.builtin.lineinfile: 150 | path: /etc/dnf/dnf.conf 151 | regexp: ^installonly_limit= 152 | line: installonly_limit={{ rhel8cis_installed_kernel_limit }} 153 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.3.1 | PATCH | Ensure bootloader password is set" 4 | ansible.builtin.copy: 5 | dest: "{{ rhel8cis_boot_path }}user.cfg" # noqa template-instead-of-copy 6 | content: "GRUB2_PASSWORD={{ rhel8cis_bootloader_password_hash }}" 7 | owner: root 8 | group: root 9 | mode: '0600' 10 | notify: Rebuild_grub 11 | when: 12 | - rhel8cis_set_boot_pass 13 | - rhel8cis_rule_1_3_1 14 | tags: 15 | - level1-server 16 | - level1-workstation 17 | - automated 18 | - grub 19 | - patch 20 | - NIST800-53R5_AC-3 21 | - NIST800-53R5_MP-2 22 | - rule_1.3.1 23 | 24 | - name: "1.3.2 | PATCH | Ensure permissions on bootloader config are configured" 25 | when: 26 | - rhel8cis_rule_1_3_2 27 | - prelim_grub_cfg.stat.exists 28 | - prelim_grub_cfg.stat.islnk 29 | tags: 30 | - level1-server 31 | - level1-workstation 32 | - automated 33 | - grub 34 | - patch 35 | - NIST800-53R5_AC-3 36 | - NIST800-53R5_MP-2 37 | - rule_1.3.2 38 | block: 39 | - name: "1.3.2 | PATCH | Ensure permissions on bootloader config are configured | BIOS" 40 | when: rhel8cis_legacy_boot 41 | ansible.builtin.file: 42 | path: "{{ rhel8cis_boot_path }}" 43 | owner: root 44 | group: root 45 | mode: '0600' 46 | recurse: true 47 | 48 | - name: "1.3.2 | PATCH | Ensure permissions on bootloader config are configured | EFI" 49 | when: not rhel8cis_legacy_boot 50 | ansible.builtin.file: 51 | path: "{{ rhel8cis_boot_path }}" 52 | owner: root 53 | group: root 54 | mode: '0700' 55 | recurse: true 56 | 57 | - name: "1.3.2 | PATCH | Ensure permissions on bootloader config are configured | UEFI" 58 | when: 59 | - not rhel8cis_legacy_boot 60 | - item.mount == "/boot/efi" 61 | notify: Change_requires_reboot 62 | ansible.posix.mount: 63 | name: /boot/efi 64 | src: "UUID={{ item.uuid }}" 65 | fstype: vfat 66 | state: present 67 | opts: defaults,umask=0027,fmask=0077,uid=0,gid=0 68 | passno: '0' 69 | loop: "{{ ansible_facts.mounts }}" 70 | loop_control: 71 | label: "{{ item.mount }}" 72 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.4.1 | PATCH | Ensure address space layout randomization (ASLR) is enabled" 4 | when: 5 | - rhel8cis_rule_1_4_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - sysctl 12 | - NIST800-53R5_CM-6 13 | - rule_1.4.1 14 | notify: Reload_sysctl 15 | ansible.builtin.lineinfile: 16 | path: /etc/sysctl.d/60-kernel_sysctl.conf 17 | regexp: kernel.randomize_va_space 18 | line: kernel.randomize_va_space = 2 19 | create: true 20 | 21 | - name: "1.4.2 | PATCH | Ensure ptrace_scope is restricted" 22 | when: 23 | - rhel8cis_rule_1_4_2 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - automated 28 | - patch 29 | - sysctl 30 | - NIST800-53R5_CM-6 31 | - rule_1.4.2 32 | notify: Reload_sysctl 33 | block: 34 | - name: Ensure ptrace_scope is restricted | Set file 35 | ansible.builtin.lineinfile: 36 | path: /etc/sysctl.d/60-kernel_sysctl.conf 37 | regexp: kernel.yama.ptrace_scope 38 | line: kernel.yama.ptrace_scope = 1 39 | create: true 40 | 41 | - name: Ensure ptrace_scope is restricted | comment out other entry 42 | ansible.builtin.lineinfile: 43 | path: /lib/sysctl.d/10-default-yama-scope.conf 44 | backrefs: true 45 | regexp: ^(\s*kernel.yama.ptrace_scope.*) 46 | line: '# \1' 47 | create: true 48 | 49 | - name: "1.4.3 | PATCH | Ensure core dump backtraces are disabled" 50 | tags: 51 | - level1-server 52 | - level1-workstation 53 | - automated 54 | - patch 55 | - sysctl 56 | - NIST800-53R5_CM-6 57 | - rule_1.4.3 58 | ansible.builtin.lineinfile: 59 | path: /etc/systemd/coredump.conf 60 | regexp: 'ProcessSizeMax=' 61 | line: 'ProcessSizeMax=0' 62 | create: true 63 | when: 64 | - rhel8cis_rule_1_4_3 65 | 66 | - name: "1.4.4 | PATCH | Ensure core dump storage is disabled" 67 | when: 68 | - rhel8cis_rule_1_4_4 69 | - prelim_systemd_coredump.stat.exists 70 | tags: 71 | - level1-server 72 | - level1-workstation 73 | - automated 74 | - patch 75 | - rule_1.4.4 76 | notify: Systemd_daemon_reload 77 | ansible.builtin.lineinfile: 78 | path: /etc/systemd/coredump.conf 79 | regexp: 'Storage=' 80 | line: 'Storage=none' 81 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.5.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.5.1.1 | PATCH | Ensure SELinux is installed" 4 | when: 5 | - rhel8cis_rule_1_5_1_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_AC-3 12 | - NIST800-53R5_MP-2 13 | - rule_1.5.1.1 14 | ansible.builtin.package: 15 | name: libselinux 16 | state: present 17 | 18 | - name: "1.5.1.2 | PATCH | Ensure SELinux is not disabled in bootloader configuration" 19 | when: 20 | - rhel8cis_rule_1_5_1_2 21 | tags: 22 | - level1-server 23 | - level1-workstation 24 | - patch 25 | - NIST800-53R5_AC-3 26 | - NIST800-53R5_MP-2 27 | - rule_1.5.1.2 28 | notify: Rebuild_grub 29 | ansible.builtin.replace: 30 | path: /etc/default/grub 31 | regexp: '(selinux|enforcing)\s*=\s*0\s*' 32 | replace: '' 33 | 34 | # State set to enforcing because control 1.5.1.5 requires enforcing to be set 35 | - name: "1.5.1.3 | PATCH | Ensure SELinux policy is configured" 36 | when: 37 | - rhel8cis_rule_1_5_1_3 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - automated 42 | - selinux 43 | - patch 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | - rule_1.5.1.3 47 | notify: Change_requires_reboot 48 | ansible.posix.selinux: 49 | conf: /etc/selinux/config 50 | policy: "{{ rhel8cis_selinux_policy }}" 51 | state: "{{ rhel8cis_selinux_state }}" 52 | 53 | # State set to enforcing because control 1.6.1.5 requires enforcing to be set 54 | - name: "1.5.1.4 | PATCH | Ensure the SELinux mode is not disabled" 55 | when: 56 | - rhel8cis_rule_1_5_1_4 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - auotmated 61 | - selinux 62 | - patch 63 | - NIST800-53R5_AC-3 64 | - NIST800-53R5_MP-2 65 | - rule_1.5.1.4 66 | ansible.posix.selinux: 67 | conf: /etc/selinux/config 68 | policy: "{{ rhel8cis_selinux_policy }}" 69 | state: "{{ rhel8cis_selinux_state }}" 70 | 71 | - name: "1.5.1.5 | PATCH | Ensure the SELinux state is enforcing" 72 | when: 73 | - rhel8cis_rule_1_5_1_5 74 | tags: 75 | - level2-server 76 | - level2-workstation 77 | - automated 78 | - selinux 79 | - patch 80 | - NIST800-53R5_AC-3 81 | - NIST800-53R5_MP-2 82 | - rule_1.5.1.5 83 | ansible.posix.selinux: 84 | conf: /etc/selinux/config 85 | policy: "{{ rhel8cis_selinux_policy }}" 86 | state: enforcing 87 | 88 | - name: "1.5.1.6 | AUDIT | Ensure no unconfined services exist" 89 | when: 90 | - rhel8cis_rule_1_5_1_6 91 | tags: 92 | - level1-server 93 | - level1-workstation 94 | - automated 95 | - audit 96 | - services 97 | - NIST800-53R5_AC-3 98 | - NIST800-53R5_MP-2 99 | - rule_1.5.1.6 100 | vars: 101 | warn_control_id: '1.5.1.6' 102 | block: 103 | - name: "1.5.1.6 | AUDIT | Ensure no unconfined services exist | Find the unconfined services" 104 | ansible.builtin.shell: ps -eZ | grep unconfined_service_t | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }' 105 | register: discovered_unconf_services 106 | failed_when: false 107 | changed_when: false 108 | 109 | - name: "1.5.1.6 | WARNING | Ensure no unconfined services exist | Message on unconfined services" 110 | when: 111 | - discovered_unconf_services is defined 112 | - discovered_unconf_services.stdout | length > 0 113 | ansible.builtin.debug: 114 | msg: "Warning!! You have unconfined services: {{ discovered_unconf_services.stdout_lines }}" 115 | 116 | - name: "1.5.1.6 | WARNING | Ensure no unconfined services exist | warning count" 117 | when: 118 | - discovered_unconf_services is defined 119 | - discovered_unconf_services.stdout | length > 0 120 | ansible.builtin.import_tasks: 121 | file: warning_facts.yml 122 | 123 | - name: "1.5.1.7 | PATCH | Ensure the MCS Translation Service (mcstrans) is not installed" 124 | when: 125 | - rhel8cis_rule_1_5_1_7 126 | tags: 127 | - level1-server 128 | - level1-workstation 129 | - automated 130 | - patch 131 | - NIST800-53R5_AC-3 132 | - NIST800-53R5_MP-2 133 | - rule_1.5.1.7 134 | ansible.builtin.package: 135 | name: mcstrans 136 | state: absent 137 | 138 | - name: "1.5.1.8 | PATCH | Ensure SETroubleshoot is not installed" 139 | ansible.builtin.package: 140 | name: setroubleshoot 141 | state: absent 142 | when: 143 | - rhel8cis_rule_1_5_1_8 144 | - "'setroubleshoot' in ansible_facts.packages" 145 | tags: 146 | - level1-server 147 | - automated 148 | - selinux 149 | - patch 150 | - NIST800-53R5_AC-3 151 | - NIST800-53R5_MP-2 152 | - rule_1.5.1.8 153 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.6.1 | PATCH | Ensure system-wide crypto policy is not legacy" 4 | when: 5 | - rhel8cis_rule_1_6_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_SC-8 12 | - rule_1.6.1 13 | notify: Change_requires_reboot 14 | block: 15 | - name: "1.6.1 | PATCH | Ensure system-wide crypto policy is not legacy | set_fact" 16 | ansible.builtin.set_fact: 17 | rhel8cis_full_crypto_policy: "{{ rhel8cis_crypto_policy }}{% if rhel8cis_crypto_policy_module | length > 0 %}:{{ rhel8cis_crypto_policy_module }}{% endif %}" 18 | 19 | - name: "1.6.1 | PATCH | Ensure system-wide crypto policy is not legacy" 20 | when: 21 | - rhel8cis_full_crypto_policy not in prelim_system_wide_crypto_policy.stdout 22 | ansible.builtin.shell: | 23 | update-crypto-policies --set "{{ rhel8cis_full_crypto_policy }}" 24 | update-crypto-policies 25 | 26 | - name: "1.6.2 | PATCH | Ensure system wide crypto policy disables sha1 hash and signature support" 27 | when: 28 | - rhel8cis_rule_1_6_2 29 | - "'NO-SHA1' not in prelim_system_wide_crypto_policy.stdout" 30 | tags: 31 | - level1-server 32 | - level1-workstation 33 | - automated 34 | - patch 35 | - NIST800-53R5_SC-8 36 | - rule_1.6.2 37 | notify: Change_requires_reboot 38 | block: 39 | - name: "1.6.2 | PATCH | Ensure system wide crypto policy disables sha1 hash and signature support | crypto_file" 40 | ansible.builtin.template: 41 | dest: /etc/crypto-policies/policies/modules/NO-SHA1.pmod 42 | src: etc/crypto-policies/policies/modules/NO-SHA1.pmod.j2 43 | 44 | - name: "1.6.2 | PATCH | Ensure system wide crypto policy disables sha1 hash and signature support | set crypto policy" 45 | ansible.builtin.shell: "update-crypto-policies --set {{ rhel8cis_full_crypto_policy }}:NO-SHA1" 46 | 47 | - name: "1.6.3 | PATCH | Ensure system wide crypto policy disables cbc for ssh" 48 | when: 49 | - rhel8cis_rule_1_6_3 50 | - "'NO-SSHCBC' not in prelim_system_wide_crypto_policy.stdout" 51 | tags: 52 | - level1-server 53 | - level1-workstation 54 | - automated 55 | - patch 56 | - NIST800-53R5_SC-8 57 | - rule_1.6.3 58 | notify: Change_requires_reboot 59 | block: 60 | - name: "1.6.3 | PATCH | Ensure system wide crypto policy disables cbc for ssh | crypto_file" 61 | ansible.builtin.template: 62 | dest: /etc/crypto-policies/policies/modules/NO-SSHCBC.pmod 63 | src: etc/crypto-policies/policies/modules/NO-SSHCBC.pmod.j2 64 | 65 | - name: "1.6.3 | PATCH | Ensure system wide crypto policy disables cbc for ssh | set crypto policy" 66 | ansible.builtin.shell: "update-crypto-policies --set {{ rhel8cis_full_crypto_policy }}:{% if rhel8cis_rule_1_6_2 %}NO-SHA1:{% endif %}NO-SSHCBC" 67 | 68 | - name: "1.6.4 | PATCH | Ensure system wide crypto policy disables macs less than 128 bits" 69 | when: 70 | - rhel8cis_rule_1_6_4 71 | - "'NO-WEAKMAC' not in prelim_system_wide_crypto_policy.stdout" 72 | tags: 73 | - level1-server 74 | - level1-workstation 75 | - automated 76 | - patch 77 | - NIST800-53R5_SC-8 78 | - rule_1.6.4 79 | notify: Change_requires_reboot 80 | block: 81 | - name: "1.6.4 | PATCH | Ensure system wide crypto policy disables cbc for ssh | crypto_file" 82 | ansible.builtin.template: 83 | dest: /etc/crypto-policies/policies/modules/NO-WEAKMAC.pmod 84 | src: etc/crypto-policies/policies/modules/NO-WEAKMAC.pmod.j2 85 | 86 | - name: "1.6.4 | PATCH | Ensure system wide crypto policy disables cbc for ssh | set crypto policy" 87 | ansible.builtin.shell: "update-crypto-policies --set {{ rhel8cis_full_crypto_policy }}:{% if rhel8cis_rule_1_6_2 %}NO-SHA1:{% endif %}{% if rhel8cis_rule_1_6_3 %}NO-SSHCBC:{% endif %}NO-WEAKMAC" 88 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.7.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.7.1 | PATCH | Ensure message of the day is configured properly" 4 | when: 5 | - rhel8cis_rule_1_7_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - banner 11 | - patch 12 | - NIST800-53R5_CM-1 13 | - NIST800-53R5_CM-3 14 | - NIST800-53R5_CM-6 15 | - rule_1.7.1 16 | ansible.builtin.template: 17 | src: etc/motd.j2 18 | dest: /etc/motd 19 | owner: root 20 | group: root 21 | mode: 'go-rx' 22 | 23 | - name: "1.7.2 | PATCH | Ensure local login warning banner is configured properly" 24 | when: 25 | - rhel8cis_rule_1_7_2 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - NIST800-53R5_CM-1 32 | - NIST800-53R5_CM-3 33 | - NIST800-53R5_CM-6 34 | - rule_1.7.2 35 | ansible.builtin.template: 36 | src: etc/issue.j2 37 | dest: /etc/issue 38 | owner: root 39 | group: root 40 | mode: 'go-rx' 41 | 42 | - name: "1.7.3 | PATCH | Ensure remote login warning banner is configured properly" 43 | when: 44 | - rhel8cis_rule_1_7_3 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - automated 49 | - banner 50 | - patch 51 | - NIST800-53R5_CM-1 52 | - NIST800-53R5_CM-3 53 | - NIST800-53R5_CM-6 54 | - rule_1.7.3 55 | ansible.builtin.template: 56 | src: etc/issue.net.j2 57 | dest: /etc/issue.net 58 | owner: root 59 | group: root 60 | mode: 'go-wx' 61 | 62 | - name: "1.7.4 | PATCH | Ensure access to /etc/motd is configured" 63 | when: 64 | - rhel8cis_rule_1_7_4 65 | tags: 66 | - level1-server 67 | - level1-workstation 68 | - automated 69 | - perms 70 | - patch 71 | - NIST800-53R5_AC-3 72 | - NIST800-53R5_MP-2 73 | - rule_1.7.4 74 | ansible.builtin.file: 75 | path: /etc/motd 76 | state: file 77 | owner: root 78 | group: root 79 | mode: 'go-wx' 80 | 81 | - name: "1.7.5 | PATCH | Ensure access to /etc/issue is configured" 82 | when: 83 | - rhel8cis_rule_1_7_5 84 | tags: 85 | - level1-server 86 | - level1-workstation 87 | - automated 88 | - perms 89 | - patch 90 | - NIST800-53R5_AC-3 91 | - NIST800-53R5_MP-2 92 | - rule_1.7.5 93 | ansible.builtin.file: 94 | path: /etc/issue 95 | state: file 96 | owner: root 97 | group: root 98 | mode: 'go-wx' 99 | 100 | - name: "1.7.6 | PATCH | Ensure access to /etc/issue.net is configured" 101 | when: 102 | - rhel8cis_rule_1_7_6 103 | tags: 104 | - level1-server 105 | - level1-workstation 106 | - automated 107 | - perms 108 | - patch 109 | - NIST800-53R5_AC-3 110 | - NIST800-53R5_MP-2 111 | - rule_1.7.6 112 | ansible.builtin.file: 113 | path: /etc/issue.net 114 | state: file 115 | owner: root 116 | group: root 117 | mode: 'go-wx' 118 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.9 | PATCH | Disable Automounting" 4 | ansible.builtin.service: 5 | name: autofs 6 | enabled: false 7 | when: 8 | - not rhel8cis_allow_autofs 9 | - "'autofs' in ansible_facts.packages" 10 | - rhel8cis_rule_1_1_9 11 | tags: 12 | - level1-server 13 | - level2-workstation 14 | - automated 15 | - patch 16 | - mounts 17 | - automounting 18 | - rule_1.1.9 19 | -------------------------------------------------------------------------------- /tasks/section_1/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 1.1.1.x | Disable unused filesystems" 4 | ansible.builtin.import_tasks: 5 | file: cis_1.1.1.x.yml 6 | 7 | - name: "SECTION | 1.1.2.1.x | Configure /tmp" 8 | ansible.builtin.import_tasks: 9 | file: cis_1.1.2.1.x.yml 10 | 11 | - name: "SECTION | 1.1.2.2.x | Configure /dev/shm" 12 | ansible.builtin.import_tasks: 13 | file: cis_1.1.2.2.x.yml 14 | 15 | - name: "SECTION | 1.1.2.3.x | Configure /home" 16 | ansible.builtin.import_tasks: 17 | file: cis_1.1.2.3.x.yml 18 | 19 | - name: "SECTION | 1.1.2.4.x | Configure /var" 20 | ansible.builtin.import_tasks: 21 | file: cis_1.1.2.4.x.yml 22 | 23 | - name: "SECTION | 1.1.2.5.x | Configure /var/tmp" 24 | ansible.builtin.import_tasks: 25 | file: cis_1.1.2.5.x.yml 26 | 27 | - name: "SECTION | 1.1.2.6.x | Configure /var/log" 28 | ansible.builtin.import_tasks: 29 | file: cis_1.1.2.6.x.yml 30 | 31 | - name: "SECTION | 1.1.2.7.x | Configure /var/log/audit" 32 | ansible.builtin.import_tasks: 33 | file: cis_1.1.2.7.x.yml 34 | 35 | - name: "SECTION | 1.2 | DNF and updates" 36 | ansible.builtin.import_tasks: 37 | file: cis_1.2.x.yml 38 | 39 | - name: "SECTION | 1.3 | Bootloader" 40 | ansible.builtin.import_tasks: 41 | file: cis_1.3.x.yml 42 | 43 | - name: "SECTION | 1.4 | sysctl kernel" 44 | ansible.builtin.import_tasks: 45 | file: cis_1.4.x.yml 46 | 47 | - name: "SECTION | 1.5 | SELinux" 48 | when: not rhel8cis_selinux_disable 49 | ansible.builtin.import_tasks: 50 | file: cis_1.5.1.x.yml 51 | 52 | - name: "SECTION | 1.6 | Crypto" 53 | ansible.builtin.import_tasks: 54 | file: cis_1.6.x.yml 55 | 56 | - name: "SECTION | 1.7 | Command Line Warning Banners" 57 | ansible.builtin.import_tasks: 58 | file: cis_1.7.x.yml 59 | 60 | - name: "SECTION | 1.8 | Gnome Display Manager" 61 | when: rhel8cis_gui 62 | ansible.builtin.import_tasks: 63 | file: cis_1.8.x.yml 64 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.1.1 | PATCH | Ensure time synchronization is in use" 4 | when: 5 | - rhel8cis_rule_2_1_1 6 | - not system_is_container 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - automated 11 | - patch 12 | - rule_2.1.1 13 | ansible.builtin.package: 14 | name: "{{ rhel8cis_time_synchronization }}" 15 | state: present 16 | 17 | - name: "2.1.2 | PATCH | Ensure chrony is configured" 18 | when: 19 | - rhel8cis_time_synchronization == "chrony" 20 | - rhel8cis_rule_2_1_2 21 | - not system_is_container 22 | tags: 23 | - level1-server 24 | - level1-workstation 25 | - patch 26 | - NIST800-53R5_AU-3 27 | - NIST800-53R5_AU-12 28 | - rule_2.1.2 29 | ansible.builtin.template: 30 | src: chrony.conf.j2 31 | dest: /etc/chrony.conf 32 | owner: root 33 | group: root 34 | mode: '0644' 35 | 36 | - name: "2.1.3 | PATCH | Ensure chrony is not run as the root user" 37 | when: 38 | - rhel8cis_time_synchronization == "chrony" 39 | - rhel8cis_rule_2_1_3 40 | - not system_is_container 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - patch 45 | - rule_2.1.3 46 | ansible.builtin.lineinfile: 47 | path: /etc/sysconfig/chronyd 48 | regexp: '^OPTIONS="(?!.* -u chrony.*)(.*)"' 49 | line: OPTIONS="\1 -u chrony" 50 | create: true 51 | backrefs: true 52 | mode: '0644' 53 | notify: Restart_chronyd 54 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.3.1 | PATCH | Ensure ftp client is not installed" 4 | when: 5 | - not rhel8cis_ftp_client 6 | - "'ftp' in ansible_facts.packages" 7 | - rhel8cis_rule_2_3_1 8 | tags: 9 | - level1-server 10 | - level1-workstation 11 | - automated 12 | - patch 13 | - ftp 14 | - NIST800-53R5_CM-7 15 | - rule_2.3.1 16 | ansible.builtin.package: 17 | name: ftp 18 | state: absent 19 | 20 | - name: "2.3.2 | PATCH | Ensure ldap client is not installed" 21 | when: 22 | - not rhel8cis_openldap_clients_required 23 | - "'openldap-clients' in ansible_facts.packages" 24 | - rhel8cis_rule_2_3_2 25 | tags: 26 | - level2-server 27 | - level2-workstation 28 | - automated 29 | - patch 30 | - ldap 31 | - NIST800-53R5_CM-7 32 | - rule_2.3.2 33 | ansible.builtin.package: 34 | name: openldap-clients 35 | state: absent 36 | 37 | - name: "2.3.3 | PATCH | Ensure nis client is not installed" 38 | when: 39 | - not rhel8cis_ypbind_required 40 | - "'ypbind' in ansible_facts.packages" 41 | - rhel8cis_rule_2_3_3 42 | tags: 43 | - level1-server 44 | - level1-workstation 45 | - automated 46 | - patch 47 | - nis 48 | - NIST800-53R5_CM-7 49 | - rule_2.3.3 50 | ansible.builtin.package: 51 | name: ypbind 52 | state: absent 53 | 54 | - name: "2.3.4 | PATCH | Ensure telnet client is not installed" 55 | when: 56 | - not rhel8cis_telnet_required 57 | - "'telnet' in ansible_facts.packages" 58 | - rhel8cis_rule_2_3_4 59 | tags: 60 | - level1-server 61 | - level1-workstation 62 | - automated 63 | - patch 64 | - telnet 65 | - NIST800-53R5_CM-7 66 | - rule_2.3.4 67 | ansible.builtin.package: 68 | name: telnet 69 | state: absent 70 | 71 | - name: "2.3.5 | PATCH | Ensure TFTP client is not installed" 72 | when: 73 | - not rhel8cis_tftp_client 74 | - "'tftp' in ansible_facts.packages" 75 | - rhel8cis_rule_2_3_5 76 | tags: 77 | - level1-server 78 | - level1-workstation 79 | - automated 80 | - patch 81 | - tftp 82 | - NIST800-53R5_CM-7 83 | - rule_2.3.5 84 | ansible.builtin.package: 85 | name: tftp 86 | state: absent 87 | -------------------------------------------------------------------------------- /tasks/section_2/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 2.1 | Time Synchronization" 4 | ansible.builtin.import_tasks: 5 | file: cis_2.1.x.yml 6 | 7 | - name: "SECTION | 2.2 | Special Purpose Services" 8 | ansible.builtin.import_tasks: 9 | file: cis_2.2.x.yml 10 | 11 | - name: "SECTION | 2.3 | Service Clients" 12 | ansible.builtin.import_tasks: 13 | file: cis_2.3.x.yml 14 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # The CIS Control wants IPv6 disabled if not in use. 4 | # We are using the rhel8cis_ipv6_required to specify if you have IPv6 in use 5 | 6 | - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system" 7 | when: 8 | - not rhel8cis_ipv6_required 9 | - rhel8cis_rule_3_1_1 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - manual 14 | - patch 15 | - ipv6 16 | - networking 17 | - NIST800-53R5_CM-7 18 | - rule_3.1.1 19 | notify: Change_requires_reboot 20 | block: 21 | - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system | disable all except localhost" 22 | ansible.posix.sysctl: 23 | name: "{{ item }}" 24 | value: '1' 25 | sysctl_set: true 26 | sysctl_file: "{{ rhel8cis_sysctl_file }}" 27 | loop: 28 | - net.ipv6.conf.all.disable_ipv6 29 | - net.ipv6.conf.default.disable_ipv6 30 | 31 | - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system | disable localhost sysctl" 32 | when: 33 | - rhel8cis_ipv6_disable_localhost 34 | ansible.posix.sysctl: 35 | name: net.ipv6.conf.lo.disable_ipv6 36 | value: '1' 37 | sysctl_set: true 38 | sysctl_file: "{{ rhel8cis_sysctl_file }}" 39 | 40 | - name: "3.1.1 | PATCH | Verify if IPv6 is enabled on the system | disable localhost /etc/hosts" 41 | when: 42 | - rhel8cis_ipv6_disable_localhost 43 | ansible.builtin.lineinfile: 44 | path: /etc/hosts 45 | regexp: '^(::1.*)' 46 | line: '#\1' 47 | backrefs: true 48 | 49 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled" 50 | when: 51 | - rhel8cis_rule_3_1_2 52 | tags: 53 | - level1-server 54 | - automated 55 | - patch 56 | - wireless 57 | - NIST800-53R5_CM-7 58 | - rule_3.1.2 59 | block: 60 | - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if nmcli command is available" 61 | ansible.builtin.shell: rpm -q NetworkManager 62 | changed_when: false 63 | failed_when: false 64 | check_mode: false 65 | register: discovered_nmcli_available 66 | 67 | - name: "3.1.2 | AUDIT | Ensure wireless interfaces are disabled | Check if wifi is enabled" 68 | when: discovered_nmcli_available.rc == 0 69 | ansible.builtin.shell: nmcli radio wifi 70 | register: discovered_wifi_enabled 71 | changed_when: discovered_wifi_enabled.stdout != "disabled" 72 | failed_when: false 73 | 74 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Disable wifi if enabled" 75 | when: discovered_wifi_enabled is changed # noqa: no-handler 76 | ansible.builtin.shell: nmcli radio all off 77 | changed_when: false 78 | failed_when: false 79 | 80 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use" 81 | when: 82 | - rhel8cis_rule_3_1_3 83 | tags: 84 | - level1-server 85 | - level2-workstation 86 | - automated 87 | - patch 88 | - sctp 89 | - NIST800-53R5_CM-7 90 | - rule_3.1.3 91 | block: 92 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use | pkg" 93 | when: 94 | - not rhel8cis_bluetooth_service 95 | - not rhel8cis_bluetooth_mask 96 | ansible.builtin.package: 97 | name: bluez 98 | state: absent 99 | 100 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use | mask" 101 | when: 102 | - not rhel8cis_bluetooth_service 103 | - rhel8cis_bluetooth_mask 104 | notify: Systemd_daemon_reload 105 | ansible.builtin.systemd: 106 | name: bluetooth.service 107 | enabled: false 108 | state: stopped 109 | masked: true 110 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.2.1 | PATCH | Ensure dccp kernel module is not available" 4 | when: 5 | - rhel8cis_rule_3_2_1 6 | tags: 7 | - level2-server 8 | - level2-workstation 9 | - automated 10 | - dccp 11 | - patch 12 | - NIST800-53R5_CM-7 13 | - NIST800-53R5_SI-4 14 | - rule_3.2.1 15 | block: 16 | - name: "3.2.1 | PATCH | Ensure dccp kernel module is not available | CIS" 17 | ansible.builtin.lineinfile: 18 | path: /etc/modprobe.d/CIS.conf 19 | regexp: "^(#)?install dccp(\\s|$)" 20 | line: "install dccp /bin/true" 21 | create: true 22 | mode: '0600' 23 | 24 | - name: "3.2.1 | PATCH | Ensure dccp kernel module is not available | blacklist" 25 | ansible.builtin.lineinfile: 26 | path: /etc/modprobe.d/blacklist.conf 27 | regexp: "^(#)?blacklist dccp(\\s|$)" 28 | line: "blacklist dccp" 29 | create: true 30 | mode: '0600' 31 | 32 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available" 33 | when: 34 | - rhel8cis_rule_3_2_2 35 | tags: 36 | - level2-server 37 | - level2-workstation 38 | - automated 39 | - patch 40 | - tipc 41 | - NIST800-53R5_CM-7 42 | - NIST800-53R5_SI-4 43 | - rule_3.2.2 44 | 45 | block: 46 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available | CIS" 47 | ansible.builtin.lineinfile: 48 | path: /etc/modprobe.d/CIS.conf 49 | regexp: "^(#)?install tipc(\\s|$)" 50 | line: "install tipc /bin/true" 51 | create: true 52 | mode: '0600' 53 | 54 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available | blacklist" 55 | ansible.builtin.lineinfile: 56 | path: /etc/modprobe.d/blacklist.conf 57 | regexp: "^(#)?blacklist tipc(\\s|$)" 58 | line: "blacklist tipc" 59 | create: true 60 | mode: '0600' 61 | 62 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available" 63 | when: 64 | - rhel8cis_rule_3_2_3 65 | tags: 66 | - level2-server 67 | - level2-workstation 68 | - automated 69 | - patch 70 | - rds 71 | - NIST800-53R5_CM-7 72 | - NIST800-53R5_SI-4 73 | - rule_3.2.3 74 | block: 75 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available | CIS" 76 | ansible.builtin.lineinfile: 77 | path: /etc/modprobe.d/CIS.conf 78 | regexp: "^(#)?install rpc(\\s|$)" 79 | line: "install rds /bin/true" 80 | create: true 81 | mode: '0600' 82 | 83 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available | blacklist" 84 | ansible.builtin.lineinfile: 85 | path: /etc/modprobe.d/blacklist.conf 86 | regexp: "^(#)?blacklist rpc(\\s|$)" 87 | line: "blacklist rds" 88 | create: true 89 | mode: '0600' 90 | 91 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available" 92 | when: 93 | - rhel8cis_rule_3_2_4 94 | tags: 95 | - level2-server 96 | - level2-workstation 97 | - automated 98 | - patch 99 | - sctp 100 | - NIST800-53R5_CM-7 101 | - NIST800-53R5_SI-4 102 | - rule_3.2.4 103 | block: 104 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available | CIS" 105 | ansible.builtin.lineinfile: 106 | path: /etc/modprobe.d/CIS.conf 107 | regexp: "^(#)?install sctp(\\s|$)" 108 | line: "install sctp /bin/true" 109 | create: true 110 | mode: '0600' 111 | 112 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available | blacklist" 113 | ansible.builtin.lineinfile: 114 | path: /etc/modprobe.d/blacklist.conf 115 | regexp: "^(#)?blacklist sctp(\\s|$)" 116 | line: "blacklist sctp" 117 | create: true 118 | mode: '0600' 119 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.4.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.4.1.1 | PATCH | Ensure nftables is installed" 4 | when: 5 | - rhel8cis_rule_3_4_1_1 6 | - "'nftables' not in ansible_facts.packages" 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - nftables 12 | - NIST800-53R5_CA-9 13 | - rule_3.4.1.1 14 | ansible.builtin.package: 15 | name: 16 | - nftables 17 | state: present 18 | 19 | - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use" 20 | when: 21 | - rhel8cis_rule_3_4_1_2 22 | tags: 23 | - level1-server 24 | - level1-workstation 25 | - patch 26 | - firewalld 27 | - nftables 28 | - rule_3.4.1.2 29 | block: 30 | - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | nftables" 31 | when: 32 | - "'firewalld' in ansible_facts.packages" 33 | - rhel8cis_firewall == "nftables" 34 | ansible.builtin.systemd: 35 | name: firewalld 36 | masked: true 37 | 38 | - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | firewalld" 39 | when: 40 | - "'nftables' in ansible_facts.packages" 41 | - rhel8cis_firewall == "firewalld" 42 | ansible.builtin.systemd: 43 | name: nftables 44 | masked: true 45 | 46 | - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | {{ rhel8cis_firewall }} package installed" 47 | when: "rhel8cis_firewall not in ansible_facts.packages" 48 | ansible.builtin.package: 49 | name: "{{ rhel8cis_firewall }}" 50 | state: present 51 | 52 | - name: "3.4.1.2 | PATCH | Ensure a single firewall configuration utility is in use | {{ rhel8cis_firewall }} started and enabled" 53 | ansible.builtin.systemd: 54 | name: "{{ rhel8cis_firewall }}" 55 | enabled: true 56 | state: started 57 | -------------------------------------------------------------------------------- /tasks/section_3/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 3.1.x | Configure network devices" 4 | ansible.builtin.import_tasks: 5 | file: cis_3.1.x.yml 6 | 7 | - name: "SECTION | 3.2.x | Network kernel modules" 8 | ansible.builtin.import_tasks: 9 | file: cis_3.2.x.yml 10 | 11 | - name: "SECTION | 3.3.x | Network kernel Parameters" 12 | ansible.builtin.import_tasks: 13 | file: cis_3.3.x.yml 14 | 15 | - name: "SECTION | 3.4.1.x | Configure firewall utility" 16 | ansible.builtin.import_tasks: 17 | file: cis_3.4.1.x.yml 18 | 19 | - name: "SECTION | 3.4.2.x | Configure firewall rules" 20 | ansible.builtin.import_tasks: 21 | file: cis_3.4.2.x.yml 22 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.1.1 | PATCH | Ensure cron daemon is enabled and active" 4 | when: 5 | - rhel8cis_rule_4_1_1_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - cron 12 | - NIST800-53R5_CM-1 13 | - NIST800-53R5_CM-2 14 | - NIST800-53R5_CM-6 15 | - NIST800-53R5_CM-7 16 | - NIST800-53R5_IA-5 17 | - rule_4.1.1.1 18 | ansible.builtin.systemd: 19 | name: crond 20 | enabled: true 21 | state: started 22 | 23 | - name: "4.1.1.2 | PATCH | Ensure permissions on /etc/crontab are configured" 24 | when: 25 | - rhel8cis_rule_4_1_1_2 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - cron 32 | - NIST800-53R5_AC-3 33 | - NIST800-53R5_MP-2 34 | - rule_4.1.1.2 35 | ansible.builtin.file: 36 | path: /etc/crontab 37 | owner: root 38 | group: root 39 | mode: '0600' 40 | 41 | - name: "4.1.1.3 | PATCH | Ensure permissions on /etc/cron.hourly are configured" 42 | when: 43 | - rhel8cis_rule_4_1_1_3 44 | tags: 45 | - level1-server 46 | - level1-workstation 47 | - automated 48 | - patch 49 | - cron 50 | - NIST800-53R5_AC-3 51 | - NIST800-53R5_MP-2 52 | - rule_4.1.1.3 53 | ansible.builtin.file: 54 | path: /etc/cron.hourly 55 | state: directory 56 | owner: root 57 | group: root 58 | mode: '0700' 59 | 60 | - name: "4.1.1.4 | PATCH | Ensure permissions on /etc/cron.daily are configured" 61 | when: 62 | - rhel8cis_rule_4_1_1_4 63 | tags: 64 | - level1-server 65 | - level1-workstation 66 | - automated 67 | - patch 68 | - cron 69 | - NIST800-53R5_AC-3 70 | - NIST800-53R5_MP-2 71 | - rule_4.1.1.4 72 | ansible.builtin.file: 73 | path: /etc/cron.daily 74 | state: directory 75 | owner: root 76 | group: root 77 | mode: '0700' 78 | 79 | - name: "4.1.1.5 | PATCH | Ensure permissions on /etc/cron.weekly are configured" 80 | when: 81 | - rhel8cis_rule_4_1_1_5 82 | tags: 83 | - level1-server 84 | - level1-workstation 85 | - patch 86 | - cron 87 | - NIST800-53R5_AC-3 88 | - NIST800-53R5_MP-2 89 | - rule_4.1.1.5 90 | ansible.builtin.file: 91 | path: /etc/cron.weekly 92 | state: directory 93 | owner: root 94 | group: root 95 | mode: '0700' 96 | 97 | - name: "4.1.1.6 | PATCH | Ensure permissions on /etc/cron.monthly are configured" 98 | when: 99 | - rhel8cis_rule_4_1_1_6 100 | tags: 101 | - level1-server 102 | - level1-workstation 103 | - automated 104 | - patch 105 | - cron 106 | - NIST800-53R5_AC-3 107 | - NIST800-53R5_MP-2 108 | - rule_4.1.1.6 109 | ansible.builtin.file: 110 | path: /etc/cron.monthly 111 | state: directory 112 | owner: root 113 | group: root 114 | mode: '0700' 115 | 116 | - name: "4.1.1.7 | PATCH | Ensure permissions on /etc/cron.d are configured" 117 | when: 118 | - rhel8cis_rule_4_1_1_7 119 | tags: 120 | - level1-server 121 | - level1-workstation 122 | - automated 123 | - patch 124 | - cron 125 | - NIST800-53R5_AC-3 126 | - NIST800-53R5_MP-2 127 | - rule_4.1.1.7 128 | ansible.builtin.file: 129 | path: /etc/cron.d 130 | state: directory 131 | owner: root 132 | group: root 133 | mode: '0700' 134 | 135 | - name: "4.1.1.8 | PATCH | Ensure cron is restricted to authorized users" 136 | when: 137 | - rhel8cis_rule_4_1_1_8 138 | tags: 139 | - level1-server 140 | - level1-workstation 141 | - automated 142 | - patch 143 | - cron 144 | - NIST800-53R5_AC-3 145 | - NIST800-53R5_MP-2 146 | - rule_4.1.1.8 147 | block: 148 | - name: "4.1.1.8 | PATCH | Ensure cron is restricted to authorized users | Remove cron.deny" 149 | ansible.builtin.file: 150 | path: /etc/cron.deny 151 | state: absent 152 | 153 | - name: "4.1.1.8 | PATCH | Ensure cron is restricted to authorized users | Check if cron.allow exists" 154 | ansible.builtin.stat: 155 | path: "/etc/cron.allow" 156 | register: discovered_cron_allow_state 157 | 158 | - name: "4.1.1.8 | PATCH | Ensure cron is restricted to authorized users | Ensure cron.allow is restricted to authorized users" 159 | ansible.builtin.file: 160 | path: /etc/cron.allow 161 | state: '{{ "file" if discovered_cron_allow_state.stat.exists else "touch" }}' 162 | owner: root 163 | group: root 164 | mode: 'g-wx,o-rwx' 165 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.2.1 | PATCH | Ensure at is restricted to authorized users" 4 | when: 5 | - rhel8cis_rule_4_1_2_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - cron 12 | - NIST800-53R5_AC-3 13 | - NIST800-53R5_MP-2 14 | - rule_4.1.2.1 15 | block: 16 | - name: "4.1.2.1 | PATCH | Ensure at is restricted to authorized users | Remove at.deny" 17 | ansible.builtin.file: 18 | path: /etc/at.deny 19 | state: absent 20 | 21 | - name: "4.1.2.1 | PATCH | Ensure at is restricted to authorized users | Check if at.allow exists" 22 | ansible.builtin.stat: 23 | path: "/etc/at.allow" 24 | register: discovered_at_allow_state 25 | 26 | - name: "4.1.2.1 | PATCH | Ensure at is restricted to authorized users | Ensure at.allow is restricted to authorized users" 27 | ansible.builtin.file: 28 | path: /etc/at.allow 29 | state: '{{ "file" if discovered_at_allow_state.stat.exists else "touch" }}' 30 | owner: root 31 | group: root 32 | mode: '0600' 33 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.3.1 | PATCH | Ensure sudo is installed" 4 | when: 5 | - rhel8cis_rule_4_3_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_AC-6 12 | - sudo 13 | - rule_4.3.1 14 | ansible.builtin.package: 15 | name: sudo 16 | state: present 17 | 18 | - name: "4.3.2 | PATCH | Ensure sudo commands use pty" 19 | when: 20 | - rhel8cis_rule_4_3_2 21 | tags: 22 | - level1-server 23 | - level1-workstation 24 | - automated 25 | - patch 26 | - NIST800-53R5_AC-6 27 | - sudo 28 | - rule_4.3.2 29 | ansible.builtin.lineinfile: 30 | path: /etc/sudoers 31 | line: "Defaults use_pty" 32 | validate: '/usr/sbin/visudo -cf %s' 33 | 34 | - name: "4.3.3 | PATCH | Ensure sudo log file exists" 35 | when: 36 | - rhel8cis_rule_4_3_3 37 | tags: 38 | - level1-server 39 | - level1-workstation 40 | - automated 41 | - patch 42 | - NIST800-53R5_AU-3 43 | - NIST800-53R5_AU-12 44 | - sudo 45 | - rule_4.3.3 46 | ansible.builtin.lineinfile: 47 | path: /etc/sudoers 48 | regexp: '^Defaults\s+logfile=' 49 | line: 'Defaults logfile={{ rhel8cis_sudolog_location }}' 50 | 51 | - name: "4.3.4 | PATCH | Ensure users must provide password for escalation" 52 | when: 53 | - rhel8cis_rule_4_3_4 54 | tags: 55 | - level2-server 56 | - level2-workstation 57 | - automated 58 | - patch 59 | - NIST800-53R5_AC-6 60 | - sudo 61 | - rule_4.3.4 62 | ansible.builtin.replace: 63 | path: "{{ item }}" 64 | regexp: '^([^#|{% if system_is_ec2 %}ec2-user{% endif %}].*)NOPASSWD(.*)' 65 | replace: '\1PASSWD\2' 66 | validate: '/usr/sbin/visudo -cf %s' 67 | loop: "{{ prelim_sudoers_files.stdout_lines }}" 68 | 69 | - name: "4.3.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" 70 | when: 71 | - rhel8cis_rule_4_3_5 72 | tags: 73 | - level1-server 74 | - level1-workstation 75 | - automated 76 | - patch 77 | - NIST800-53R5_AC-6 78 | - sudo 79 | - rule_4.3.5 80 | ansible.builtin.replace: 81 | path: "{{ item }}" 82 | regexp: '^([^#].*)!authenticate(.*)' 83 | replace: '\1authenticate\2' 84 | validate: '/usr/sbin/visudo -cf %s' 85 | loop: "{{ prelim_sudoers_files.stdout_lines }}" 86 | 87 | - name: "4.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly" 88 | when: 89 | - rhel8cis_rule_4_3_6 90 | tags: 91 | - level1-server 92 | - level1-workstation 93 | - automated 94 | - patch 95 | - sudo 96 | - rule_4.3.6 97 | block: 98 | - name: "4.3.6 | AUDIT | Ensure sudo authentication timeout is configured correctly | Get files with timeout set" 99 | ansible.builtin.shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort 100 | changed_when: false 101 | failed_when: false 102 | register: discovered_timeout_files 103 | 104 | - name: "4.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" 105 | when: discovered_timeout_files.stdout | length == 0 106 | ansible.builtin.lineinfile: 107 | path: /etc/sudoers 108 | regexp: 'Defaults timestamp_timeout=' 109 | line: "Defaults timestamp_timeout={{ rhel8cis_sudo_timestamp_timeout }}" 110 | validate: '/usr/sbin/visudo -cf %s' 111 | 112 | - name: "4.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" 113 | when: discovered_timeout_files.stdout | length > 0 114 | ansible.builtin.replace: 115 | path: "{{ item }}" 116 | regexp: 'timestamp_timeout=(\d+)' 117 | replace: "timestamp_timeout={{ rhel8cis_sudo_timestamp_timeout }}" 118 | validate: '/usr/sbin/visudo -cf %s' 119 | loop: "{{ discovered_timeout_files.stdout_lines }}" 120 | 121 | - name: "4.3.7 | PATCH | Ensure access to the su command is restricted" 122 | when: 123 | - rhel8cis_rule_4_3_7 124 | tags: 125 | - level1-server 126 | - level1-workstation 127 | - automated 128 | - patch 129 | - NIST800-53R5_AC-3 130 | - NIST800-53R5_MP-2 131 | - sudo 132 | - rule_4.3.7 133 | block: 134 | - name: "4.3.7 | PATCH | Ensure access to the su command is restricted | Ensure sugroup exists" 135 | ansible.builtin.group: 136 | name: "{{ rhel8cis_sugroup }}" 137 | state: present 138 | 139 | - name: "4.3.7 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" 140 | ansible.builtin.lineinfile: 141 | path: /etc/pam.d/su 142 | regexp: '^(#)?auth\s+required\s+pam_wheel\.so' 143 | line: 'auth required pam_wheel.so use_uid group={{ rhel8cis_sugroup }}' 144 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.4.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.4.1.1 | PATCH | Ensure latest version of pam is installed" 4 | when: 5 | - rhel8cis_rule_4_4_1_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - pam 12 | - rule_4.4.1.1 13 | ansible.builtin.package: 14 | name: pam 15 | state: latest 16 | 17 | - name: "4.4.1.2 | PATCH | Ensure latest version of authselect is installed" 18 | when: 19 | - rhel8cis_rule_4_4_1_2 20 | - rhel8cis_authselect_pkg_update 21 | tags: 22 | - level1-server 23 | - level1-workstation 24 | - automated 25 | - patch 26 | - pam 27 | - rule_4.4.1.2 28 | ansible.builtin.package: 29 | name: authselect 30 | state: latest 31 | 32 | - name: "4.4.1.1 and 4.4.1.2 | Update package facts" 33 | when: 34 | - rhel8cis_rule_4_4_1_1 or 35 | rhel8cis_rule_4_4_1_2 36 | ansible.builtin.package_facts: 37 | manager: auto 38 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.4.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.4.2.1 | PATCH | Ensure custom authselect profile is used" 4 | when: 5 | - rhel8cis_rule_4_4_2_1 6 | - rhel8cis_disruption_high 7 | - rhel8cis_allow_authselect_updates 8 | tags: 9 | - level1-server 10 | - level1-workstation 11 | - manual 12 | - patch 13 | - authselect 14 | - rule_4.4.2.1 15 | block: 16 | - name: "4.4.2.1 | PATCH | Ensure custom authselect profile is used | Create custom profiles" 17 | when: 18 | - rhel8cis_authselect_custom_profile_name not in prelim_authselect_current_profile.stdout or 19 | prelim_authselect_current_profile.stdout is not defined 20 | ansible.builtin.shell: "/usr/bin/authselect create-profile {{ rhel8cis_authselect_custom_profile_name }} -b {{ rhel8cis_authselect_default_profile_to_copy }}" 21 | args: 22 | creates: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}" 23 | 24 | - name: "4.4.2.1 | AUDIT | Ensure custom authselect profile is used | get profile features" 25 | ansible.builtin.shell: "/usr/bin/authselect list-features custom/{{ rhel8cis_authselect_custom_profile_name }}" 26 | changed_when: false 27 | register: discovered_authselect_profile_features 28 | 29 | - name: "4.4.2.1 | PATCH | Ensure custom authselect profile is used | Add missing pam modules to config | pwquality" 30 | when: "'with-pwquality' not in discovered_authselect_profile_features.stdout_lines" 31 | ansible.builtin.lineinfile: 32 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 33 | regexp: ^password\s*requisite\s*pam_pwquality.so.* 34 | line: password requisite pam_pwquality.so local_users_only {include if "with-pwquality"} 35 | loop: 36 | - system 37 | - password 38 | 39 | - name: "4.4.2.1 | PATCH | Ensure custom authselect profile is used | Backup and Add pam modules" 40 | ansible.builtin.shell: "/usr/bin/authselect select custom/{{ rhel8cis_authselect_custom_profile_name }}{% if rhel8cis_rule_4_4_2_2 %} with-faillock{% endif %}{% if rhel8cis_rule_4_4_2_3 %} with-pwquality{% endif %}{% if rhel8cis_rule_4_4_2_4 %} with-pwhistory{% endif %}{% if rhel8cis_rule_4_4_3_4_1 %} without-nullok{% endif %} --force --backup=rhel8cis-preremediate-{{ lookup('pipe', 'date +%Y-%m-%d-%H%M') }}" 41 | 42 | - name: "4.4.2.2 | PATCH | Ensure pam_faillock module is enabled" 43 | when: 44 | - rhel8cis_rule_4_4_2_2 45 | - rhel8cis_disruption_high 46 | - rhel8cis_allow_authselect_updates 47 | tags: 48 | - level1-server 49 | - level1-workstation 50 | - automated 51 | - patch 52 | - NIST800-53R5_CM-1 53 | - NIST800-53R5_CM-2 54 | - NIST800-53R5_CM-6 55 | - NIST800-53R5_CM-7 56 | - NIST800-53R5_IA-5 57 | - authselect 58 | - rule_4.4.2.2 59 | notify: Authselect update 60 | block: 61 | - name: "4.4.2.2 | AUDIT | Ensure pam_faillock module is enabled | Get current config" 62 | ansible.builtin.shell: authselect current | grep faillock 63 | changed_when: false 64 | failed_when: discovered_authselect_current_faillock.rc not in [ 0, 1 ] 65 | register: discovered_authselect_current_faillock 66 | 67 | - name: "4.4.2.2 | AUDIT | Ensure pam_faillock module is enabled | Add feature if missing" 68 | when: discovered_authselect_current_faillock.rc != 0 69 | ansible.builtin.shell: "/usr/bin/authselect select custom/{{ rhel8cis_authselect_custom_profile_name }}{% if rhel8cis_rule_4_4_2_2 %} with-faillock{% endif %}{% if rhel8cis_rule_4_4_2_3 %} with-pwquality{% endif %}{% if rhel8cis_rule_4_4_2_4 %} with-pwhistory{% endif %}{% if rhel8cis_rule_4_4_3_4_1 %} without-nullok{% endif %}" 70 | 71 | - name: "4.4.2.3 | PATCH | Ensure pam_pwquality module is enabled" 72 | when: 73 | - rhel8cis_rule_4_4_2_3 74 | - rhel8cis_disruption_high 75 | - rhel8cis_allow_authselect_updates 76 | tags: 77 | - level1-server 78 | - level1-workstation 79 | - automated 80 | - patch 81 | - NIST800-53R5_IA-5 82 | - authselect 83 | - rule_4.4.2.3 84 | block: 85 | - name: "4.4.2.3 | AUDIT | Ensure pam_pwquality module is enabled | Get current config" 86 | ansible.builtin.shell: authselect current | grep quality 87 | changed_when: false 88 | failed_when: discovered_authselect_current_quality.rc not in [ 0, 1 ] 89 | register: discovered_authselect_current_quality 90 | 91 | - name: "4.4.2.3 | AUDIT | Ensure pam_pwquality module is enabled | Add feature if missing" 92 | when: discovered_authselect_current_quality.rc != 0 93 | ansible.builtin.shell: "/usr/bin/authselect select custom/{{ rhel8cis_authselect_custom_profile_name }}{% if rhel8cis_rule_4_4_2_2 %} with-faillock{% endif %}{% if rhel8cis_rule_4_4_2_3 %} with-pwquality{% endif %}{% if rhel8cis_rule_4_4_2_4 %} with-pwhistory{% endif %}{% if rhel8cis_rule_4_4_3_4_1 %} without-nullok{% endif %}" 94 | notify: Authselect update 95 | 96 | - name: "4.4.2.4 | PATCH | Ensure pam_pwhistory module is enabled" 97 | when: 98 | - rhel8cis_rule_4_4_2_4 99 | - rhel8cis_disruption_high 100 | - rhel8cis_allow_authselect_updates 101 | tags: 102 | - level1-server 103 | - level1-workstation 104 | - automated 105 | - patch 106 | - NIST800-53R5_IA-5 107 | - authselect 108 | - rule_4.4.2.4 109 | block: 110 | - name: "4.4.2.4 | AUDIT | Ensure pam_pwhistory module is enabled | Get current config" 111 | ansible.builtin.shell: authselect current | grep pwhistory 112 | changed_when: false 113 | failed_when: discovered_authselect_current_history.rc not in [ 0, 1 ] 114 | register: discovered_authselect_current_history 115 | 116 | - name: "4.4.2.4 | PATCH | Ensure pam_pwhistory module is enabled | enable feature" 117 | when: discovered_authselect_current_history.rc != 0 118 | ansible.builtin.shell: "/usr/bin/authselect select custom/{{ rhel8cis_authselect_custom_profile_name }}{% if rhel8cis_rule_4_4_2_2 %} with-faillock{% endif %}{% if rhel8cis_rule_4_4_2_3 %} with-pwquality{% endif %}{% if rhel8cis_rule_4_4_2_4 %} with-pwhistory{% endif %}{% if rhel8cis_rule_4_4_3_4_1 %} without-nullok{% endif %}" 119 | notify: Authselect update 120 | 121 | - name: "4.4.2.5 | PATCH | Ensure pam_unix module is enabled" 122 | when: 123 | - rhel8cis_rule_4_4_2_5 124 | - rhel8cis_disruption_high 125 | - rhel8cis_allow_authselect_updates 126 | tags: 127 | - level1-server 128 | - level1-workstation 129 | - automated 130 | - patch 131 | - NIST800-53R5_IA-5 132 | - authselect 133 | - rule_4.4.2.5 134 | block: 135 | - name: "4.4.2.5 | AUDIT | Ensure pam_unix module is enabled" 136 | ansible.builtin.shell: | 137 | grep -P -- '\b(pam_unix\.so)\b' /etc/authselect/"$(head -1 /etc/authselect/authselect.conf)"/{system,password}-auth 138 | changed_when: false 139 | failed_when: discovered_authselect_pam_unix.rc not in [ 0, 1 ] 140 | register: discovered_authselect_pam_unix 141 | 142 | - name: "4.4.2.5 | PATCH | Ensure pam_unix module is enabled | system-auth" 143 | when: "'system-auth:password' not in discovered_authselect_pam_unix.stdout" 144 | ansible.builtin.lineinfile: 145 | path: /etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/system-auth 146 | regexp: "{{ item.regexp }}" 147 | line: "{{ item.line }}" 148 | backrefs: true 149 | insertafter: "{{ item.after | default (omit) }}" 150 | insertbefore: "{{ item.before | default (omit) }}" 151 | loop: 152 | - { regexp: '^(auth\s+)sufficient(\s+pam_unix.so.*)(.*)', line: '\1sufficient\2\3', after: '^auth.*pam_faillock.*preauth' } 153 | - { regexp: '^(password\s+)sufficient(\s+pam_unix.so.*)(.*)', line: '\1sufficient\2\3', before: '^password.*pam_deny.so' } 154 | notify: Authselect update 155 | 156 | - name: "4.4.2.5 | PATCH | Ensure pam_unix module is enabled | password-auth" 157 | when: "'password-auth:password' not in discovered_authselect_pam_unix.stdout" 158 | ansible.builtin.lineinfile: 159 | path: /etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/password-auth 160 | line: "{{ item.line }}" 161 | regexp: "{{ item.regexp }}" 162 | backrefs: true 163 | insertafter: "{{ item.after | default (omit) }}" 164 | insertbefore: "{{ item.before | default (omit) }}" 165 | loop: 166 | - { regexp: '^(auth\s+)sufficient(\s+pam_unix.so.*)(.*)', line: '\1sufficient\2\2', after: '^auth.*pam_faillock.*preauth' } 167 | - { regexp: '^(password\s+)sufficient(\s+pam_unix.so.*)(.*)', line: '\1sufficient\2\3', before: '^password.*pam_deny.so' } 168 | notify: Authselect update 169 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.4.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.4.3.1.1 | PATCH | Ensure password failed attempts lockout is configured" 4 | when: 5 | - rhel8cis_rule_4_4_3_1_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - pam 12 | - rule_4.4.3.1.1 13 | block: 14 | - name: "4.4.3.1.1 | PATCH | Ensure password failed attempts lockout is configured | faillock.conf" 15 | ansible.builtin.lineinfile: 16 | path: /etc/security/faillock.conf 17 | state: present 18 | regexp: '^(#|)\s*deny\s*=\s*\d' 19 | line: "deny = {{ rhel8cis_pam_faillock_deny }}" 20 | 21 | - name: "4.4.3.1.1 | PATCH | Ensure password failed attempts lockout is configured | pam_files" 22 | when: not rhel8cis_allow_authselect_updates 23 | ansible.builtin.replace: 24 | path: "/etc/pam.d/{{ item }}-auth" 25 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s+deny\s*=\s*\S+(.*$) 26 | replace: \1\2\3 27 | loop: 28 | - password 29 | - system 30 | 31 | - name: "4.4.2.2 | AUDIT | Ensure pam_faillock module is enabled | get current state" 32 | when: 33 | - rhel8cis_allow_authselect_updates 34 | ansible.builtin.shell: authselect current 35 | changed_when: false 36 | failed_when: discovered_authselect_current.rc not in [ 0, 1 ] 37 | register: discovered_authselect_current 38 | 39 | - name: "4.4.3.1.1 | PATCH | Ensure password failed attempts lockout is configured | authselect_files" 40 | when: 41 | - rhel8cis_allow_authselect_updates 42 | - discovered_authselect_current.rc != 0 43 | ansible.builtin.replace: 44 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 45 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s+deny\s*=\s*\S+(.*$) 46 | replace: \1\2\3 47 | loop: 48 | - password 49 | - system 50 | 51 | - name: "4.4.3.1.2 | PATCH | Ensure password unlock time is configured" 52 | when: 53 | - rhel8cis_rule_4_4_3_1_2 54 | tags: 55 | - level1-server 56 | - level1-workstation 57 | - automated 58 | - patch 59 | - pam 60 | - rule_4.4.3.1.2 61 | block: 62 | - name: "4.4.3.1.2 | PATCH | Ensure password unlock time is configured | faillock.conf" 63 | ansible.builtin.lineinfile: 64 | path: /etc/security/faillock.conf 65 | state: present 66 | regexp: '^(#|)\s*unlock_time\s*=\s*\d' 67 | line: "unlock_time = {{ rhel8cis_pam_faillock_unlock_time }}" 68 | 69 | - name: "4.4.3.1.2 | PATCH | Ensure password unlock time is configured | pam_files" 70 | when: not rhel8cis_allow_authselect_updates 71 | ansible.builtin.replace: 72 | path: "/etc/pam.d/{{ item }}-auth" 73 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s+unlock_time\s*=\s*\S+(.*$) 74 | replace: \1\2\3 75 | loop: 76 | - password 77 | - system 78 | 79 | - name: "4.4.3.1.2 | PATCH | Ensure password unlock time is configured | authselect_files" 80 | when: 81 | - rhel8cis_allow_authselect_updates 82 | - "'example' not in rhel8cis_authselect_custom_profile_name" 83 | notify: Update_authselect 84 | ansible.builtin.replace: 85 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 86 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s+unlock_time\s*=\s*\S+(.*$) 87 | replace: \1\2\3 88 | loop: 89 | - password 90 | - system 91 | 92 | - name: "4.4.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account" 93 | when: 94 | - rhel8cis_rule_4_4_3_1_3 95 | tags: 96 | - level1-server 97 | - level1-workstation 98 | - automated 99 | - patch 100 | - pam 101 | - rule_4.4.3.1.3 102 | block: 103 | - name: "4.4.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account | faillock.conf" 104 | ansible.builtin.lineinfile: 105 | path: /etc/security/faillock.conf 106 | state: present 107 | regexp: ^(#|)\s*"{{ rhel8cis_pam_faillock_root_option }}"(\s*=\s*\d|.*) 108 | line: "{{ rhel8cis_pam_faillock_root_option }}" 109 | 110 | - name: "4.4.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account | pam_files" 111 | when: not rhel8cis_allow_authselect_updates 112 | ansible.builtin.replace: 113 | path: "/etc/pam.d/{{ item }}-auth" 114 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s"{{ rhel8cis_pam_faillock_root_option }}"(\s*=\s*\d|.*)\S+(.*$) 115 | replace: \1\2\3\4 116 | loop: 117 | - password 118 | - system 119 | 120 | - name: "4.4.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account | authselect_files" 121 | when: 122 | - rhel8cis_allow_authselect_updates 123 | - "'example' not in rhel8cis_authselect_custom_profile_name" 124 | notify: Update_authselect 125 | ansible.builtin.replace: 126 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 127 | regexp: ^(\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so)(.*)\s"{{ rhel8cis_pam_faillock_root_option }}"(\s*=\s*\d|.*)\S+(.*$) 128 | replace: \1\2\3\4 129 | loop: 130 | - password 131 | - system 132 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.4.3.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.4.3.3.1 | PATCH | Ensure password history remember is configured" 4 | when: 5 | - rhel8cis_rule_4_4_3_3_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_IA-5 12 | - pam 13 | - rule_4.4.3.3.1 14 | block: 15 | - name: "4.4.3.3.1 | PATCH | Ensure password history remember is configured | pwhistory.conf" 16 | ansible.builtin.lineinfile: 17 | path: /etc/security/pwhistory.conf 18 | state: present 19 | regexp: '^(#|)\s*remember\s*=\s*\d' 20 | line: "remember = {{ rhel8cis_pam_pwhistory_remember }}" 21 | 22 | - name: "4.4.3.3.1 | PATCH | Ensure password number of changed characters is configured | authselect_files" 23 | when: 24 | - rhel8cis_allow_authselect_updates 25 | - "'example' not in rhel8cis_authselect_custom_profile_name" 26 | notify: Update_authselect 27 | ansible.builtin.replace: 28 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 29 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_pwhistory\.so)(.*)\s+remember\s*=\s*\S+(.*$) 30 | replace: \1\2\3 31 | loop: 32 | - password 33 | - system 34 | 35 | - name: "4.4.3.3.2 | PATCH | Ensure password history is enforced for the root user" 36 | when: 37 | - rhel8cis_rule_4_4_3_3_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - automated 42 | - patch 43 | - NIST800-53R5_IA-5 44 | - pam 45 | - rule_4.4.3.3.2 46 | block: 47 | - name: "4.4.3.3.2 | PATCH | Ensure password history is enforced for the root user | pwhistory.conf" 48 | ansible.builtin.lineinfile: 49 | path: /etc/security/pwhistory.conf 50 | state: present 51 | regexp: '^(#|)\s*enforce_for_root' 52 | line: "enforce_for_root" 53 | 54 | - name: "4.4.3.3.2 | PATCH | Ensure password history is enforced for the root user | pam_files" 55 | when: not rhel8cis_allow_authselect_updates 56 | ansible.builtin.replace: 57 | path: "/etc/pam.d/{{ item }}-auth" 58 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_pwhistory\.so)(.*)\s+enforce_for_root(.*$) 59 | replace: \1\2\3 60 | loop: 61 | - password 62 | - system 63 | 64 | - name: "4.4.3.3.2 | PATCH | Ensure password history is enforced for the root user | authselect_files" 65 | when: 66 | - rhel8cis_allow_authselect_updates 67 | - "'example' not in rhel8cis_authselect_custom_profile_name" 68 | notify: Update_authselect 69 | ansible.builtin.replace: 70 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 71 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_pwhistory\.so)(.*)\s+enforce_for_root(.*$) 72 | replace: \1\2\3 73 | loop: 74 | - password 75 | - system 76 | 77 | - name: "4.4.3.3.3 | PATCH | Ensure pam_pwhistory includes use_authtok" 78 | when: 79 | - rhel8cis_rule_4_4_3_3_3 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - automated 84 | - patch 85 | - NIST800-53R5_IA-5 86 | - pam 87 | - rule_4.4.3.3.3 88 | block: 89 | - name: "4.4.3.3.3 | PATCH | Ensure pam_pwhistory includes use_authtok | pwhistory.conf" 90 | ansible.builtin.lineinfile: 91 | path: /etc/security/pwhistory.conf 92 | state: present 93 | regexp: '^(#|)\s*use_authtok' 94 | line: "use_authtok" 95 | 96 | - name: "4.4.3.3.3 | AUDIT | Ensure pam_pwhistory includes use_authtok | Get current state" 97 | ansible.builtin.shell: | 98 | grep -P -- '^\h*password\h+([^#\n\r]+)\h+pam_pwhistory\.so\h+([^#\n\r]+\h+)?use_authtok\ b' /etc/pam.d/{password,system}-auth 99 | changed_when: false 100 | failed_when: discovered_pwhistory_authtok.rc not in [ 0, 1 ] 101 | register: discovered_pwhistory_authtok 102 | 103 | - name: "4.4.3.3.3 | PATCH | Ensure pam_pwhistory includes use_authtok | authselect_files" 104 | when: 105 | - rhel8cis_allow_authselect_updates 106 | - discovered_pwhistory_authtok.rc != 0 107 | ansible.builtin.lineinfile: 108 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 109 | regexp: ^(\s*password\s+)(requisite|required|sufficient)(\s+pam_unix.so\s)(.*)use_authtok(.*$) 110 | line: \1\2\3\4use_authtok\5 111 | backrefs: true 112 | loop: 113 | - password 114 | - system 115 | notify: Update_authselect 116 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.4.3.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.4.3.4.1 | PATCH | Ensure pam_unix does not include nullok" 4 | when: 5 | - rhel8cis_rule_4_4_3_4_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - pam 12 | - rule_4.4.3.4.1 13 | block: 14 | - name: "4.4.3.4.1 | PATCH | Ensure pam_unix does not include nullok | capture state" 15 | ansible.builtin.shell: grep -P -- '^\h*(auth|account|password|session)\h+(requisite|required|sufficient)\h+pam_unix\.so\b' /etc/pam.d/{password,system}-auth | grep -P -- '\bnullok\b' 16 | changed_when: false 17 | failed_when: discovered_pam_nullok.rc not in [ 0, 1 ] 18 | register: discovered_pam_nullok 19 | 20 | - name: "4.4.3.4.1 | PATCH | Ensure pam_unix does not include nullok | pam_files" 21 | when: 22 | - discovered_pam_nullok.stdout | length > 0 23 | - not rhel8cis_allow_authselect_updates 24 | ansible.builtin.replace: 25 | path: "/etc/pam.d/{{ item }}-auth" 26 | regexp: nullok 27 | replace: '' 28 | loop: 29 | - password 30 | - system 31 | 32 | - name: "4.4.3.4.1 | PATCH | Ensure pam_unix does not include nullok | update authselect" 33 | when: 34 | - discovered_pam_nullok is defined 35 | - discovered_pam_nullok.stdout | length > 0 36 | - rhel8cis_allow_authselect_updates 37 | notify: Update_authselect 38 | ansible.builtin.shell: authselect enable-feature without-nullok 39 | 40 | - name: "4.4.3.4.2 | PATCH | Ensure pam_unix does not include remember" 41 | when: 42 | - rhel8cis_rule_4_4_3_4_2 43 | tags: 44 | - level1-server 45 | - level1-workstation 46 | - automated 47 | - patch 48 | - NIST800-53R5_IA-5 49 | - pam 50 | - rule_4.4.3.4.2 51 | block: 52 | - name: "4.4.3.4.2 | AUDIT | Ensure pam_unix does not include remember | capture state" 53 | ansible.builtin.shell: grep -Pi '^\h*password\h+([^#\n\r]+\h+)?pam_unix\.so\b' /etc/pam.d/{password,system}-auth | grep -P '\bremember=\d\b' 54 | changed_when: false 55 | failed_when: discovered_pam_remember.rc not in [ 0, 1 ] 56 | register: discovered_pam_remember 57 | 58 | - name: "4.4.3.4.2 | PATCH | Ensure pam_unix does not include remember | pam_files" 59 | when: 60 | - discovered_pam_remember is defined 61 | - discovered_pam_remember | length > 0 62 | - not rhel8cis_allow_authselect_updates 63 | ansible.builtin.replace: 64 | path: "/etc/pam.d/{{ item }}-auth" 65 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)remember\s*=\s*\d+(.*$) 66 | replace: \1\3\4 67 | loop: 68 | - password 69 | - system 70 | 71 | - name: "4.4.3.4.2 | PATCH | Ensure pam_unix does not include remember | authconfig_files" 72 | when: 73 | - discovered_pam_remember is defined 74 | - discovered_pam_remember | length > 0 75 | - rhel8cis_allow_authselect_updates 76 | - "'example' not in rhel8cis_authselect_custom_profile_name" 77 | notify: Update_authselect 78 | ansible.builtin.replace: 79 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 80 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)remember\s*=\s*\d+(.*$) 81 | replace: \1\3\4 82 | loop: 83 | - password 84 | - system 85 | 86 | - name: "4.4.3.4.3 | PATCH | Ensure pam_unix includes a strong password hashing algorithm" 87 | when: 88 | - rhel8cis_rule_4_4_3_4_3 89 | tags: 90 | - level1-server 91 | - level1-workstation 92 | - automated 93 | - patch 94 | - NIST800-53R5_IA-5 95 | - pam 96 | - rule_4.4.3.4.3 97 | block: 98 | - name: "4.4.3.4.3 | AUDIT | Ensure pam_unix includes a strong password hashing algorithm | capture state" 99 | ansible.builtin.shell: grep -P -- '^\h*password\h+([^#\n\r]+)\h+pam_unix\.so\h+([^#\n\r]+\h+)?(sha512|yescrypt)\b' /etc/pam.d/{password,system}-auth | grep -v {{ rhel8cis_pam_pwhash }} 100 | changed_when: false 101 | failed_when: discovered_hash_state.rc not in [ 0, 1 ] 102 | register: discovered_hash_state 103 | 104 | - name: "4.4.3.4.3 | PATCH | Ensure pam_unix includes a strong password hashing algorithm | pam_files" 105 | when: 106 | - not rhel8cis_allow_authselect_updates 107 | - discovered_hash_state is defined 108 | - discovered_hash_state | length > 0 109 | ansible.builtin.replace: 110 | path: "/etc/pam.d/{{ item }}-auth" 111 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)(sha512|yescrypt)(.*$) 112 | replace: \1\3\5 {{ rhel8cis_pam_pwhash }} 113 | loop: 114 | - password 115 | - system 116 | 117 | - name: "4.4.3.4.3 | PATCH | Ensure pam_unix includes a strong password hashing algorithm | authselect_files" 118 | when: 119 | - discovered_hash_state is defined 120 | - discovered_hash_state | length > 0 121 | - rhel8cis_allow_authselect_updates 122 | - "'example' not in rhel8cis_authselect_custom_profile_name" 123 | notify: Update_authselect 124 | ansible.builtin.replace: 125 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 126 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)(sha512|yescrypt)\s*=\s*\d+(.*$) 127 | replace: \1\3\5 {{ rhel8cis_pam_pwhash }} 128 | loop: 129 | - password 130 | - system 131 | 132 | - name: "4.4.3.4.4 | PATCH | Ensure pam_unix includes use_authtok" 133 | when: 134 | - rhel8cis_rule_4_4_3_4_4 135 | tags: 136 | - level1-server 137 | - level1-workstation 138 | - automated 139 | - patch 140 | - NIST800-53R5_IA-5 141 | - pam 142 | - rule_4.4.3.4.4 143 | block: 144 | - name: "4.4.3.4.4 | PATCH | Ensure pam_unix includes use_authtok | capture state" 145 | ansible.builtin.shell: grep -P -- '^\h*password\h+([^#\n\r]+)\h+pam_unix\.so\h+([^#\n\r]+\h+)?use_authtok\b' /etc/pam.d/{password,system}-auth | grep -v use_authtok 146 | changed_when: false 147 | failed_when: discovered_pam_authtok.rc not in [ 0, 1 ] 148 | register: discovered_pam_authtok 149 | 150 | - name: "4.4.3.4.4 | PATCH | Ensure pam_unix includes use_authtok | pam_files" 151 | when: 152 | - not rhel8cis_allow_authselect_updates 153 | - discovered_pam_authtok is defined 154 | - discovered_pam_authtok | length > 0 155 | ansible.builtin.replace: 156 | path: "/etc/pam.d/{{ item }}-auth" 157 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)\s+use_authtok(.*$) 158 | replace: \1\3\4 use_authtok 159 | loop: 160 | - password 161 | - system 162 | 163 | - name: "4.4.3.4.4 | PATCH | Ensure password same consecutive characters is configured | authselect_files" 164 | when: 165 | - discovered_pam_authtok is defined 166 | - discovered_pam_authtok | length > 0 167 | - rhel8cis_allow_authselect_updates 168 | - "'example' not in rhel8cis_authselect_custom_profile_name" 169 | notify: Update_authselect 170 | ansible.builtin.replace: 171 | path: "/etc/authselect/custom/{{ rhel8cis_authselect_custom_profile_name }}/{{ item }}-auth" 172 | regexp: ^(\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so)(.*)\s+use_authtok(.*$) 173 | replace: \1\3\4 use_authtok 174 | loop: 175 | - password 176 | - system 177 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.5.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.5.1.1 | PATCH | Ensure strong password hashing algorithm is configured" 4 | when: 5 | - rhel8cis_rule_4_5_1_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_IA-5 12 | - shadow_suite 13 | - rule_4.5.1.1 14 | ansible.builtin.lineinfile: 15 | path: /etc/libuser.conf 16 | regexp: ^(#|)\s*crypt_style\s*=\s* 17 | line: "crypt_style = {{ rhel8cis_pam_pwhash }}" 18 | 19 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less" 20 | when: 21 | - rhel8cis_rule_4_5_1_2 22 | tags: 23 | - level1-server 24 | - level1-workstation 25 | - automated 26 | - patch 27 | - NIST800-53R5_CM-1 28 | - NIST800-53R5_CM-2 29 | - NIST800-53R5_CM-6 30 | - NIST800-53R5_CM-7 31 | - NIST800-53R5_IA-5 32 | - shadow_suite 33 | - rule_4.5.1.2 34 | block: 35 | - name: "4.5.1.2 | AUDIT | Ensure password expiration is 365 days or less | Capture accounts more than 365" 36 | ansible.builtin.shell: "grep -E '^[^:]+:[^!*]' /etc/shadow | awk -F':' '$5>{{ rhel8cis_pam_pass_max_days }} { print $1 }'" 37 | changed_when: false 38 | failed_when: discovered_pass_max_expire.rc not in [ 0, 1 ] 39 | register: discovered_pass_max_expire 40 | 41 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | update login.defs" 42 | ansible.builtin.lineinfile: 43 | path: /etc/login.defs 44 | regexp: '^PASS_MAX_DAYS' 45 | line: "PASS_MAX_DAYS {{ rhel8cis_pam_pass_max_days }}" 46 | 47 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Ensure all accounts set to 365" 48 | ansible.builtin.user: 49 | name: "{{ item }}" 50 | password_expire_max: "{{ rhel8cis_pam_pass_max_days }}" 51 | loop: "{{ discovered_pass_max_expire.stdout_lines | default([]) }}" 52 | when: 53 | - rhel8cis_set_max_expiry 54 | - item not in rhel8cis_user_skip_list 55 | 56 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Warning" 57 | block: 58 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Output list if not set to change" 59 | ansible.builtin.debug: 60 | msg: | 61 | "Warning!! The following account are set beyond the expected expiration date: 62 | {{ rhel8cis_pass_max_expire.stdout_lines | default([]) }}" 63 | 64 | - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | set warning fact" 65 | ansible.builtin.import_tasks: warning_facts.yml 66 | vars: 67 | warn_control_id: '4.5.1.2' 68 | when: 69 | - discovered_pass_max_expire is defined 70 | - discovered_pass_max_expire.stdout | length > 0 71 | - not rhel8cis_set_max_expiry 72 | 73 | - name: "4.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more" 74 | when: 75 | - rhel8cis_rule_4_5_1_3 76 | tags: 77 | - level1-server 78 | - level1-workstation 79 | - automated 80 | - patch 81 | - NIST800-53R5_CM-1 82 | - NIST800-53R5_CM-2 83 | - NIST800-53R5_CM-6 84 | - NIST800-53R5_CM-7 85 | - NIST800-53R5_IA-5 86 | - shadow_suite 87 | - rule_4.5.1.3 88 | block: 89 | - name: "4.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | set login.defs" 90 | ansible.builtin.lineinfile: 91 | path: /etc/login.defs 92 | regexp: ^(#|)\s*PASS_WARN_AGE\s+\d+ 93 | line: "PASS_WARN_AGE {{ rhel8cis_pam_pass_warn_age }}" 94 | 95 | - name: "4.5.1.3 | AUDIT | Ensure password expiration warning days is 7 or more | capture users not matching" 96 | ansible.builtin.shell: | 97 | awk -F: '/^[^:\n\r]+:[^!*xX\n\r]/ {print $1}' /etc/shadow 98 | changed_when: false 99 | failed_when: discovered_users_warn_days.rc not in [ 0, 1 ] 100 | register: discovered_users_warn_days 101 | 102 | - name: "4.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | change users not matching req" 103 | when: 104 | - discovered_users_warn_days is defined 105 | - discovered_users_warn_days.stdout | length > 0 106 | ansible.builtin.shell: chage --warndays {{ rhel8cis_pam_pass_warn_age }} {{ item }} 107 | loop: "{{ discovered_users_warn_days.stdout_lines }}" 108 | 109 | - name: "4.5.1.4 | PATCH | Ensure inactive password lock is 30 days or less" 110 | when: 111 | - rhel8cis_rule_4_5_1_4 112 | tags: 113 | - level1-server 114 | - level1-workstation 115 | - automated 116 | - patch 117 | - shadow_suite 118 | - rule_4.5.1.4 119 | block: 120 | - name: "4.5.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | capture default state" 121 | ansible.builtin.shell: useradd -D | grep INACTIVE 122 | changed_when: false 123 | failed_when: discovered_users_inactive_def.rc not in [ 0, 1 ] 124 | register: discovered_users_inactive_def 125 | 126 | - name: "4.5.1.4 | PATCH | Ensure inactive password lock is 30 days or less | change default" 127 | when: 128 | - discovered_users_inactive_def is defined 129 | - "'30' not in discovered_users_inactive_def.stdout" 130 | ansible.builtin.shell: useradd -D -f {{ rhel8cis_pam_pass_inactive }} 131 | 132 | - name: "4.5.1.4 | AUDIT | Ensure inactive password lock is 30 days or less | capture users not matching" 133 | ansible.builtin.shell: | 134 | awk -F: '/^[^#:]+:[^!*:]*:[^:]*:[^:]*:[^:]*:[^:]*:(\s*|-1|3[1-9]|[4-9][0-9]|[1-9][0-9][0-9]+):[^:]*:[^:]*\s*$/ {print $1}' /etc/shadow 135 | changed_when: false 136 | failed_when: discovered_users_inactive.rc not in [ 0, 1 ] 137 | register: discovered_users_inactive 138 | 139 | - name: "4.5.1.4 | PATCH | Ensure inactive password lock is 30 days or less | change users not matching req" 140 | when: 141 | - discovered_users_inactive is defined 142 | - discovered_users_inactive.stdout | length > 0 143 | ansible.builtin.shell: chage --inactive {{ rhel8cis_pam_pass_inactive }} {{ item }} 144 | loop: "{{ discovered_users_inactive.stdout_lines }}" 145 | 146 | - name: "4.5.1.5 | PATCH | Ensure all users last password change date is in the past" 147 | when: 148 | - rhel8cis_rule_4_5_1_5 149 | tags: 150 | - level1-server 151 | - level1-workstation 152 | - automated 153 | - patch 154 | - shadow_suite 155 | - rule_4.5.1.5 156 | vars: 157 | warn_control_id: '4.5.1.5' 158 | block: 159 | - name: "4.5.1.5 | AUDIT | Ensure all users last password change date is in the past | capture default state" 160 | ansible.builtin.shell: useradd -D | grep INACTIVE 161 | changed_when: false 162 | failed_when: discovered_users_inactive_default.rc not in [ 0, 1 ] 163 | register: discovered_users_inactive_default 164 | 165 | - name: "4.5.1.5 | PATCH | Ensure all users last password change date is in the past | change default" 166 | when: 167 | - discovered_users_inactive_default is defined 168 | - "'30' not in discovered_users_inactive_default.stdout" 169 | ansible.builtin.shell: useradd -D -f {{ rhel8cis_pam_pass_inactive }} 170 | 171 | - name: "4.5.1.5 | FACT | Ensure inactive password lock is 30 days or less | set fact days since_epoch" 172 | ansible.builtin.set_fact: 173 | days_since_epoch: "{{ (ansible_facts['date_time']['epoch']|int)/86400 }}" 174 | 175 | - name: "4.5.1.5 | AUDIT | Ensure inactive password lock is 30 days or less | capture users date in future" 176 | ansible.builtin.shell: | 177 | awk -F: '{ if ($3 > "{{ days_since_epoch }}") { print $1 } }' /etc/shadow 178 | changed_when: false 179 | failed_when: discovered_users_user_future.rc not in [ 0, 1 ] 180 | register: discovered_users_user_future 181 | 182 | - name: "4.5.1.5 | WARNING | Ensure inactive password lock is 30 days or less | Warn Users in future" 183 | when: 184 | - discovered_users_user_future is defined 185 | - discovered_users_user_future.stdout | length > 0 186 | ansible.builtin.debug: 187 | msg: 188 | - "Warning!! - The following users have passwords set in the future please investigate" 189 | - "{{ discovered_users_user_future.stdout_lines }}" 190 | 191 | - name: "4.5.1.5 | WARNING | Ensure inactive password lock is 30 days or less | Warn Users in future" 192 | when: 193 | - rhel8cis_users_user_future is defined 194 | - rhel8cis_users_user_future.stdout | length > 0 195 | ansible.builtin.import_tasks: 196 | file: warning_facts.yml 197 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.5.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.5.2.1 | PATCH | Ensure default group for the root account is GID 0" 4 | when: 5 | - rhel8cis_rule_4_5_2_1 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_CM-1 12 | - NIST800-53R5_CM-2 13 | - NIST800-53R5_CM-6 14 | - NIST800-53R5_CM-7 15 | - NIST800-53R5_IA-5 16 | - root 17 | - rule_4.5.2.1 18 | ansible.builtin.user: 19 | name: root 20 | group: 0 21 | 22 | - name: "4.5.2.2 | PATCH | Ensure root user umask is configured" 23 | when: 24 | - rhel8cis_rule_4_5_2_2 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - automated 29 | - patch 30 | - NIST800-53R5_AC-3 31 | - NIST800-53R5_MP-2 32 | - shadow_suite 33 | - rule_4.5.2.2 34 | ansible.builtin.lineinfile: 35 | path: /root/.bash_profile 36 | regexp: \s*umask 37 | line: "umask {{ rhel8cis_root_umask }}" 38 | 39 | - name: "4.5.2.3 | PATCH | Ensure system accounts are secured" 40 | when: 41 | - rhel8cis_rule_4_5_2_3 42 | tags: 43 | - level1-server 44 | - level1-workstation 45 | - automated 46 | - patch 47 | - NIST800-53R5_AC-2 48 | - NIST800-53R5_AC-3 49 | - NIST800-53R5_AC-11 50 | - NIST800-53R5_MP-2 51 | - shadow_suite 52 | - rule_4.5.2.3 53 | block: 54 | - name: "4.5.2.3 | PATCH | | Ensure system accounts are secured | Set nologin" 55 | when: 56 | - item.id not in prelim_interactive_usernames.stdout 57 | - item.id not in rhel8cis_system_users_shell 58 | - "'root' not in item.id" 59 | ansible.builtin.user: 60 | name: "{{ item.id }}" 61 | shell: /usr/sbin/nologin 62 | loop: "{{ rhel8cis_passwd }}" 63 | loop_control: 64 | label: "{{ item.id }}" 65 | 66 | - name: "4.5.2.3 | PATCH | | Ensure system accounts are secured | Lock accounts" 67 | when: 68 | - "item.id not in prelim_interactive_usernames.stdout" 69 | - "'root' not in item.id" 70 | ansible.builtin.user: 71 | name: "{{ item.id }}" 72 | password_lock: true 73 | loop: "{{ rhel8cis_passwd }}" 74 | loop_control: 75 | label: "{{ item.id }}" 76 | 77 | - name: "4.5.2.4 | PATCH | Ensure root password is set" 78 | when: 79 | - rhel8cis_rule_4_5_2_4 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - automated 84 | - patch 85 | - shadow_suite 86 | - rule_4.5.2.4 87 | ansible.builtin.debug: 88 | msg: "This is set as an assert in tasks/main" 89 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.5.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.5.3.1 | PATCH | Ensure nologin is not listed in /etc/shells" 4 | when: 5 | - rhel8cis_rule_4_5_3_1 6 | tags: 7 | - level2-server 8 | - level2-workstation 9 | - automated 10 | - NIST800-53R5_CM-1 11 | - NIST800-53R5_CM-2 12 | - NIST800-53R5_CM-6 13 | - NIST800-53R5_CM-7 14 | - NIST800-53R5_IA-5 15 | - patch 16 | - shells 17 | - rule_4.5.3.1 18 | ansible.builtin.replace: 19 | path: /etc/shells 20 | regexp: nologin 21 | replace: "" 22 | 23 | - name: "4.5.3.2 | PATCH | Ensure default user shell timeout is configured" 24 | when: 25 | - rhel8cis_rule_4_5_3_2 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - NIST800-53R5_AC-3 32 | - NIST800-53R5_MP-2 33 | - shell 34 | - rule_4.5.3.2 35 | ansible.builtin.blockinfile: 36 | path: "{{ item.path }}" 37 | state: "{{ item.state }}" 38 | marker: "# {mark} - CIS benchmark - Ansible-lockdown" 39 | create: true 40 | mode: '0644' 41 | block: | 42 | TMOUT={{ rhel8cis_shell_session_timeout }} 43 | readonly TMOUT 44 | export TMOUT 45 | loop: 46 | - { path: "{{ rhel8cis_shell_session_file }}", state: present } 47 | - { path: /etc/profile, state: "{{ (rhel8cis_shell_session_file == '/etc/profile') | ternary('present', 'absent') }}" } 48 | 49 | - name: "4.5.3.3 | PATCH | Ensure default user umask is configured" 50 | when: 51 | - rhel8cis_rule_4_5_3_3 52 | tags: 53 | - level1-server 54 | - level1-workstation 55 | - automated 56 | - patch 57 | - umask 58 | - NIST800-53R5_AC-3 59 | - NIST800-53R5_MP-2 60 | - rule_4.5.3.3 61 | ansible.builtin.replace: 62 | path: "{{ item.path }}" 63 | regexp: (?i)(umask\s+\d\d\d) 64 | replace: '{{ item.line }} 027' 65 | loop: 66 | - { path: '/etc/bashrc', line: 'umask' } 67 | - { path: '/etc/profile', line: 'umask' } 68 | - { path: '/etc/login.defs', line: 'UMASK' } 69 | -------------------------------------------------------------------------------- /tasks/section_4/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Access, Authentication, and Authorization 4 | 5 | - name: "SECTION | 4.1.1 | Configure time-based job schedulers | cron" 6 | ansible.builtin.import_tasks: 7 | file: cis_4.1.1.x.yml 8 | 9 | - name: "SECTION | 4.1.2 | Configure job schedulers | at" 10 | ansible.builtin.import_tasks: 11 | file: cis_4.1.2.x.yml 12 | 13 | - name: "SECTION | 4.2 | Configure SSH Server" 14 | ansible.builtin.import_tasks: 15 | file: cis_4.2.x.yml 16 | when: 17 | - "'openssh-server' in ansible_facts.packages" 18 | 19 | - name: "SECTION | 4.3 | Configure privilege escalation" 20 | ansible.builtin.import_tasks: 21 | file: cis_4.3.x.yml 22 | 23 | - name: "SECTION | 4.4.1 | Configure PAM software packages" 24 | ansible.builtin.import_tasks: 25 | file: cis_4.4.1.x.yml 26 | 27 | - name: "SECTION | 4.4.2 | Authselect" 28 | when: 29 | - rhel8cis_allow_authselect_updates 30 | - rhel8cis_disruption_high 31 | ansible.builtin.import_tasks: 32 | file: cis_4.4.2.x.yml 33 | 34 | - name: "SECTION | 4.4.3.1.x | Configure module pam_faillock" 35 | when: rhel8cis_disruption_high 36 | ansible.builtin.import_tasks: 37 | file: cis_4.4.3.1.x.yml 38 | 39 | - name: "SECTION | 4.4.3.2.x | Configure module pam_pwquality" 40 | when: rhel8cis_disruption_high 41 | ansible.builtin.import_tasks: 42 | file: cis_4.4.3.2.x.yml 43 | 44 | - name: "SECTION | 4.4.3.3.x | Configure module pam_pwhistory" 45 | when: rhel8cis_disruption_high 46 | ansible.builtin.import_tasks: 47 | file: cis_4.4.3.3.x.yml 48 | 49 | - name: "SECTION | 4.4.3.4.x | Configure module pam_unix" 50 | when: rhel8cis_disruption_high 51 | ansible.builtin.import_tasks: 52 | file: cis_4.4.3.4.x.yml 53 | 54 | - name: "SECTION | 4.5.1.x | Shadow suite" 55 | ansible.builtin.import_tasks: 56 | file: cis_4.5.1.x.yml 57 | 58 | - name: "SECTION | 4.5.2.x | Root and sys accounts" 59 | ansible.builtin.import_tasks: 60 | file: cis_4.5.2.x.yml 61 | 62 | - name: "SECTION | 4.5.3.x | User enviroment" 63 | ansible.builtin.import_tasks: 64 | file: cis_4.5.3.x.yml 65 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.1.1.1 | PATCH | Ensure rsyslog installed" 4 | when: 5 | - "'rsyslog' not in ansible_facts.packages" 6 | - rhel8cis_rule_5_1_1_1 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rsyslog 12 | - NIST800-53R5_AU-2 13 | - NIST800-53R5_AU-12 14 | - NIST800-53R5_SI-5 15 | - rule_5.1.1.1 16 | ansible.builtin.package: 17 | name: rsyslog 18 | state: present 19 | 20 | - name: "5.1.1.2 | PATCH | Ensure rsyslog service is enabled" 21 | when: 22 | - rhel8cis_rule_5_1_1_2 23 | - rhel8cis_rsyslog_ansiblemanaged 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - patch 28 | - rsyslog 29 | - NIST800-53R5_AU-3 30 | - NIST800-53R5_AU-12 31 | - rule_5.1.1.2 32 | ansible.builtin.systemd: 33 | name: rsyslog 34 | enabled: true 35 | 36 | - name: "5.1.1.3 | PATCH | Ensure journald is configured to send logs to rsyslog" 37 | ansible.builtin.lineinfile: 38 | path: /etc/systemd/journald.conf 39 | regexp: "^#ForwardToSyslog=|^ForwardToSyslog=" 40 | line: ForwardToSyslog=yes 41 | notify: Restart_journald 42 | when: 43 | - rhel8cis_rule_5_1_1_3 44 | - rhel8cis_syslog == "rsyslog" 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - patch 49 | - NIST800-53R5_AC-3 50 | - NIST800-53R5_AU-2 51 | - NIST800-53R5_AU-4 52 | - NIST800-53R5_AU-12 53 | - NIST800-53R5_MP-2 54 | - NIST800-53R5_SI-5 55 | - rsyslog 56 | - rule_5.1.1.3 57 | 58 | - name: "5.1.1.4 | PATCH | Ensure rsyslog default file permissions configured" 59 | when: 60 | - rhel8cis_rule_5_1_1_4 61 | - rhel8cis_rsyslog_ansiblemanaged 62 | tags: 63 | - level1-server 64 | - level1-workstation 65 | - patch 66 | - rsyslog 67 | - rule_5.1.1.4 68 | notify: Restart_rsyslog 69 | ansible.builtin.lineinfile: 70 | path: /etc/rsyslog.conf 71 | regexp: '^\$FileCreateMode' 72 | line: '$FileCreateMode 0640' 73 | 74 | - name: "5.1.1.5 | PATCH | Ensure logging is configured" 75 | when: 76 | - rhel8cis_rule_5_1_1_5 77 | - rhel8cis_rsyslog_ansiblemanaged 78 | tags: 79 | - level1-server 80 | - level1-workstation 81 | - patch 82 | - rsyslog 83 | - rule_5.1.1.5 84 | notify: Restart_rsyslog 85 | block: 86 | - name: "5.1.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" 87 | ansible.builtin.shell: cat /etc/rsyslog.conf 88 | changed_when: false 89 | failed_when: false 90 | check_mode: false 91 | register: discovered_captured_rsyslog_conf 92 | 93 | - name: "5.1.1.5 | AUDIT | Ensure logging is configured | rsyslog current config message out" 94 | ansible.builtin.debug: 95 | msg: 96 | - "These are the current logging configurations for rsyslog, please review:" 97 | - "{{ discovered_captured_rsyslog_conf.stdout_lines }}" 98 | 99 | - name: "5.1.1.5 | PATCH | Ensure logging is configured | mail.* log setting" 100 | when: rhel8cis_rsyslog_ansiblemanaged 101 | ansible.builtin.blockinfile: 102 | path: /etc/rsyslog.conf 103 | marker: "# {mark} MAIL LOG SETTINGS - CIS benchmark - Ansible-lockdown" 104 | block: | 105 | # mail logging additions to meet CIS standards 106 | mail.* -/var/log/mail 107 | mail.info -/var/log/mail.info 108 | mail.warning -/var/log/mail.warning 109 | mail.err /var/log/mail.err 110 | insertafter: '# Log all the mail messages in one place.' 111 | 112 | - name: "5.1.1.5 | PATCH | Ensure logging is configured | Misc. log setting" 113 | when: rhel8cis_rsyslog_ansiblemanaged 114 | ansible.builtin.blockinfile: 115 | path: /etc/rsyslog.conf 116 | state: present 117 | marker: "# {mark} MISC. LOG SETTINGS - CIS benchmark - Ansible-lockdown" 118 | block: | 119 | # misc. logging additions to meet CIS standards 120 | *.=warning;*.=err -/var/log/warn 121 | *.crit /var/log/warn 122 | *.*;mail.none;news.none /var/log/messages 123 | insertafter: '#### RULES ####' 124 | 125 | - name: "5.1.1.5 | PATCH | Ensure logging is configured | Local log settings" 126 | ansible.builtin.blockinfile: 127 | path: /etc/rsyslog.conf 128 | state: present 129 | marker: "#{mark} LOCAL LOG SETTINGS - CIS benchmark - Ansible-lockdown" 130 | block: | 131 | # local log settings to meet CIS standards 132 | local0,local1.* -/var/log/localmessages 133 | local2,local3.* -/var/log/localmessages 134 | local4,local5.* -/var/log/localmessages 135 | local6,local7.* -/var/log/localmessages 136 | *.emerg :omusrmsg:* 137 | insertafter: '#### RULES ####' 138 | 139 | - name: "5.1.1.5 | PATCH | Ensure logging is configured | Auth Settings" 140 | ansible.builtin.blockinfile: 141 | path: /etc/rsyslog.conf 142 | state: present 143 | marker: "#{mark} Auth SETTINGS - CIS benchmark - Ansible-lockdown" 144 | block: | 145 | # Private settings to meet CIS standards 146 | auth,authpriv.* /var/log/secure 147 | insertafter: '#### RULES ####' 148 | 149 | - name: "5.1.1.5 | PATCH | Ensure logging is configured | Cron Settings" 150 | ansible.builtin.blockinfile: 151 | path: /etc/rsyslog.conf 152 | state: present 153 | marker: "#{mark} Cron SETTINGS - CIS benchmark - Ansible-lockdown" 154 | block: | 155 | # Cron settings to meet CIS standards 156 | cron.* /var/log/cron 157 | insertafter: '#### RULES ####' 158 | 159 | - name: "5.1.1.6 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" 160 | when: 161 | - rhel8cis_rule_5_1_1_6 162 | - rhel8cis_remote_log_server 163 | - rhel8cis_rsyslog_ansiblemanaged 164 | tags: 165 | - level1-server 166 | - level1-workstation 167 | - patch 168 | - rsyslog 169 | - rule_5.1.1.6 170 | notify: Restart_rsyslog 171 | ansible.builtin.blockinfile: 172 | path: /etc/rsyslog.conf 173 | state: present 174 | block: | 175 | # target can be IP or FQDN 176 | *.* action(type="omfwd" target="{{ rhel8cis_remote_log_host }}" port="{{ rhel8cis_remote_log_port }}" protocol="{{ rhel8cis_remote_log_protocol }}" action.resumeRetryCount="{{ rhel8cis_remote_log_retrycount }}" queue.type="LinkedList" queue.size="{{ rhel8cis_remote_log_queuesize }}") 177 | insertafter: EOF 178 | register: set_remote_syslog 179 | failed_when: 180 | - set_remote_syslog is failed 181 | - set_remote_syslog.rc != 257 182 | 183 | - name: "5.1.1.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote client" 184 | when: 185 | - rhel8cis_rule_5_1_1_7 186 | - rhel8cis_rsyslog_ansiblemanaged 187 | tags: 188 | - level1-server 189 | - level1-workstation 190 | - patch 191 | - rsyslog 192 | - rule_5.1.1.7 193 | notify: Restart_rsyslog 194 | block: 195 | - name: "5.1.1.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote client. | When not log host" 196 | when: not rhel8cis_system_is_log_server 197 | ansible.builtin.replace: 198 | path: /etc/rsyslog.conf 199 | regexp: '{{ item }}' 200 | replace: '#\1' 201 | loop: 202 | - '^(\$ModLoad imtcp)' 203 | - '^(\$InputTCPServerRun)' 204 | - '^(module\(load="imtcp"\))' 205 | - '^(input\(type="imtcp")' 206 | 207 | - name: "5.1.1.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote clients. | When log host" 208 | when: rhel8cis_system_is_log_server 209 | ansible.builtin.replace: 210 | path: /etc/rsyslog.conf 211 | regexp: '^#(.*{{ item }}.*)' 212 | replace: '\1' 213 | loop: 214 | - 'ModLoad imtcp' 215 | - 'InputTCPServerRun' 216 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.1.2.1.1 | PATCH | Ensure systemd-journal-remote is installed" 4 | when: 5 | - rhel8cis_rule_5_1_2_1_1 6 | - rhel8cis_syslog == 'journald' 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - manual 11 | - patch 12 | - NIST800-53R5_AU-2 13 | - NIST800-53R5_AU-12 14 | - NIST800-53R5_SI-5 15 | - journald 16 | - rule_5.1.2.1.1 17 | ansible.builtin.package: 18 | name: systemd-journal-remote 19 | state: present 20 | 21 | - name: "5.1.2.1.2 | PATCH | Ensure systemd-journal-remote is configured" 22 | when: 23 | - rhel8cis_rule_5_1_2_1_2 24 | - rhel8cis_syslog == 'journald' 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - manual 29 | - patch 30 | - NIST800-53R5_AU-2 31 | - NIST800-53R5_AU-12 32 | - NIST800-53R5_SI-5 33 | - journald 34 | - rule_5.1.2.1.2 35 | notify: Restart_systemd_journal_upload 36 | ansible.builtin.lineinfile: 37 | path: /etc/systemd/journal-upload.conf 38 | regexp: "{{ item.regexp }}" 39 | line: "{{ item.line }}" 40 | loop: 41 | - { regexp: 'URL=', line: 'URL={{ rhel8cis_journal_upload_url }}'} 42 | - { regexp: 'ServerKeyFile=', line: 'ServerKeyFile={{ rhel8cis_journal_upload_serverkeyfile }}'} 43 | - { regexp: 'ServerCertificateFile=', line: 'ServerCertificateFile={{ rhel8cis_journal_servercertificatefile }}'} 44 | - { regexp: 'TrustedCertificateFile=', line: 'TrustedCertificateFile={{ rhel8cis_journal_trustedcertificatefile }}'} 45 | 46 | - name: "5.1.2.1.3 | PATCH | Ensure systemd-journal-remote is enabled" 47 | when: 48 | - rhel8cis_system_is_log_server 49 | - rhel8cis_rule_5_1_2_1_3 50 | - rhel8cis_syslog == 'journald' 51 | tags: 52 | - level1-server 53 | - level1-workstation 54 | - automated 55 | - patch 56 | - NIST800-53R5_AU-2 57 | - NIST800-53R5_AU-12 58 | - NIST800-53R5_SI-5 59 | - journald 60 | - rule_5.1.2.1.3 61 | ansible.builtin.systemd: 62 | name: systemd-journal-upload 63 | state: started 64 | enabled: true 65 | 66 | - name: "5.1.2.1.4 | PATCH | Ensure journald is not configured to receive logs from a remote client" 67 | when: 68 | - not rhel8cis_system_is_log_server 69 | - rhel8cis_rule_5_1_2_1_4 70 | - rhel8cis_syslog == 'journald' 71 | tags: 72 | - level1-server 73 | - level1-workstation 74 | - automated 75 | - patch 76 | - journald 77 | - rule_5.1.2.1.4 78 | ansible.builtin.systemd: 79 | name: systemd-journal-remote.socket 80 | state: stopped 81 | enabled: false 82 | masked: true 83 | 84 | - name: "5.1.2.2 | PATCH | Ensure journald service is enabled" 85 | when: 86 | - rhel8cis_rule_5_1_2_2 87 | - rhel8cis_syslog == 'journald' 88 | tags: 89 | - level1-server 90 | - level1-workstation 91 | - automated 92 | - audit 93 | - journald 94 | - rule_5.1.2.2 95 | block: 96 | - name: "5.1.2.2 | PATCH | Ensure journald service is enabled | Enable service" 97 | ansible.builtin.systemd: 98 | name: systemd-journald 99 | state: started 100 | enabled: true 101 | 102 | - name: "5.1.2.2 | AUDIT | Ensure journald service is enabled | Capture status" 103 | ansible.builtin.shell: systemctl is-enabled systemd-journald.service 104 | changed_when: false 105 | failed_when: false 106 | register: discovered_journald_status 107 | 108 | - name: "5.1.2.2 | AUDIT | Ensure journald service is enabled | Alert on bad status" 109 | ansible.builtin.debug: 110 | msg: 111 | - "Warning!! The status of systemd-journald should be static and it is not. Please investigate" 112 | when: "'static' not in discovered_journald_status.stdout" 113 | 114 | - name: "5.1.2.2 | AUDIT | Ensure journald service is enabled | Warn Count" 115 | ansible.builtin.import_tasks: 116 | file: warning_facts.yml 117 | vars: 118 | warn_control_id: '5.1.2.2' 119 | when: "'static' not in discovered_journald_status.stdout" 120 | 121 | - name: "5.1.2.3 | PATCH | Ensure journald is configured to compress large log files" 122 | when: 123 | - rhel8cis_rule_5_1_2_3 124 | tags: 125 | - level1-server 126 | - level1-workstation 127 | - automated 128 | - patch 129 | - journald 130 | - rule_5.1.2.3 131 | notify: Restart_journald 132 | ansible.builtin.lineinfile: 133 | path: /etc/systemd/journald.conf 134 | regexp: 'Compress=' 135 | line: Compress=yes 136 | insertafter: ^#Compress 137 | validate: /usr/bin/bash -n %s 138 | 139 | - name: "5.1.2.4 | PATCH | Ensure journald is configured to write logfiles to persistent disk" 140 | when: 141 | - rhel8cis_rule_5_1_2_4 142 | tags: 143 | - level1-server 144 | - level1-workstation 145 | - automated 146 | - patch 147 | - journald 148 | - rule_5.1.2.4 149 | notify: Restart_journald 150 | ansible.builtin.lineinfile: 151 | path: /etc/systemd/journald.conf 152 | regexp: 'Storage=' 153 | line: Storage=persistent 154 | state: present 155 | insertafter: ^#Storage 156 | validate: /usr/bin/bash -n %s 157 | 158 | # This is counter to control 5.1.1.3?? 159 | - name: "5.1.2.5 | PATCH | Ensure journald is not configured to send logs to rsyslog" 160 | when: 161 | - rhel8cis_rule_5_1_2_5 162 | - rhel8cis_syslog == 'journald' 163 | tags: 164 | - level1-server 165 | - level2-workstation 166 | - manual 167 | - patch 168 | - journald 169 | - rule_5.1.2.5 170 | notify: Restart_journald 171 | ansible.builtin.lineinfile: 172 | path: /etc/systemd/journald.conf 173 | regexp: "^ForwardToSyslog=" 174 | line: "#ForwardToSyslog=yes" 175 | 176 | - name: "5.1.2.6 | PATCH | Ensure journald log rotation is configured per site policy" 177 | when: 178 | - rhel8cis_rule_5_1_2_6 179 | - rhel8cis_syslog == 'journald' 180 | tags: 181 | - level1-server 182 | - level1-workstation 183 | - manual 184 | - patch 185 | - journald 186 | - rule_5.1.2.6 187 | ansible.builtin.lineinfile: 188 | path: /etc/systemd/journald.conf 189 | regexp: "{{ item.regexp }}" 190 | line: "{{ item.line }}" 191 | notify: Restart_journald 192 | loop: 193 | - { regexp: '^#SystemMaxUse=|^SystemMaxUse=', line: 'SystemMaxUse={{ rhel8cis_journald_systemmaxuse }}'} 194 | - { regexp: '^#SystemKeepFree=|^SystemKeepFree=', line: 'SystemKeepFree={{ rhel8cis_journald_systemkeepfree }}' } 195 | - { regexp: '^#RuntimeMaxUse=|^RuntimeMaxUse=', line: 'RuntimeMaxUse={{ rhel8cis_journald_runtimemaxuse }}'} 196 | - { regexp: '^#RuntimeKeepFree=|^RuntimeKeepFree=', line: 'RuntimeKeepFree={{ rhel8cis_journald_runtimekeepfree }}'} 197 | - { regexp: '^#MaxFileSec=|^MaxFileSec=', line: 'MaxFileSec={{ rhel8cis_journald_maxfilesec }}'} 198 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.1.3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.1.3 | PATCH | Ensure logrotate is configured" 4 | when: 5 | - rhel8cis_rule_5_1_3 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - manual 10 | - patch 11 | - logrotate 12 | - rule_5.1.3 13 | block: 14 | - name: "5.1.3 | AUDIT | Ensure logrotate is configured | Get logrotate settings" 15 | ansible.builtin.find: 16 | paths: /etc/logrotate.d/ 17 | register: discovered_log_rotate_conf 18 | 19 | - name: "5.1.3 | PATCH | Ensure logrotate is configured | conf files" 20 | ansible.builtin.replace: 21 | path: "{{ item.path }}" 22 | regexp: '^(\s*)(daily|weekly|monthly|yearly)$' 23 | replace: "\\1{{ rhel8cis_logrotate }}" 24 | loop: "{{ discovered_log_rotate_conf.files }}" 25 | 26 | - name: "5.1.3 | PATCH | Ensure logrotate is configured | logrotate.conf" 27 | ansible.builtin.replace: 28 | path: /etc/logrotate.conf 29 | regexp: '^(\s*)(daily|weekly|monthly|yearly)$' 30 | replace: "\\1{{ rhel8cis_logrotate }}" 31 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.1.4.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.1.4 | PATCH | Ensure permissions on all logfiles are configured" 4 | when: 5 | - rhel8cis_rule_5_1_4 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - patch 10 | - NIST800-53R5_AU-2 11 | - NIST800-53R5_AU-12 12 | - logfiles 13 | - rule_5.1.4 14 | block: 15 | - name: "5.1.4 | AUDIT | Ensure permissions on all logfiles are configured | find files" 16 | ansible.builtin.shell: find -L /var/log/ -type f -perm /g+wx,o+rwx -exec ls {} \; 17 | changed_when: false 18 | failed_when: false 19 | register: discovered_logfiles 20 | 21 | - name: "5.1.4 | PATCH | Ensure permissions on all logfiles are configured | change permissions" 22 | when: 23 | - item != "/var/log/btmp" 24 | - item != "/var/log/utmp" 25 | - item != "/var/log/wtmp" 26 | - item != "/var/log/lastlog" 27 | ansible.builtin.file: 28 | path: "{{ item }}" 29 | mode: 'u-x,g-wx,o-rwx' 30 | failed_when: discovered_logfile_list.state not in '[ file, absent ]' 31 | register: discovered_logfile_list 32 | loop: "{{ discovered_logfiles.stdout_lines }}" 33 | 34 | - name: "5.1.4 | PATCH | Ensure permissions on all logfiles are configured | change permissions" 35 | ansible.builtin.file: 36 | path: "{{ item.name }}" 37 | mode: 'ug-x,o-wx' 38 | failed_when: discovered_logfile_list.state not in '[ file, absent ]' 39 | register: discovered_logfile_list 40 | with_fileglob: 41 | - /var/log/*tmp.*" 42 | - /var/log/lastlog" 43 | - /var/log/ssd*" 44 | - /var/log/SSD*" 45 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.2.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.2.1.1 | PATCH | Ensure auditd is installed" 4 | when: 5 | - rhel8cis_rule_5_2_1_1 6 | tags: 7 | - level2-server 8 | - level2-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_AU-2 12 | - NIST800-53R5_AU-3 13 | - NIST800-53R5_AU-12 14 | - NIST800-53R5_SI-5 15 | - auditd 16 | - rule_5.2.1.1 17 | block: 18 | - name: "5.2.1.1 | PATCH | Ensure auditd is installed | Install auditd packages" 19 | ansible.builtin.package: 20 | name: audit 21 | state: present 22 | when: '"auditd" not in ansible_facts.packages' 23 | 24 | - name: "5.2.1.1 | PATCH | Ensure auditd is installed | Install auditd-lib packages" 25 | ansible.builtin.package: 26 | name: audit-libs 27 | state: present 28 | when: '"auditd-lib" not in ansible_facts.packages' 29 | 30 | - name: "5.2.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled" 31 | when: 32 | - rhel8cis_rule_5_2_1_2 33 | tags: 34 | - level2-server 35 | - level2-workstation 36 | - automated 37 | - patch 38 | - auditd 39 | - grub 40 | - rule_5.2.1.2 41 | notify: Rebuild_grub 42 | block: 43 | - name: "5.2.1.2 | AUDIT | Ensure auditing for processes that start prior to auditd is enabled | Get GRUB_CMDLINE_LINUX" 44 | ansible.builtin.shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' 45 | changed_when: false 46 | failed_when: false 47 | check_mode: false 48 | register: discovered_auditd_grub_cmdline_linux 49 | 50 | - name: "5.2.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Replace existing setting" 51 | when: "'audit=' in discovered_auditd_grub_cmdline_linux.stdout" 52 | ansible.builtin.replace: 53 | path: /etc/default/grub 54 | regexp: 'audit=.' 55 | replace: 'audit=1' 56 | 57 | - name: "5.2.1.2 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Add audit setting if missing" 58 | when: "'audit=' not in discovered_auditd_grub_cmdline_linux.stdout" 59 | ansible.builtin.lineinfile: 60 | path: /etc/default/grub 61 | regexp: '^GRUB_CMDLINE_LINUX=' 62 | line: '{{ discovered_auditd_grub_cmdline_linux.stdout }} audit=1"' 63 | 64 | - name: "5.2.1.3 | PATCH | Ensure audit_backlog_limit is sufficient" 65 | when: 66 | - rhel8cis_rule_5_2_1_3 67 | tags: 68 | - level2-server 69 | - level2-workstation 70 | - automated 71 | - patch 72 | - auditd 73 | - NIST800-53R5_AU-2 74 | - NIST800-53R5_AU-12 75 | - NIST800-53R5_SI-5 76 | - grub 77 | - rule_5.2.1.3 78 | notify: Rebuild_grub 79 | block: 80 | - name: "5.2.1.3 | AUDIT | Ensure audit_backlog_limit is sufficient | Get GRUB_CMDLINE_LINUX" 81 | ansible.builtin.shell: grep 'GRUB_CMDLINE_LINUX=' /etc/default/grub | sed 's/.$//' 82 | changed_when: false 83 | failed_when: false 84 | check_mode: false 85 | register: discovered_backlog_grub_cmdline_linux 86 | 87 | - name: "5.2.1.3 | PATCH | Ensure audit_backlog_limit is sufficient | Replace existing setting" 88 | ansible.builtin.replace: 89 | path: /etc/default/grub 90 | regexp: 'audit_backlog_limit=\d+' 91 | replace: 'audit_backlog_limit={{ rhel8cis_auditd_back_log_limit }}' 92 | when: "'audit_backlog_limit=' in discovered_backlog_grub_cmdline_linux.stdout" 93 | 94 | - name: "5.2.1.3 | PATCH | Ensure audit_backlog_limit is sufficient | Add audit_backlog_limit setting if missing" 95 | ansible.builtin.lineinfile: 96 | path: /etc/default/grub 97 | regexp: '^GRUB_CMDLINE_LINUX=' 98 | line: '{{ discovered_backlog_grub_cmdline_linux.stdout }} audit_backlog_limit={{ rhel8cis_auditd_back_log_limit }}"' 99 | when: "'audit_backlog_limit=' not in discovered_backlog_grub_cmdline_linux.stdout" 100 | 101 | - name: "5.2.1.4 | PATCH | Ensure auditd service is enabled" 102 | when: 103 | - rhel8cis_rule_5_2_1_4 104 | tags: 105 | - level2-server 106 | - level2-workstation 107 | - automated 108 | - patch 109 | - NIST800-53R5_AU-2 110 | - NIST800-53R5_AU-12 111 | - NIST800-53R5_SI-5 112 | - auditd 113 | - rule_5.2.1.4 114 | ansible.builtin.service: 115 | name: auditd 116 | state: started 117 | enabled: true 118 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.2.2.1 | PATCH | Ensure audit log storage size is configured" 4 | when: 5 | - rhel8cis_rule_5_2_2_1 6 | tags: 7 | - level2-server 8 | - level2-workstation 9 | - automated 10 | - patch 11 | - NIST800-53R5_AU-8 12 | - auditd 13 | - rule_5.2.2.1 14 | notify: Restart_auditd 15 | ansible.builtin.lineinfile: 16 | path: /etc/audit/auditd.conf 17 | regexp: ^max_log_file(\s|=) 18 | line: "max_log_file = {{ rhel8cis_auditd_max_log_file_size }}" 19 | 20 | - name: "5.2.2.2 | PATCH | Ensure audit logs are not automatically deleted" 21 | when: 22 | - rhel8cis_rule_5_2_2_2 23 | tags: 24 | - level2-server 25 | - level2-workstation 26 | - automated 27 | - patch 28 | - NIST800-53R5_AU-8 29 | - auditd 30 | - rule_5.2.2.2 31 | notify: Restart_auditd 32 | ansible.builtin.lineinfile: 33 | path: /etc/audit/auditd.conf 34 | regexp: "^max_log_file_action" 35 | line: "max_log_file_action = {{ rhel8cis_auditd_max_log_file_action }}" 36 | 37 | - name: "5.2.2.3 | PATCH | Ensure system is disabled when audit logs are full" 38 | when: 39 | - rhel8cis_rule_5_2_2_3 40 | tags: 41 | - level2-server 42 | - level2-workstation 43 | - automated 44 | - patch 45 | - NIST800-53R5_AU-2 46 | - NIST800-53R5_AU-8 47 | - NIST800-53R5_AU-12 48 | - NIST800-53R5_SI-5 49 | - auditd 50 | - rule_5.2.2.3 51 | ansible.builtin.lineinfile: 52 | path: /etc/audit/auditd.conf 53 | regexp: "{{ item.regexp }}" 54 | line: "{{ item.line }}" 55 | notify: Restart_auditd 56 | loop: 57 | - { regexp: '^disk_full_action', line: 'disk_full_action = {{ rhel8cis_auditd_disk_full_action }}' } 58 | - { regexp: '^disk_error_action', line: 'disk_error_action = {{ rhel8cis_auditd_disk_error_action }}' } 59 | 60 | - name: "5.2.2.4 | PATCH | Ensure system warns when audit logs are low on space" 61 | when: 62 | - rhel8cis_rule_5_2_2_4 63 | tags: 64 | - level2-server 65 | - level2-workstation 66 | - automated 67 | - patch 68 | - NIST800-53R5_AU-2 69 | - NIST800-53R5_AU-8 70 | - NIST800-53R5_AU-12 71 | - NIST800-53R5_SI-5 72 | - auditd 73 | - rule_5.2.2.4 74 | ansible.builtin.lineinfile: 75 | path: /etc/audit/auditd.conf 76 | regexp: "{{ item.regexp }}" 77 | line: "{{ item.line }}" 78 | notify: Restart_auditd 79 | loop: 80 | - { regexp: '^admin_space_left_action', line: 'admin_space_left_action = {{ rhel8cis_auditd_admin_space_left_action }}' } 81 | - { regexp: '^space_left_action', line: 'space_left_action = {{ rhel8cis_auditd_space_left_action }}' } 82 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.2.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.2.4.1 | PATCH | Ensure the audit log directory is 0750 or more restrictive" 4 | when: 5 | - rhel8cis_rule_5_2_4_1 6 | tags: 7 | - level2-server 8 | - level2-workstation 9 | - patch 10 | - auditd 11 | - rule_5.2.4.1 12 | ansible.builtin.file: 13 | path: "{{ prelim_audit_logfile.stdout | dirname }}" 14 | state: directory 15 | mode: 'g-w,o-rwx' 16 | 17 | - name: | 18 | "5.2.4.2 | PATCH | Ensure audit log files are mode 0640 or less permissive" 19 | "5.2.4.3 | PATCH | Ensure only authorized users own audit log files" 20 | "5.2.4.4 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" 21 | when: 22 | - rhel8cis_rule_5_2_4_2 or 23 | rhel8cis_rule_5_2_4_3 or 24 | rhel8cis_rule_5_2_4_4 25 | tags: 26 | - level2-server 27 | - level2-workstation 28 | - patch 29 | - auditd 30 | - rule_5.2.4.2 31 | - rule_5.2.4.3 32 | - rule_5.2.4.4 33 | block: 34 | - name: "5.2.4.2 | AUDIT | Ensure audit log files are mode 0640 or less permissive | stat file" 35 | ansible.builtin.stat: 36 | path: "{{ prelim_audit_logfile.stdout }}" 37 | changed_when: false 38 | register: discovered_auditlog_info 39 | 40 | - name: | 41 | "5.2.4.2 | PATCH | Ensure audit log files are mode 0640 or less permissive" 42 | "5.2.4.3 | PATCH | Ensure only authorized users own audit log files" 43 | "5.2.4.4 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" 44 | ansible.builtin.file: 45 | path: "{{ prelim_audit_logfile.stdout }}" 46 | mode: 'o-x,g-wx,o-rwx' 47 | owner: root 48 | group: root 49 | 50 | - name: "5.2.4.5 | PATCH | Ensure audit configuration files are 640 or more restrictive" 51 | when: 52 | - rhel8cis_rule_5_2_4_5 53 | tags: 54 | - level2-server 55 | - level2-workstation 56 | - patch 57 | - auditd 58 | - rule_5.2.4.5 59 | ansible.builtin.file: 60 | path: "{{ item.path }}" 61 | mode: 'g-wx,o-rwx' 62 | loop: "{{ prelim_auditd_conf_files.files }}" 63 | loop_control: 64 | label: "{{ item.path }}" 65 | 66 | - name: "5.2.4.6 | PATCH | Ensure audit configuration files are owned by root" 67 | when: 68 | - rhel8cis_rule_5_2_4_6 69 | tags: 70 | - level2-server 71 | - level2-workstation 72 | - patch 73 | - auditd 74 | - rule_5.2.4.6 75 | ansible.builtin.file: 76 | path: "{{ item.path }}" 77 | owner: root 78 | loop: "{{ prelim_auditd_conf_files.files }}" 79 | loop_control: 80 | label: "{{ item.path }}" 81 | 82 | - name: "5.2.4.7 | PATCH | Ensure audit configuration files belong to group root" 83 | when: 84 | - rhel8cis_rule_5_2_4_7 85 | tags: 86 | - level2-server 87 | - level2-workstation 88 | - patch 89 | - auditd 90 | - rule_5.2.4.7 91 | ansible.builtin.file: 92 | path: "{{ item.path }}" 93 | group: root 94 | loop: "{{ prelim_auditd_conf_files.files }}" 95 | loop_control: 96 | label: "{{ item.path }}" 97 | 98 | - name: "5.2.4.8 | PATCH | Ensure audit tools are 755 or more restrictive" 99 | when: 100 | - rhel8cis_rule_5_2_4_8 101 | tags: 102 | - level2-server 103 | - level2-workstation 104 | - patch 105 | - auditd 106 | - rule_5.2.4.8 107 | block: 108 | - name: "5.2.4.8 | AUDIT | Get audit binary file stat | get current mode" 109 | ansible.builtin.stat: 110 | path: "{{ item }}" 111 | register: discovered_audit_bins_mode 112 | loop: 113 | - /sbin/auditctl 114 | - /sbin/aureport 115 | - /sbin/ausearch 116 | - /sbin/autrace 117 | - /sbin/auditd 118 | - /sbin/augenrules 119 | 120 | - name: "5.2.4.8 | PATCH | Ensure audit tools are 755 or more restrictive | set if required" 121 | ansible.builtin.file: 122 | path: "{{ item.item }}" 123 | mode: 'g-w,o-w' 124 | loop: "{{ discovered_audit_bins_mode.results }}" 125 | loop_control: 126 | label: "{{ item.item }}" 127 | 128 | - name: "5.2.4.9 | PATCH | Ensure audit tools are owned by root" 129 | when: 130 | - rhel8cis_rule_5_2_4_9 131 | tags: 132 | - level2-server 133 | - level2-workstation 134 | - patch 135 | - auditd 136 | - rule_5.2.4.9 137 | ansible.builtin.file: 138 | path: "{{ item }}" 139 | owner: root 140 | group: root 141 | loop: 142 | - /sbin/auditctl 143 | - /sbin/aureport 144 | - /sbin/ausearch 145 | - /sbin/autrace 146 | - /sbin/auditd 147 | - /sbin/augenrules 148 | 149 | - name: "5.2.4.10 | PATCH | Ensure audit tools belong to group root" 150 | when: 151 | - rhel8cis_rule_5_2_4_10 152 | tags: 153 | - level2-server 154 | - level2-workstation 155 | - patch 156 | - auditd 157 | - rule_5.2.4.10 158 | ansible.builtin.file: 159 | path: "{{ item }}" 160 | group: root 161 | loop: 162 | - /sbin/auditctl 163 | - /sbin/aureport 164 | - /sbin/ausearch 165 | - /sbin/autrace 166 | - /sbin/auditd 167 | - /sbin/augenrules 168 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.1 | PATCH | Ensure AIDE is installed" 4 | when: 5 | - rhel8cis_config_aide 6 | - rhel8cis_rule_5_3_1 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - automated 11 | - aide 12 | - NIST800-53R5_AU-2 13 | - patch 14 | - rule_5.3.1 15 | block: 16 | - name: "5.3.1 | PATCH | Ensure AIDE is installed | Install AIDE" 17 | ansible.builtin.package: 18 | name: aide 19 | state: present 20 | 21 | - name: "5.3.1 | PATCH | Ensure AIDE is installed | Configure AIDE" 22 | ansible.builtin.shell: /usr/sbin/aide --init 23 | changed_when: false 24 | failed_when: false 25 | async: 45 26 | poll: 0 27 | args: 28 | creates: /var/lib/aide/aide.db.new.gz 29 | when: not ansible_check_mode 30 | 31 | - name: "5.3.1 | PATCH | Ensure AIDE is installed | copy AIDE DB" 32 | ansible.builtin.copy: 33 | src: /var/lib/aide/aide.db.new.gz 34 | dest: /var/lib/aide/aide.db.gz 35 | remote_src: true 36 | register: aide_db_cp 37 | failed_when: 38 | - not ansible_check_mode 39 | - aide_db_cp.failed 40 | 41 | - name: "5.3.2 | PATCH | Ensure filesystem integrity is regularly checked" 42 | when: 43 | - rhel8cis_rule_5_3_2 44 | - rhel8cis_config_aide 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - automated 49 | - aide 50 | - NIST800-53R5_AU-2 51 | - file_integrity 52 | - patch 53 | - rule_5.3.2 54 | block: 55 | - name: "5.3.2 | PATCH | Ensure filesystem integrity is regularly checked | cron" 56 | when: rhel8cis_aide_scan == "cron" 57 | ansible.builtin.cron: 58 | name: Run AIDE integrity check 59 | cron_file: "{{ rhel8cis_aide_cron_file }}" 60 | user: "{{ rhel8cis_aide_cron_user }}" 61 | minute: "{{ rhel8cis_aide_cron_minute | default('0') }}" 62 | hour: "{{ rhel8cis_aide_cron_hour | default('5') }}" 63 | day: "{{ rhel8cis_aide_cron_day | default('*') }}" 64 | month: "{{ rhel8cis_aide_cron_month| default('*') }}" 65 | weekday: "{{ rhel8cis_aide_cron_weekday | default('*') }}" 66 | job: "{{ rhel8cis_aide_cron_job }}" 67 | 68 | - name: "5.3.2 | PATCH | Ensure filesystem integrity is regularly checked | timer" 69 | when: rhel8cis_aide_scan == "timer" 70 | ansible.builtin.systemd: 71 | name: "{{ item.name }}" 72 | enabled: true 73 | state: "{{ item.state | default(omit)}}" 74 | loop: 75 | - { name: 'aidecheck.service' } 76 | - { name: 'aidecheck.timer', state: started } 77 | 78 | - name: "5.3.3 | Ensure cryptographic mechanisms are used to protect the integrity of audit tools" 79 | when: 80 | - rhel8cis_rule_5_3_3 81 | tags: 82 | - level1-server 83 | - level1-workstation 84 | - aide 85 | - file_integrity 86 | - patch 87 | - rule_5.3.3 88 | ansible.builtin.blockinfile: 89 | path: /etc/aide.conf 90 | marker: "# {mark} Audit tools - CIS benchmark - Ansible-lockdown" 91 | block: | 92 | /sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 93 | /sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 94 | /sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 95 | /sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 96 | /sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 97 | /sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 98 | validate: aide -D --config %s 99 | register: aide_file_integrity_check 100 | failed_when: 101 | - not ansible_check_mode 102 | - aide_file_integrity_check.failed 103 | -------------------------------------------------------------------------------- /tasks/section_5/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # 5.1 Configure Logging 4 | - name: "SECTION | 5.1.1.x | Configure rsyslog" 5 | ansible.builtin.import_tasks: 6 | file: cis_5.1.1.x.yml 7 | when: rhel8cis_syslog == 'rsyslog' 8 | 9 | - name: "SECTION | 5.1.2.x | Configure journald" 10 | ansible.builtin.import_tasks: 11 | file: cis_5.1.2.x.yml 12 | 13 | - name: "SECTION | 5.1.3 | Configure logile perms" 14 | ansible.builtin.import_tasks: 15 | file: cis_5.1.3.yml 16 | 17 | - name: "SECTION | 5.1.4 | Configure logrotate" 18 | ansible.builtin.import_tasks: 19 | file: cis_5.1.4.yml 20 | 21 | # 5.2 Configure System Accounting (auditd) 22 | - name: "SECTION | 5.2.1 | Ensure auditing is enabled" 23 | ansible.builtin.import_tasks: 24 | file: cis_5.2.1.x.yml 25 | when: 26 | - not system_is_container 27 | 28 | - name: "SECTION | 5.2.2 | Configure Data Retention" 29 | ansible.builtin.import_tasks: 30 | file: cis_5.2.2.x.yml 31 | when: 32 | - not system_is_container 33 | 34 | - name: "SECTION | 5.2.3.x | Configure auditd rules" 35 | ansible.builtin.import_tasks: 36 | file: cis_5.2.3.x.yml 37 | 38 | - name: "SECTION | 5.2.4.x | Audit file permissions" 39 | ansible.builtin.import_tasks: 40 | file: cis_5.2.4.x.yml 41 | 42 | - name: "SECTION | 5.3.x | Aide" 43 | ansible.builtin.import_tasks: 44 | file: cis_5.3.x.yml 45 | -------------------------------------------------------------------------------- /tasks/section_6/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 6.1 | System File Permissions" 4 | ansible.builtin.import_tasks: 5 | file: cis_6.1.x.yml 6 | 7 | - name: "SECTION | 6.2 | User and Group Settings" 8 | ansible.builtin.import_tasks: 9 | file: cis_6.2.x.yml 10 | -------------------------------------------------------------------------------- /tasks/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Install openscap 4 | ansible.builtin.dnf: 5 | state: present 6 | name: 7 | - openscap-scanner 8 | - scap-security-guide 9 | 10 | - name: Run CIS oscap scan and create /tmp/report.html 11 | ansible.builtin.shell: 12 | oscap xccdf eval \ 13 | --report /tmp/report.html 14 | --profile cis 15 | --fetch-remote-resources 16 | /usr/share/xml/scap/ssg/content/ssg-almalinux8-ds.xml 17 | changed_when: true 18 | no_log: false 19 | ignore_errors: true # noqa: ignore-errors 20 | -------------------------------------------------------------------------------- /tasks/warning_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # This task is used to create variables used in giving a warning summary for manual tasks 4 | # that need attention 5 | # 6 | # The warn_control_list and warn_count vars start life in vars/main.yml but get updated 7 | # as the tasks that have a warning complete 8 | # 9 | # Those two variables are used in the tasks/main.yml to display a list of warnings 10 | # 11 | # warn_control_id is set within the task itself and has the control ID as the value 12 | # 13 | # warn_control_list is the main variable to be used and is a list made up of the warn_control_id’s 14 | # 15 | # warn_count the main variable for the number of warnings and each time a warn_control_id is added 16 | # the count increases by a value of 1 17 | - name: "{{ warn_control_id }} | WARNING | Set fact for manual task warning." 18 | ansible.builtin.set_fact: 19 | warn_control_list: "{{ warn_control_list }} [{{ warn_control_id }}]" 20 | warn_count: "{{ warn_count | int + 1 }}" 21 | -------------------------------------------------------------------------------- /templates/audit/99_auditd.rules.j2: -------------------------------------------------------------------------------- 1 | ## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! 2 | 3 | # This template will set all of the auditd configurations via a handler in the role in one task instead of individually 4 | {% if rhel8cis_rule_5_2_3_1 %} 5 | -w /etc/sudoers -p wa -k scope 6 | -w /etc/sudoers.d/ -p wa -k scope 7 | {% endif %} 8 | {% if rhel8cis_rule_5_2_3_2 %} 9 | -a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation 10 | -a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation 11 | {% endif %} 12 | {% if rhel8cis_rule_5_2_3_3 %} 13 | -w {{ rhel8cis_sudolog_location }} -p wa -k sudo_log_file 14 | {% endif %} 15 | {% if rhel8cis_rule_5_2_3_4 %} 16 | -a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change 17 | -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time-change 18 | -w /etc/localtime -p wa -k time-change 19 | {% endif %} 20 | {% if rhel8cis_rule_5_2_3_5 %} 21 | -a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale 22 | -a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale 23 | -w /etc/issue -p wa -k system-locale 24 | -w /etc/issue.net -p wa -k system-locale 25 | -w /etc/hosts -p wa -k system-locale 26 | -w /etc/sysconfig/network -p wa -k system-locale 27 | -w /etc/sysconfig/network-scripts/ -p wa -k system-locale 28 | {% endif %} 29 | {% if rhel8cis_rule_5_2_3_6 %} 30 | {% for proc in discovered_privilege_procs.stdout_lines -%} 31 | -a always,exit -F path={{ proc }} -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k privileged 32 | {% endfor %} 33 | {% endif %} 34 | {% if rhel8cis_rule_5_2_3_7 %} 35 | -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ min_int_uid }} -F auid!=unset -F key=access 36 | -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ min_int_uid }} -F auid!=unset -F key=access 37 | -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>={{ min_int_uid }} -F auid!=unset -F key=access 38 | -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>={{ min_int_uid }} -F auid!=unset -F key=access 39 | {% endif %} 40 | {% if rhel8cis_rule_5_2_3_8 %} 41 | -w /etc/group -p wa -k identity 42 | -w /etc/passwd -p wa -k identity 43 | -w /etc/gshadow -p wa -k identity 44 | -w /etc/shadow -p wa -k identity 45 | -w /etc/security/opasswd -p wa -k identity 46 | {% endif %} 47 | {% if rhel8cis_rule_5_2_3_9 %} 48 | -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod 49 | -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod 50 | -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod 51 | -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>={{ min_int_uid }} -F auid!=unset -F key=perm_mod 52 | -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 53 | -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 54 | {% endif %} 55 | {% if rhel8cis_rule_5_2_3_10 %} 56 | -a always,exit -F arch=b32 -S mount -F auid>={{ min_int_uid }} -F auid!=unset -k mounts 57 | -a always,exit -F arch=b64 -S mount -F auid>={{ min_int_uid }} -F auid!=unset -k mounts 58 | {% endif %} 59 | {% if rhel8cis_rule_5_2_3_11 %} 60 | -w /var/run/utmp -p wa -k session 61 | -w /var/log/wtmp -p wa -k session 62 | -w /var/log/btmp -p wa -k session 63 | {% endif %} 64 | {% if rhel8cis_rule_5_2_3_12 %} 65 | -w /var/log/lastlog -p wa -k logins 66 | -w /var/run/faillock -p wa -k logins 67 | {% endif %} 68 | {% if rhel8cis_rule_5_2_3_13 %} 69 | -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>={{ min_int_uid }} -F auid!=unset -F key=delete 70 | -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>={{ min_int_uid }} -F auid!=unset -F key=delete 71 | {% endif %} 72 | {% if rhel8cis_rule_5_2_3_14 %} 73 | -w /etc/selinux/ -p wa -k MAC-policy 74 | -w /usr/share/selinux/ -p wa -k MAC-policy 75 | {% endif %} 76 | {% if rhel8cis_rule_5_2_3_15 %} 77 | -a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k perm_chng 78 | {% endif %} 79 | {% if rhel8cis_rule_5_2_3_16 %} 80 | -a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k perm_chng 81 | {% endif %} 82 | {% if rhel8cis_rule_5_2_3_17 %} 83 | -a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k perm_chng 84 | {% endif %} 85 | {% if rhel8cis_rule_5_2_3_18 %} 86 | -a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k usermod 87 | {% endif %} 88 | {% if rhel8cis_rule_5_2_3_19 %} 89 | -a always,exit -F arch=b32 -S init_module,finit_module,delete_module,create_module,query_module -F auid>={{ min_int_uid }} -F auid!=unset -k kernel_modules 90 | -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 91 | -a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>={{ min_int_uid }} -F auid!=unset -k kernel_modules 92 | {% endif %} 93 | {% if rhel8cis_rule_5_2_3_20 %} 94 | -e 2 95 | {% endif %} 96 | -------------------------------------------------------------------------------- /templates/chrony.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | # Use public servers from the pool.ntp.org project. 4 | # Please consider joining the pool (http://www.pool.ntp.org/join.html). 5 | {% for server in rhel8cis_time_synchronization_servers -%} 6 | server {{ server }} {{ rhel8cis_chrony_server_options }} 7 | {% endfor %} 8 | 9 | # Record the rate at which the system clock gains/losses time. 10 | driftfile /var/lib/chrony/drift 11 | 12 | # Allow the system clock to be stepped in the first three updates 13 | # if its offset is larger than 1 second. 14 | makestep 1.0 3 15 | 16 | # Enable kernel synchronization of the real-time clock (RTC). 17 | rtcsync 18 | 19 | # Enable hardware timestamping on all interfaces that support it. 20 | #hwtimestamp * 21 | 22 | # Increase the minimum number of selectable sources required to adjust 23 | # the system clock. 24 | #minsources 2 25 | 26 | # Allow NTP client access from local network. 27 | #allow 192.168.0.0/16 28 | 29 | # Serve time even if not synchronized to a time source. 30 | #local stratum 10 31 | 32 | # Specify file containing keys for NTP authentication. 33 | keyfile /etc/chrony.keys 34 | 35 | # Get TAI-UTC offset and leap seconds from the system tz database. 36 | leapsectz right/UTC 37 | 38 | # Specify directory for log files. 39 | logdir /var/log/chrony 40 | 41 | # Select which information is logged. 42 | #log measurements statistics tracking 43 | -------------------------------------------------------------------------------- /templates/etc/ansible/compliance_facts.j2: -------------------------------------------------------------------------------- 1 | # CIS Hardening Carried out 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | [lockdown_details] 6 | # Benchmark release 7 | Benchmark_release = CIS-{{ benchmark_version }} 8 | Benchmark_run_date = {{ '%Y-%m-%d - %H:%M:%S' | ansible.builtin.strftime }} 9 | # If options set (doesn't mean it ran all controls) 10 | level_1_hardening_enabled = {{ rhel8cis_level_1 }} 11 | level_2_hardening_enabled = {{ rhel8cis_level_2 }} 12 | 13 | {% if ansible_run_tags | length > 0 %} 14 | # If tags used to stipulate run level 15 | {% if 'level1-server' in ansible_run_tags %} 16 | Level_1_Server_tag_run = true 17 | {% endif %} 18 | {% if 'level2-server' in ansible_run_tags %} 19 | Level_2_Server_tag_run = true 20 | {% endif %} 21 | {% if 'level1-workstation' in ansible_run_tags %} 22 | Level_1_workstation_tag_run = true 23 | {% endif %} 24 | {% if 'level2-workstation' in ansible_run_tags %} 25 | Level_2_workstation_tag_run = true 26 | {% endif %} 27 | {% endif %} 28 | 29 | [lockdown_audit_details] 30 | {% if run_audit %} 31 | # Audit run 32 | audit_run_date = {{ '%Y-%m-%d - %H:%M:%S' | ansible.builtin.strftime }} 33 | audit_file_local_location = {{ audit_log_dir }} 34 | {% if not audit_only %} 35 | audit_summary = {{ post_audit_results }} 36 | {% endif %} 37 | {% if fetch_audit_output %} 38 | audit_files_centralized_location = {{ audit_output_destination }} 39 | {% endif %} 40 | {% endif %} 41 | -------------------------------------------------------------------------------- /templates/etc/crypto-policies/policies/modules/NO-SHA1.pmod.j2: -------------------------------------------------------------------------------- 1 | # Added as part of RHEL8-CIS by ansible-lockdown 2 | # Sponsored by MindPointGroup 3 | 4 | hash = -SHA1 5 | sign = -*-SHA1 6 | sha1_in_certs = 0 7 | -------------------------------------------------------------------------------- /templates/etc/crypto-policies/policies/modules/NO-SSHCBC.pmod.j2: -------------------------------------------------------------------------------- 1 | # Added as part of RHEL8-CIS by ansible-lockdown 2 | # Sponsored by MindPointGroup 3 | 4 | # This is a subpolicy to disable all CBC mode ciphers 5 | # for the SSH protocol (libssh and OpenSSH) 6 | cipher@SSH = -*-CBC 7 | -------------------------------------------------------------------------------- /templates/etc/crypto-policies/policies/modules/NO-WEAKMAC.pmod.j2: -------------------------------------------------------------------------------- 1 | # Added as part of RHEL8-CIS by ansible-lockdown 2 | # Sponsored by MindPointGroup 3 | 4 | # This is a subpolicy to disable weak macs 5 | mac = -*-64 6 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-media-automount.j2: -------------------------------------------------------------------------------- 1 | [org/gnome/desktop/media-handling] 2 | automount-open=false 3 | 4 | [org/gnome/desktop/media-handling] 5 | automount=false 6 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-media-autorun.j2: -------------------------------------------------------------------------------- 1 | [org/gnome/desktop/media-handling] 2 | autorun-never=true 3 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-screensaver.j2: -------------------------------------------------------------------------------- 1 | [org/gnome/desktop/session] 2 | # Number of seconds of inactivity before the screen goes blank 3 | # Set to 0 seconds if you want to deactivate the screensaver. 4 | idle-delay=uint32 {{ rhel8cis_screensaver_idle_delay }} 5 | 6 | # Specify the dconf path 7 | [org/gnome/desktop/screensaver] 8 | # Number of seconds after the screen is blank before locking the screen 9 | lock-delay=uint32 {{ rhel8cis_screensaver_lock_delay }} 10 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/locks/00-automount_lock.j2: -------------------------------------------------------------------------------- 1 | # Lock automount settings 2 | 3 | # Lock desktop media-handling automount setting 4 | /org/gnome/desktop/media-handling/automount 5 | # Lock desktop media-handling automount-open setting 6 | /org/gnome/desktop/media-handling/automount-open 7 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/locks/00-autorun_lock.j2: -------------------------------------------------------------------------------- 1 | # Lock desktop media-handling settings 2 | # Lock desktop media-handling autorun-never setting 3 | /org/gnome/desktop/media-handling/autorun-never 4 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/locks/00-screensaver_lock.j2: -------------------------------------------------------------------------------- 1 | # Lock desktop screensaver idle-delay setting 2 | /org/gnome/desktop/session/idle-delay 3 | # Lock desktop screensaver lock-delay setting 4 | /org/gnome/desktop/screensaver/lock-delay 5 | -------------------------------------------------------------------------------- /templates/etc/issue.j2: -------------------------------------------------------------------------------- 1 | {{ rhel8cis_warning_banner }} 2 | -------------------------------------------------------------------------------- /templates/etc/issue.net.j2: -------------------------------------------------------------------------------- 1 | {{ rhel8cis_warning_banner }} 2 | -------------------------------------------------------------------------------- /templates/etc/motd.j2: -------------------------------------------------------------------------------- 1 | {{ rhel8cis_warning_banner }} 2 | -------------------------------------------------------------------------------- /templates/etc/systemd/system/tmp.mount.j2: -------------------------------------------------------------------------------- 1 | ## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! 2 | 3 | # SPDX-License-Identifier: LGPL-2.1+ 4 | # 5 | # This file is part of systemd. 6 | # 7 | # systemd is free software; you can redistribute it and/or modify it 8 | # under the terms of the GNU Lesser General Public License as published by 9 | # the Free Software Foundation; either version 2.1 of the License, or 10 | # (at your option) any later version. 11 | 12 | [Unit] 13 | Description=Temporary Directory (/tmp) 14 | Documentation=man:hier(7) 15 | Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems 16 | ConditionPathIsSymbolicLink=!/tmp 17 | DefaultDependencies=no 18 | Conflicts=umount.target 19 | Before=local-fs.target umount.target 20 | After=swap.target 21 | 22 | [Mount] 23 | What=tmpfs 24 | Where=/tmp 25 | Type=tmpfs 26 | Options=mode=1777,strictatime,{% if rhel8cis_rule_1_1_2_1_2 %}nodev,{% endif %}{% if rhel8cis_rule_1_1_2_1_3 %}nosuid,{% endif %}{% if rhel8cis_rule_1_1_2_1_4 %}noexec{% endif %} 27 | 28 | # Make 'systemctl enable tmp.mount' work: 29 | [Install] 30 | WantedBy=local-fs.target 31 | -------------------------------------------------------------------------------- /templates/ntp.conf.j2: -------------------------------------------------------------------------------- 1 | ## This file is managed by Ansible, YOUR CHANGED WILL BE LOST! 2 | 3 | # For more information about this file, see the man pages 4 | # ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5). 5 | 6 | driftfile /var/lib/ntp/drift 7 | 8 | # Permit time synchronization with our time source, but do not 9 | # permit the source to query or modify the service on this system. 10 | #restrict default nomodify notrap nopeer noquery 11 | restrict -4 default kod nomodify notrap nopeer noquery 12 | restrict -6 default kod nomodify notrap nopeer noquery 13 | 14 | # Permit all access over the loopback interface. This could 15 | # be tightened as well, but to do so would effect some of 16 | # the administrative functions. 17 | restrict 127.0.0.1 18 | restrict ::1 19 | 20 | # Hosts on local network are less restricted. 21 | #restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap 22 | 23 | # Use public servers from the pool.ntp.org project. 24 | # Please consider joining the pool (http://www.pool.ntp.org/join.html). 25 | {% for server in rhel8cis_time_synchronization_servers -%} 26 | server {{ server }} {{ rhel8cis_ntp_server_options }} 27 | {% endfor %} 28 | 29 | #broadcast 192.168.1.255 autokey # broadcast server 30 | #broadcastclient # broadcast client 31 | #broadcast 224.0.1.1 autokey # multicast server 32 | #multicastclient 224.0.1.1 # multicast client 33 | #manycastserver 239.255.254.254 # manycast server 34 | #manycastclient 239.255.254.254 autokey # manycast client 35 | 36 | # Enable public key cryptography. 37 | #crypto 38 | 39 | includefile /etc/ntp/crypto/pw 40 | 41 | # Key file containing the keys and key identifiers used when operating 42 | # with symmetric key cryptography. 43 | keys /etc/ntp/keys 44 | 45 | # Specify the key identifiers which are trusted. 46 | #trustedkey 4 8 42 47 | 48 | # Specify the key identifier to use with the ntpdc utility. 49 | #requestkey 8 50 | 51 | # Specify the key identifier to use with the ntpq utility. 52 | #controlkey 8 53 | 54 | # Enable writing of statistics records. 55 | #statistics clockstats cryptostats loopstats peerstats 56 | 57 | # Disable the monitoring facility to prevent amplification attacks using ntpdc 58 | # monlist command when default restrict does not include the noquery flag. See 59 | # CVE-2013-5211 for more details. 60 | # Note: Monitoring will not be disabled with the limited restriction flag. 61 | disable monitor 62 | -------------------------------------------------------------------------------- /vars/AlmaLinux.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # OS Specific Settings 4 | rpm_gpg_key: /etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux 5 | rpm_packager: "AlmaLinux Packaging Team" 6 | # found on https://wiki.almalinux.org/cloud/Generic-cloud.html#download-and-verification note lower cas 7 | rpm_key: "2ae81e8aced7258b" # pragma: allowlist secret https://almalinux.org/blog/2023-12-20-almalinux-8-key-update/ 8 | -------------------------------------------------------------------------------- /vars/CentOS.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # OS Specific Settings 3 | 4 | rpm_gpg_key: /etc/pki/rpm-gpg/RPM-GPG-KEY-{{ ansible_distribution | lower }}official 5 | rpm_packager: "The CentOS Project" 6 | 7 | # found on https://www.centos.org/keys/ 8 | rpm_key: "8483C65D" # pragma: allowlist secret 9 | -------------------------------------------------------------------------------- /vars/OracleLinux.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # OS Specific Settings 3 | rpm_gpg_key: /etc/pki/rpm-gpg/RPM-GPG-KEY-oracle 4 | rpm_packager: "(none)" 5 | 6 | rpm_key: "82562ea9ad986da3" # pragma: allowlist secret 7 | -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # OS Specific Settings 3 | rpm_gpg_key: /etc/pki/rpm-gpg/RPM-GPG-KEY-{{ ansible_distribution | lower }}-release 4 | rpm_packager: "Red Hat, Inc" 5 | 6 | # found on https://access.redhat.com/security/team/key/ 7 | rpm_key: "199e2f91fd431d51" # pragma: allowlist secret 8 | -------------------------------------------------------------------------------- /vars/Rocky.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # OS Specific Settings 3 | 4 | rpm_gpg_key: /etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial 5 | rpm_packager: "infrastructure@rockylinux.org" 6 | 7 | # found on https://rockylinux.org/keys/ note lower case 8 | rpm_key: "15af5dac6d745a60" # pragma: allowlist secret 9 | -------------------------------------------------------------------------------- /vars/audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | #### Audit Configuration Settings #### 4 | 5 | # Timeout for those cmds that take longer to run where timeout set 6 | audit_cmd_timeout: 120000 7 | 8 | # if get_audit_binary_method == download change accordingly 9 | audit_bin_url: "https://github.com/goss-org/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-" 10 | 11 | ### Goss Audit Benchmark file ### 12 | ## managed by the control audit_content 13 | # git 14 | audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" 15 | audit_git_version: "benchmark_{{ benchmark_version }}" 16 | 17 | ## Goss configuration information 18 | # Where the goss audit configuration will be stored - NOTE benchmark-audit is expected 19 | audit_conf_dir: "{{ audit_conf_dest | default('/opt') }}/{{ benchmark }}-Audit" 20 | 21 | # If changed these can affect other products 22 | pre_audit_outfile: "{{ audit_log_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" 23 | post_audit_outfile: "{{ audit_log_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" 24 | 25 | ## The following should not need changing 26 | 27 | ### Audit binary settings ### 28 | audit_bin_version: 29 | release: v0.4.8 30 | AMD64_checksum: 'sha256:85d00b7bba5f175bec95de7dfe1f71f8f25204914aad4c6f03c8457868eb6e2f' 31 | ARM64_checksum: 'sha256:bca8c898bfd35b94c51455ece6193c95e2cd7b2b183ac2047b2d76291e73e47d' 32 | audit_bin_path: /usr/local/bin/ 33 | audit_bin: "{{ audit_bin_path }}goss" 34 | audit_format: json 35 | 36 | audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_facts.hostname }}.yml" 37 | audit_results: | 38 | The{% if not audit_only %} pre remediation{% endif %} audit results are: {{ pre_audit_results }} 39 | {% if not audit_only %}The post remediation audit results are: {{ post_audit_results }}{% endif %} 40 | 41 | Full breakdown can be found in {{ audit_log_dir }} 42 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for RHEL8-CIS 3 | 4 | min_ansible_version: 2.11.1 5 | rhel8cis_allowed_crypto_policies: 6 | - 'DEFAULT' 7 | - 'FUTURE' 8 | - 'FIPS' 9 | 10 | # default setting, this should not be changed 11 | # and is overridden if a task that changed sets the value if required. 12 | reboot_required: false 13 | 14 | rhel8cis_legacy_boot: false 15 | 16 | # Used to control warning summary 17 | warn_control_list: "" 18 | warn_count: 0 19 | --------------------------------------------------------------------------------