├── .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 ├── site.yml ├── tasks ├── LE_audit_setup.yml ├── audit_only.yml ├── auditd.yml ├── check_prereqs.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.1.x.yml │ ├── cis_1.2.2.x.yml │ ├── cis_1.3.1.x.yml │ ├── cis_1.4.x.yml │ ├── cis_1.5.x.yml │ ├── cis_1.6.x.yml │ ├── cis_1.7.x.yml │ └── main.yml ├── section_2 │ ├── cis_2.1.x.yml │ ├── cis_2.2.x.yml │ ├── cis_2.3.1.x.yml │ ├── cis_2.3.2.x.yml │ ├── cis_2.3.3.x.yml │ ├── cis_2.4.1.x.yml │ ├── cis_2.4.2.x.yml │ └── main.yml ├── section_3 │ ├── cis_3.1.x.yml │ ├── cis_3.2.x.yml │ ├── cis_3.3.x.yml │ └── main.yml ├── section_4 │ ├── cis_4.1.x.yml │ ├── cis_4.2.x.yml │ ├── cis_4.3.1.x.yml │ ├── cis_4.3.2.x.yml │ ├── cis_4.3.3.x.yml │ └── main.yml ├── section_5 │ ├── cis_5.1.x.yml │ ├── cis_5.2.x.yml │ ├── cis_5.3.1.x.yml │ ├── cis_5.3.2.x.yml │ ├── cis_5.3.3.1.x.yml │ ├── cis_5.3.3.2.x.yml │ ├── cis_5.3.3.3.x.yml │ ├── cis_5.3.3.4.x.yml │ ├── cis_5.4.1.x.yml │ ├── cis_5.4.2.x.yml │ ├── cis_5.4.3.x.yml │ └── main.yml ├── section_6 │ ├── cis_6.1.x.yml │ ├── cis_6.2.1.1.x.yml │ ├── cis_6.2.1.2.x.yml │ ├── cis_6.2.2.x.yml │ ├── cis_6.3.1.x.yml │ ├── cis_6.3.2.x.yml │ ├── cis_6.3.3.x.yml │ ├── cis_6.3.4.x.yml │ └── main.yml ├── section_7 │ ├── cis_7.1.x.yml │ ├── cis_7.2.x.yml │ └── main.yml └── warning_facts.yml ├── templates ├── ansible_vars_goss.yml.j2 ├── audit │ ├── 98_auditd_exception.rules.j2 │ └── 99_auditd.rules.j2 ├── etc │ ├── ansible │ │ └── compliance_facts.j2 │ ├── chrony.conf.j2 │ ├── chrony │ │ └── sources.d │ │ │ ├── pool.sources.j2 │ │ │ └── server.sources.j2 │ ├── cron.d │ │ └── aide.cron.j2 │ ├── dconf │ │ └── db │ │ │ ├── 00-automount_lock.j2 │ │ │ ├── 00-autorun_lock.j2 │ │ │ ├── 00-media-automount.j2 │ │ │ ├── 00-media-autorun.j2 │ │ │ ├── 00-screensaver.j2 │ │ │ ├── 00-screensaver_lock.j2 │ │ │ └── gdm.d │ │ │ └── 01-banner-message.j2 │ ├── grub.d │ │ └── 00_user.j2 │ ├── issue.j2 │ ├── issue.net.j2 │ ├── modprobe.d │ │ └── modprobe.conf.j2 │ ├── motd.j2 │ ├── security │ │ └── pwquality.conf.d │ │ │ ├── 50-pwcomplexity.conf.j2 │ │ │ ├── 50-pwdictcheck.conf.j2 │ │ │ ├── 50-pwdifok.conf.j2 │ │ │ ├── 50-pwlength.conf.j2 │ │ │ ├── 50-pwmaxsequence.conf.j2 │ │ │ ├── 50-pwquality_enforce.conf.j2 │ │ │ ├── 50-pwrepeat.conf.j2 │ │ │ └── 50-pwroot.conf.j2 │ ├── sysctl.d │ │ ├── 60-disable_ipv6.conf.j2 │ │ ├── 60-kernel_sysctl.conf.j2 │ │ ├── 60-netipv4_sysctl.conf.j2 │ │ └── 60-netipv6_sysctl.conf.j2 │ └── systemd │ │ ├── journald.conf.d │ │ ├── forwardtosyslog.conf.j2 │ │ ├── rotation.conf.j2 │ │ └── storage.conf.j2 │ │ ├── system │ │ └── tmp.mount.j2 │ │ └── timesyncd.conf.d │ │ └── 50-timesyncd.conf.j2 ├── fs_with_cves.sh └── usr │ └── share │ └── pam-configs │ ├── faillock.j2 │ ├── faillock_notify.j2 │ ├── pam_unix.j2 │ ├── pwhistory.j2 │ └── pwquality.j2 └── vars ├── Debian.yml ├── audit.yml ├── is_container.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parseable: true 4 | quiet: true 5 | skip_list: 6 | - 'package-latest' 7 | - 'risky-shell-pipe' 8 | use_default_rules: true 9 | verbosity: 0 10 | -------------------------------------------------------------------------------- /.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 | pwd 97 | env: 98 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 99 | OSVAR: ${{ vars.OSVAR }} 100 | benchmark_type: ${{ vars.BENCHMARK_TYPE }} 101 | 102 | - name: Tofu init 103 | id: init 104 | run: tofu init 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 validate 111 | id: validate 112 | run: tofu validate 113 | env: 114 | # Imported from GitHub variables this is used to load the relevant OS.tfvars file 115 | OSVAR: ${{ vars.OSVAR }} 116 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 117 | 118 | - name: Tofu apply 119 | id: apply 120 | env: 121 | OSVAR: ${{ vars.OSVAR }} 122 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 123 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 124 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 125 | run: tofu apply -var-file "${OSVAR}.tfvars" --auto-approve -input=false 126 | 127 | ## Debug Section 128 | - name: DEBUG - Show Ansible hostfile 129 | if: env.ENABLE_DEBUG == 'true' 130 | run: cat hosts.yml 131 | 132 | # Aws deployments taking a while to come up insert sleep or playbook fails 133 | 134 | - name: Sleep to allow system to come up 135 | run: sleep ${{ vars.BUILD_SLEEPTIME }} 136 | 137 | # Run the Ansible playbook 138 | - name: Run_Ansible_Playbook 139 | env: 140 | ANSIBLE_HOST_KEY_CHECKING: "false" 141 | ANSIBLE_DEPRECATION_WARNINGS: "false" 142 | run: | 143 | /opt/ansible_${{ env.ANSIBLE_VERSION }}_venv/bin/ansible-playbook -i hosts.yml --private-key ~/.ssh/le_runner ../../../site.yml 144 | 145 | # Remove test system - User secrets to keep if necessary 146 | 147 | - name: Tofu Destroy 148 | if: always() && env.ENABLE_DEBUG == 'false' 149 | env: 150 | OSVAR: ${{ vars.OSVAR }} 151 | TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} 152 | TF_VAR_privsubnet_id: ${{ secrets.AWS_PRIVSUBNET_ID }} 153 | TF_VAR_vpc_secgrp_id: ${{ secrets.AWS_VPC_SECGRP_ID }} 154 | run: tofu destroy -var-file "${OSVAR}.tfvars" --auto-approve -input=false 155 | -------------------------------------------------------------------------------- /.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 | name: Detect AWS Credentials 15 | - id: detect-private-key 16 | name: Detect Private Keys 17 | 18 | # git checks 19 | - id: check-merge-conflict 20 | name: Check for merge conflicts 21 | - id: check-added-large-files 22 | name: Check for Large files 23 | - id: check-case-conflict 24 | name: Check case conflict 25 | 26 | # General checks 27 | - id: trailing-whitespace 28 | name: Trim Trailing Whitespace 29 | description: This hook trims trailing whitespace. 30 | entry: trailing-whitespace-fixer 31 | language: python 32 | types: [text] 33 | args: [--markdown-linebreak-ext=md] 34 | - id: end-of-file-fixer 35 | name: Ensure line at end of file 36 | 37 | # Scan for passwords 38 | - repo: https://github.com/Yelp/detect-secrets 39 | rev: v1.5.0 40 | hooks: 41 | - id: detect-secrets 42 | 43 | - repo: https://github.com/gitleaks/gitleaks 44 | rev: v8.26.0 45 | hooks: 46 | - id: gitleaks 47 | 48 | - repo: https://github.com/ansible-community/ansible-lint 49 | rev: v25.5.0 50 | hooks: 51 | - id: ansible-lint 52 | name: Ansible-lint 53 | description: This hook runs ansible-lint. 54 | entry: python3 -m ansiblelint --force-color site.yml -c .ansible-lint 55 | language: python 56 | # do not pass files to ansible-lint, see: 57 | # https://github.com/ansible/ansible-lint/issues/611 58 | pass_filenames: false 59 | always_run: true 60 | # additional_dependencies: 61 | # https://github.com/pre-commit/pre-commit/issues/1526 62 | # If you want to use specific version of ansible-core or ansible, feel 63 | # free to override `additional_dependencies` in your own hook config 64 | # file. 65 | # - ansible-core>=2.10.1 66 | 67 | - repo: https://github.com/adrienverge/yamllint.git 68 | rev: v1.37.1 # or higher tag 69 | hooks: 70 | - id: yamllint 71 | name: Check YAML Lint 72 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | ignore: | 5 | tests/ 6 | molecule/ 7 | .github/ 8 | .gitlab-ci.yml 9 | *molecule.yml 10 | rules: 11 | braces: 12 | max-spaces-inside: 1 13 | level: error 14 | brackets: 15 | max-spaces-inside: 1 16 | level: error 17 | comments: 18 | ignore-shebangs: true 19 | min-spaces-from-content: 1 # prettier compatibility 20 | comments-indentation: enable 21 | empty-lines: 22 | max: 1 23 | indentation: 24 | # Requiring 2 space indentation 25 | spaces: 2 26 | # Requiring consistent indentation within a file, either indented or not 27 | indent-sequences: consistent 28 | key-duplicates: enable 29 | line-length: disable 30 | new-line-at-end-of-file: enable 31 | new-lines: 32 | type: unix 33 | octal-values: 34 | forbid-implicit-octal: true # yamllint defaults to false 35 | forbid-explicit-octal: true 36 | trailing-spaces: enable 37 | truthy: 38 | allowed-values: ['true', 'false'] 39 | check-keys: true 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changes to deb12CIS 2 | 3 | ## 1.0.3 4 | - Issues Addressed 5 | - #61 & #63 thank you @elcign 6 | 7 | ## 1.0.2 - Based on CIS v1.0.1 8 | - New workflows 9 | - fetch audit options added 10 | - ansible facts created 11 | - fileglob options rewritten 12 | - issues addressed 13 | 14 | ## 1.0.1 - Based on CIS v1.0.1 15 | 16 | - Initial release of playbook 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Mindpoint Group - A Tyto Athene Company / Ansible Lockdown 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 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | galaxy_info: 4 | author: "MindPoint Group" 5 | description: "Apply the Debian 12 CIS" 6 | company: "MindPoint Group" 7 | license: MIT 8 | role_name: deb12_cis 9 | namespace: mindpointgroup 10 | min_ansible_version: 2.10.1 11 | platforms: 12 | - name: Debian 13 | versions: 14 | - "bookworm" 15 | galaxy_tags: 16 | - system 17 | - security 18 | - stig 19 | - hardening 20 | - benchmark 21 | - compliance 22 | - complianceascode 23 | - disa 24 | - debian 25 | - cis 26 | collections: 27 | - community.general 28 | - community.crypto 29 | - ansible.posix 30 | dependencies: [] 31 | -------------------------------------------------------------------------------- /site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Apply ansible-lockdown hardening 4 | hosts: all 5 | become: true 6 | roles: 7 | - role: "{{ playbook_dir }}" 8 | -------------------------------------------------------------------------------- /tasks/LE_audit_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Pre Audit Setup | Set audit package name 3 | block: 4 | - name: Pre Audit Setup | Set audit package name | 64bit 5 | when: ansible_facts.machine == "x86_64" 6 | ansible.builtin.set_fact: 7 | audit_pkg_arch_name: AMD64 8 | 9 | - name: Pre Audit Setup | Set audit package name | ARM64 10 | when: (ansible_facts.machine == "arm64" or ansible_facts.machine == "aarch64") 11 | ansible.builtin.set_fact: 12 | audit_pkg_arch_name: ARM64 13 | 14 | - name: Pre Audit Setup | Download audit binary 15 | when: get_audit_binary_method == 'download' 16 | ansible.builtin.get_url: 17 | url: "{{ audit_bin_url }}{{ audit_pkg_arch_name }}" 18 | dest: "{{ audit_bin }}" 19 | owner: root 20 | group: root 21 | checksum: "{{ audit_bin_version[audit_pkg_arch_name + '_checksum'] }}" 22 | mode: 'u+x,go-w' 23 | 24 | - name: Pre Audit Setup | Copy audit binary 25 | when: get_audit_binary_method == 'copy' 26 | ansible.builtin.copy: 27 | src: "{{ audit_bin_copy_location }}/goss-linux-{{ audit_pkg_arch_name }}" 28 | dest: "{{ audit_bin }}" 29 | owner: root 30 | group: root 31 | mode: 'u+x,go-w' 32 | -------------------------------------------------------------------------------- /tasks/audit_only.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Audit_Only | Create local Directories for hosts 4 | when: fetch_audit_files 5 | ansible.builtin.file: 6 | mode: 'u+x,go-w' 7 | path: "{{ audit_capture_files_dir }}/{{ inventory_hostname }}" 8 | recurse: true 9 | state: directory 10 | delegate_to: localhost 11 | become: false 12 | 13 | - name: Audit_only | Show Audit Summary 14 | when: audit_only 15 | ansible.builtin.debug: 16 | msg: "{{ audit_results.split('\n') }}" 17 | 18 | - name: Audit_only | Stop Playbook Audit Only selected 19 | when: audit_only 20 | ansible.builtin.meta: end_play 21 | -------------------------------------------------------------------------------- /tasks/auditd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "POST | AUDITD | Set supported_syscalls variable" 4 | ansible.builtin.shell: ausyscall --dump | awk '{print $2}' 5 | changed_when: false 6 | failed_when: discovered_auditd_syscalls.rc not in [ 0, 1 ] 7 | register: discovered_auditd_syscalls 8 | 9 | - name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file 10 | ansible.builtin.stat: 11 | path: /etc/audit/rules.d/99_auditd.rules 12 | register: discovered_auditd_rules_file 13 | 14 | - name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | setup file 15 | vars: 16 | supported_syscalls: "{{ discovered_auditd_syscalls.stdout_lines }}" 17 | ansible.builtin.template: 18 | src: audit/99_auditd.rules.j2 19 | dest: /etc/audit/rules.d/99_auditd.rules 20 | owner: root 21 | group: root 22 | mode: 'u-x,g-w,o-rwx' 23 | diff: "{{ discovered_auditd_rules_file.stat.exists }}" # Only run diff if not a new file 24 | register: discovered_auditd_template_updated 25 | notify: 26 | - Auditd immutable check 27 | - Audit immutable fact 28 | - Restart auditd 29 | 30 | - name: POST | AUDITD | Add Warning count for changes to template file | Warn Count # noqa no-handler 31 | when: 32 | - discovered_auditd_template_updated.changed 33 | - discovered_auditd_rules_file.stat.exists 34 | ansible.builtin.import_tasks: 35 | file: warning_facts.yml 36 | vars: 37 | warn_control_id: 'Auditd template updated, see diff output for details' 38 | 39 | - name: POST | AUDITD | Apply auditd template will for section 4.1.3 - only required rules will be added | stat file 40 | ansible.builtin.stat: 41 | path: /etc/audit/rules.d/98_auditd_exceptions.rules 42 | register: discovered_auditd_exception_file 43 | 44 | - name: POST | Set up auditd user logging exceptions | setup file 45 | when: 46 | - deb12cis_allow_auditd_uid_user_exclusions 47 | - deb12cis_auditd_uid_exclude | length > 0 48 | ansible.builtin.template: 49 | src: audit/98_auditd_exception.rules.j2 50 | dest: /etc/audit/rules.d/98_auditd_exceptions.rules 51 | owner: root 52 | group: root 53 | mode: 'u-x,g-w,o-rwx' 54 | diff: "{{ discovered_auditd_exception_file.stat.exists }}" 55 | notify: Restart auditd 56 | -------------------------------------------------------------------------------- /tasks/check_prereqs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "PREREQ | If required install libselinux package to manage file changes." 4 | when: 5 | - '"libselinux-python3" not in ansible_facts.packages' 6 | ansible.builtin.package: 7 | name: libselinux-python3 8 | state: present 9 | -------------------------------------------------------------------------------- /tasks/fetch_audit_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Stage to copy audit output to a centralised location 4 | 5 | - name: "POST | FETCH | 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: "POST | FETCH | 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_copy_state 29 | loop: 30 | - "{{ pre_audit_outfile }}" 31 | - "{{ post_audit_outfile }}" 32 | 33 | - name: "POST | FETCH | 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: "POST | FETCH | 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: "POST | FETCH | 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 | tags: 5 | - always 6 | block: 7 | - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" 8 | ansible.builtin.command: cat /etc/passwd 9 | changed_when: false 10 | check_mode: false 11 | register: prelim_passwd_file_audit 12 | 13 | - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" 14 | ansible.builtin.set_fact: 15 | prelim_passwd: "{{ prelim_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" 16 | loop: "{{ prelim_passwd_file_audit.stdout_lines }}" 17 | vars: 18 | ld_passwd_regex: >- 19 | ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) 20 | ld_passwd_yaml: | # pragma: allowlist secret 21 | id: >-4 22 | \g 23 | password: >-4 24 | \g 25 | uid: \g 26 | gid: \g 27 | gecos: >-4 28 | \g 29 | dir: >-4 30 | \g 31 | shell: >-4 32 | \g 33 | -------------------------------------------------------------------------------- /tasks/post.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Post tasks 3 | 4 | - name: "POST | AUDIT | Gather the package facts after remediation" 5 | tags: always 6 | ansible.builtin.package_facts: 7 | manager: auto 8 | 9 | - name: "POST | PATCH | Update sysctl" 10 | when: 11 | - deb12cis_sysctl_update 12 | - not system_is_container 13 | - "'procps' in ansible_facts.packages" 14 | tags: always 15 | ansible.builtin.template: 16 | src: "etc/sysctl.d/{{ item }}.j2" 17 | dest: "/etc/sysctl.d/{{ item }}" 18 | owner: root 19 | group: root 20 | mode: 'u-x,go-rwx' 21 | notify: Reload sysctl 22 | loop: 23 | - 60-kernel_sysctl.conf 24 | - 60-disable_ipv6.conf 25 | - 60-netipv4_sysctl.conf 26 | - 60-netipv6_sysctl.conf 27 | 28 | - name: "POST | PATCH | Flush handlers" 29 | ansible.builtin.meta: flush_handlers 30 | 31 | - name: "POST | PATCH |reboot system if changes require it and not skipped" 32 | when: change_requires_reboot 33 | tags: always 34 | vars: 35 | warn_control_id: Reboot_required 36 | block: 37 | - name: POST | PATCH | Reboot system if changes require it and not skipped 38 | when: not skip_reboot 39 | ansible.builtin.reboot: 40 | 41 | - name: "POST | WARN | Warning a reboot required but skip option set" 42 | when: skip_reboot 43 | ansible.builtin.debug: 44 | msg: "Warning!! changes have been made that require a reboot to be implemented but skip reboot was set - Can affect compliance check results" 45 | changed_when: true 46 | 47 | - name: "POST | WARN | Warning a reboot required but skip option set | warning count" 48 | when: skip_reboot 49 | ansible.builtin.import_tasks: 50 | file: warning_facts.yml 51 | -------------------------------------------------------------------------------- /tasks/post_remediation_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Post Audit | Run post_remediation {{ benchmark }} audit # noqa name[template] 4 | ansible.builtin.command: "{{ 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: 'u-x,go=r' 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: audit_format == "json" 22 | block: 23 | - name: Post Audit | Capture audit data if json format 24 | ansible.builtin.shell: grep -E '"summary-line.*Count:.*Failed' "{{ post_audit_outfile }}" | cut -d'"' -f4 25 | changed_when: false 26 | register: post_audit_summary 27 | 28 | - name: Post Audit | Set Fact for audit summary 29 | ansible.builtin.set_fact: 30 | post_audit_results: "{{ post_audit_summary.stdout }}" 31 | 32 | - name: Post Audit | Capture audit data if documentation format 33 | when: audit_format == "documentation" 34 | block: 35 | - name: Post Audit | Capture audit data if documentation format 36 | ansible.builtin.shell: tail -2 "{{ pre_audit_outfile }}" | tac | tr '\n' ' ' 37 | changed_when: false 38 | register: post_audit_summary 39 | 40 | - name: Post Audit | Set Fact for audit summary 41 | ansible.builtin.set_fact: 42 | post_audit_results: "{{ post_audit_summary.stdout }}" 43 | -------------------------------------------------------------------------------- /tasks/pre_remediation_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Pre Audit Setup | Setup the LE audit 4 | when: setup_audit 5 | tags: setup_audit 6 | ansible.builtin.include_tasks: 7 | file: LE_audit_setup.yml 8 | 9 | - name: Pre Audit Setup | Ensure existence of {{ audit_conf_dir }} # noqa name[template] 10 | ansible.builtin.file: 11 | path: "{{ audit_conf_dir }}" 12 | mode: 'go-w' 13 | state: directory 14 | 15 | - name: Pre Audit Setup | If using git for content set up 16 | when: audit_content == 'git' 17 | block: 18 | - name: Pre Audit Setup | Install git 19 | ansible.builtin.package: 20 | name: git 21 | state: present 22 | 23 | - name: Pre Audit Setup | Retrieve audit content files from git 24 | ansible.builtin.git: 25 | repo: "{{ audit_file_git }}" 26 | dest: "{{ audit_conf_dir }}" 27 | version: "{{ audit_git_version }}" 28 | 29 | - name: Pre Audit Setup | Copy to audit content files to server 30 | when: audit_content == 'copy' 31 | ansible.builtin.copy: 32 | src: "{{ audit_conf_source }}" 33 | dest: "{{ audit_conf_dest }}" 34 | mode: preserve 35 | 36 | - name: Pre Audit Setup | Unarchive audit content files on server 37 | when: audit_content == 'archive' 38 | ansible.builtin.unarchive: 39 | src: "{{ audit_conf_source }}" 40 | dest: "{{ audit_conf_dest }}" 41 | 42 | - name: Pre Audit Setup | Get audit content from url 43 | when: audit_content == 'get_url' 44 | ansible.builtin.unarchive: 45 | src: "{{ audit_conf_source }}" 46 | dest: "{{ audit_conf_dest }}/{{ benchmark }}-Audit" 47 | remote_src: "{{ (audit_conf_source is contains('http')) | ternary(true, false) }}" 48 | extra_opts: "{{ (audit_conf_source is contains('github')) | ternary('--strip-components=1', []) }}" 49 | 50 | - name: Pre Audit Setup | Check Goss is available 51 | when: run_audit 52 | block: 53 | - name: Pre Audit Setup | Check for goss file 54 | ansible.builtin.stat: 55 | path: "{{ audit_bin }}" 56 | register: prelim_goss_available 57 | 58 | - name: Pre Audit Setup | If audit ensure goss is available 59 | when: not prelim_goss_available.stat.exists 60 | ansible.builtin.assert: 61 | msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" 62 | 63 | - name: Pre Audit Setup | Copy ansible default vars values to test audit 64 | when: run_audit 65 | tags: 66 | - goss_template 67 | - run_audit 68 | ansible.builtin.template: 69 | src: ansible_vars_goss.yml.j2 70 | dest: "{{ audit_vars_path }}" 71 | mode: 'go-rwx' 72 | 73 | - name: Pre Audit | Run pre_remediation audit {{ benchmark }} # noqa name[template] 74 | ansible.builtin.command: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ pre_audit_outfile }} -g \"{{ group_names }}\"" # noqa yaml[line-length] 75 | changed_when: true 76 | environment: 77 | AUDIT_BIN: "{{ audit_bin }}" 78 | AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" 79 | AUDIT_FILE: goss.yml 80 | 81 | - name: Pre Audit | Capture audit data if json format 82 | when: audit_format == "json" 83 | block: 84 | - name: Pre Audit | Capture audit data if json format 85 | ansible.builtin.shell: grep -E '\"summary-line.*Count:.*Failed' "{{ pre_audit_outfile }}" | cut -d'"' -f4 86 | changed_when: false 87 | register: pre_audit_summary 88 | 89 | - name: Pre Audit | Set Fact for audit summary 90 | ansible.builtin.set_fact: 91 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 92 | 93 | - name: Pre Audit | Capture audit data if documentation format 94 | when: audit_format == "documentation" 95 | block: 96 | - name: Pre Audit | Capture audit data if documentation format 97 | ansible.builtin.shell: tail -2 "{{ pre_audit_outfile }}" | tac | tr '\n' ' ' 98 | changed_when: false 99 | register: pre_audit_summary 100 | 101 | - name: Pre Audit | Set Fact for audit summary 102 | ansible.builtin.set_fact: 103 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 104 | 105 | - name: Audit_Only | Run Audit Only 106 | when: audit_only 107 | ansible.builtin.import_tasks: 108 | file: audit_only.yml 109 | -------------------------------------------------------------------------------- /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: deb12cis_rule_1_1_2_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - audit 9 | - mounts 10 | - rule_1.1.2.1.1 11 | - NIST800-53R5_CM-7 12 | vars: 13 | warn_control_id: "1.1.2.1.1" 14 | required_mount: "/tmp" 15 | block: 16 | - name: "1.1.2.1.1 | AUDIT | Ensure /tmp is a separate partition | check for mount" 17 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 18 | changed_when: false 19 | failed_when: discovered_tmp_mount.rc not in [ 0, 1 ] 20 | register: discovered_tmp_mount 21 | 22 | - name: "1.1.2.1.1 | AUDIT | Ensure /tmp is a separate partition | Absent" 23 | when: discovered_tmp_mount is undefined or discovered_tmp_mount.rc != 0 24 | ansible.builtin.debug: 25 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 26 | 27 | - name: "1.1.2.1.1 | AUDIT | Ensure /tmp is a separate partition | Present" 28 | when: discovered_tmp_mount is undefined or discovered_tmp_mount.rc != 0 29 | ansible.builtin.import_tasks: 30 | file: warning_facts.yml 31 | 32 | # via fstab 33 | - name: "1.1.2.1.2 | PATCH | Ensure nodev option set on /tmp partition" 34 | when: 35 | - prelim_mount_point_fs_and_options[mount_point] is defined 36 | - not prelim_mount_point_fs_and_options[mount_point]['src'] == "tmpfs" 37 | - deb12cis_rule_1_1_2_1_2 38 | - not deb12cis_tmp_svc 39 | tags: 40 | - level1-server 41 | - level1-workstation 42 | - patch 43 | - mounts 44 | - rule_1.1.2.1.2 45 | - NIST800-53R5_AC-3 46 | - NIST800-53R5_MP-2 47 | vars: 48 | mount_point: "/tmp" 49 | required_option: nodev 50 | notify: &mount_option_notify 51 | - "Remount {{ mount_point }}" 52 | ansible.builtin.set_fact: &mount_option_set_fact 53 | prelim_mount_point_fs_and_options: | 54 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 55 | changed_when: &mount_option_changed_when 56 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 57 | 58 | - name: "1.1.2.1.3 | PATCH | Ensure nosuid option set on /tmp partition" 59 | when: 60 | - prelim_mount_point_fs_and_options[mount_point] is defined 61 | - not prelim_mount_point_fs_and_options[mount_point]['src'] == "tmpfs" 62 | - deb12cis_rule_1_1_2_1_3 63 | - not deb12cis_tmp_svc 64 | tags: 65 | - level1-server 66 | - level1-workstation 67 | - patch 68 | - mounts 69 | - rule_1.1.2.1.3 70 | - NIST800-53R5_AC-3 71 | - NIST800-53R5_MP-2 72 | vars: 73 | mount_point: "/tmp" 74 | required_option: nosuid 75 | notify: *mount_option_notify 76 | ansible.builtin.set_fact: 77 | <<: *mount_option_set_fact 78 | changed_when: *mount_option_changed_when 79 | 80 | - name: "1.1.2.1.4 | PATCH | Ensure noexec option set on /tmp partition" 81 | when: 82 | - prelim_mount_point_fs_and_options[mount_point] is defined 83 | - not prelim_mount_point_fs_and_options[mount_point]['src'] == "tmpfs" 84 | - deb12cis_rule_1_1_2_1_4 85 | - not deb12cis_tmp_svc 86 | tags: 87 | - level1-server 88 | - level1-workstation 89 | - patch 90 | - mounts 91 | - rule_1.1.2.1.4 92 | - NIST800-53R5_AC-3 93 | - NIST800-53R5_MP-2 94 | vars: 95 | mount_point: "/tmp" 96 | required_option: noexec 97 | notify: *mount_option_notify 98 | ansible.builtin.set_fact: 99 | <<: *mount_option_set_fact 100 | changed_when: *mount_option_changed_when 101 | 102 | # via systemd 103 | - name: | 104 | "1.1.2.1.1 | PATCH | Ensure /tmp is configured | if a service 105 | 1.1.2.1.2 | PATCH | Ensure nodev option set on /tmp partition 106 | 1.1.2.1.3 | PATCH | Ensure noexec option set on /tmp partition 107 | 1.1.2.1.4 | PATCH | Ensure nosuid option set on /tmp partition" 108 | when: 109 | - deb12cis_tmp_svc 110 | - deb12cis_rule_1_1_2_1_1 or deb12cis_rule_1_1_2_1_2 or deb12cis_rule_1_1_2_1_3 or deb12cis_rule_1_1_2_1_4 111 | tags: 112 | - level1-server 113 | - level1-workstation 114 | - patch 115 | - mounts 116 | - rule_1.1.2.1.1 117 | - rule_1.1.2.1.2 118 | - rule_1.1.2.1.3 119 | - rule_1.1.2.1.4 120 | - NIST800-53R5_AC-3 121 | - NIST800-53R5_MP-2 122 | vars: 123 | mount_point: "/tmp" 124 | ansible.builtin.template: 125 | src: etc/systemd/system/tmp.mount.j2 126 | dest: /etc/systemd/system/tmp.mount 127 | owner: root 128 | group: root 129 | mode: 'go-wx' 130 | notify: *mount_option_notify 131 | -------------------------------------------------------------------------------- /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 | - deb12cis_rule_1_1_2_2_1 6 | - required_mount not in prelim_mount_names 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - audit 11 | - mounts 12 | - rule_1.1.2.2.1 13 | - NIST800-53R5_CM-7 14 | vars: 15 | warn_control_id: "1.1.2.2.1" 16 | required_mount: "/dev/shm" 17 | block: 18 | - name: "1.1.2.2.1 | AUDIT | Ensure /dev/shm is a separate partition | check for mount" 19 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 20 | changed_when: false 21 | failed_when: discovered_dev_shm_mount.rc not in [ 0, 1 ] 22 | register: discovered_dev_shm_mount 23 | 24 | - name: "1.1.2.2.1 | AUDIT | Ensure /dev/shm is a separate partition | Absent" 25 | when: discovered_dev_shm_mount is undefined 26 | ansible.builtin.debug: 27 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 28 | 29 | - name: "1.1.2.2.1 | AUDIT | Ensure /dev/shm is a separate partition | Present" 30 | when: discovered_dev_shm_mount is undefined 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | 34 | - name: "1.1.2.2.2 | PATCH | Ensure nodev option set on /dev/shm partition" 35 | when: 36 | - prelim_mount_point_fs_and_options[mount_point] is defined 37 | - deb12cis_rule_1_1_2_2_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - mounts 43 | - rule_1.1.2.2.2 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | vars: 47 | mount_point: "/dev/shm" 48 | required_option: nodev 49 | notify: &mount_option_notify 50 | - "Remount {{ mount_point }}" 51 | ansible.builtin.set_fact: &mount_option_set_fact 52 | prelim_mount_point_fs_and_options: | 53 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 54 | changed_when: &mount_option_changed_when 55 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 56 | 57 | - name: "1.1.2.2.3 | PATCH | Ensure nosuid option set on /dev/shm partition" 58 | when: 59 | - prelim_mount_point_fs_and_options[mount_point] is defined 60 | - deb12cis_rule_1_1_2_2_3 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - mounts 66 | - rule_1.1.2.2.3 67 | - NIST800-53R5_AC-3 68 | - NIST800-53R5_MP-2 69 | vars: 70 | mount_point: "/dev/shm" 71 | required_option: nosuid 72 | notify: *mount_option_notify 73 | ansible.builtin.set_fact: 74 | <<: *mount_option_set_fact 75 | changed_when: *mount_option_changed_when 76 | 77 | - name: "1.1.2.2.4 | PATCH | Ensure noexec option set on /dev/shm partition" 78 | when: 79 | - prelim_mount_point_fs_and_options[mount_point] is defined 80 | - deb12cis_rule_1_1_2_2_4 81 | tags: 82 | - level1-server 83 | - level1-workstation 84 | - patch 85 | - mounts 86 | - rule_1.1.2.2.4 87 | - NIST800-53R5_AC-3 88 | - NIST800-53R5_MP-2 89 | vars: 90 | mount_point: "/dev/shm" 91 | required_option: noexec 92 | notify: *mount_option_notify 93 | ansible.builtin.set_fact: 94 | <<: *mount_option_set_fact 95 | changed_when: *mount_option_changed_when 96 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "1.1.2.3.1 | PATCH | Ensure /home is a separate partition" 3 | when: 4 | - deb12cis_rule_1_1_2_3_1 5 | - required_mount not in prelim_mount_names 6 | tags: 7 | - level1-server 8 | - level1-workstation 9 | - audit 10 | - mounts 11 | - rule_1.1.2.3.1 12 | - NIST800-53R5_CM-7 13 | vars: 14 | warn_control_id: "1.1.2.3.1" 15 | required_mount: "/home" 16 | block: 17 | - name: "1.1.2.3.1 | AUDIT | Ensure /home is a separate partition | check for mount" 18 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 19 | changed_when: false 20 | failed_when: discovered_home_mount.rc not in [ 0, 1 ] 21 | register: discovered_home_mount 22 | 23 | - name: "1.1.2.3.1 | AUDIT | Ensure /home is a separate partition | Absent" 24 | when: discovered_dev_shm_mount is undefined 25 | ansible.builtin.debug: 26 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 27 | 28 | - name: "1.1.2.3.1 | AUDIT | Ensure /home is a separate partition | Present" 29 | when: discovered_dev_shm_mount is undefined 30 | ansible.builtin.import_tasks: 31 | file: warning_facts.yml 32 | 33 | - name: "1.1.2.3.2 | PATCH | Ensure nodev option set on /home partition" 34 | when: 35 | - prelim_mount_point_fs_and_options[mount_point] is defined 36 | - deb12cis_rule_1_1_2_3_2 37 | tags: 38 | - level1-server 39 | - level1-workstation 40 | - patch 41 | - mounts 42 | - rule_1.1.2.3.2 43 | - NIST800-53R5_AC-3 44 | - NIST800-53R5_MP-2 45 | vars: 46 | mount_point: "/home" 47 | required_option: nodev 48 | notify: &mount_option_notify 49 | - "Remount {{ mount_point }}" 50 | ansible.builtin.set_fact: &mount_option_set_fact 51 | prelim_mount_point_fs_and_options: | 52 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 53 | changed_when: &mount_option_changed_when 54 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 55 | 56 | - name: "1.1.2.3.3 | PATCH | Ensure nosuid option set on /home partition" 57 | when: 58 | - prelim_mount_point_fs_and_options[mount_point] is defined 59 | - deb12cis_rule_1_1_2_3_3 60 | tags: 61 | - level1-server 62 | - level1-workstation 63 | - patch 64 | - mounts 65 | - rule_1.1.2.3.3 66 | - NIST800-53R5_AC-3 67 | - NIST800-53R5_MP-2 68 | vars: 69 | mount_point: "/home" 70 | required_option: nosuid 71 | notify: *mount_option_notify 72 | ansible.builtin.set_fact: 73 | <<: *mount_option_set_fact 74 | changed_when: *mount_option_changed_when 75 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.4.1 | PATCH | Ensure /var is a separate partition" 4 | when: 5 | - deb12cis_rule_1_1_2_4_1 6 | - required_mount not in prelim_mount_names 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - audit 11 | - mounts 12 | - rule_1.1.2.4.1 13 | - NIST800-53R5_CM-7 14 | vars: 15 | warn_control_id: '1.1.2.4.1' 16 | required_mount: '/var' 17 | block: 18 | - name: "1.1.2.4.1 | AUDIT | Ensure /var is a separate partition | check for mount" 19 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 20 | changed_when: false 21 | failed_when: discovered_var_mount.rc not in [ 0, 1 ] 22 | register: discovered_var_mount 23 | 24 | - name: "1.1.2.4.1 | AUDIT | Ensure /var is a separate partition | Absent" 25 | when: discovered_dev_shm_mount is undefined 26 | ansible.builtin.debug: 27 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 28 | 29 | - name: "1.1.2.4.1 | AUDIT | Ensure /var is a separate partition | Present" 30 | when: discovered_dev_shm_mount is undefined 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | 34 | - name: "1.1.2.4.2 | PATCH | Ensure nodev option set on /var partition" 35 | when: 36 | - prelim_mount_point_fs_and_options[mount_point] is defined 37 | - deb12cis_rule_1_1_2_4_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - mounts 43 | - rule_1.1.2.4.2 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | vars: 47 | mount_point: "/var" 48 | required_option: nodev 49 | notify: &mount_option_notify 50 | - "Remount {{ mount_point }}" 51 | ansible.builtin.set_fact: &mount_option_set_fact 52 | prelim_mount_point_fs_and_options: | 53 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 54 | changed_when: &mount_option_changed_when 55 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 56 | 57 | - name: "1.1.2.4.3 | PATCH | Ensure nosuid option set on /var partition" 58 | when: 59 | - prelim_mount_point_fs_and_options[mount_point] is defined 60 | - deb12cis_rule_1_1_2_4_3 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - mounts 66 | - rule_1.1.2.4.3 67 | - NIST800-53R5_AC-3 68 | - NIST800-53R5_MP-2 69 | vars: 70 | mount_point: "/var" 71 | required_option: nosuid 72 | notify: *mount_option_notify 73 | ansible.builtin.set_fact: 74 | <<: *mount_option_set_fact 75 | changed_when: *mount_option_changed_when 76 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.5.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.5.1 | PATCH | Ensure /var/tmp is a separate partition" 4 | when: 5 | - deb12cis_rule_1_1_2_5_1 6 | - required_mount not in prelim_mount_names 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - audit 11 | - mounts 12 | - rule_1.1.2.5.1 13 | - NIST800-53R5_CM-7 14 | vars: 15 | warn_control_id: '1.1.2.5.1' 16 | required_mount: '/var/tmp' 17 | block: 18 | - name: "1.1.2.5.1 | AUDIT | Ensure /var/tmp is a separate partition | check for mount" 19 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 20 | changed_when: false 21 | failed_when: discovered_var_tmp_mount.rc not in [ 0, 1 ] 22 | register: discovered_var_tmp_mount 23 | 24 | - name: "1.1.2.5.1 | AUDIT | Ensure /var/tmp is a separate partition | Absent" 25 | when: discovered_var_tmp_mount is undefined 26 | ansible.builtin.debug: 27 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 28 | 29 | - name: "1.1.2.5.1 | AUDIT | Ensure /var/tmp is a separate partition | Present" 30 | when: discovered_var_tmp_mount is undefined 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | 34 | - name: "1.1.2.5.2 | PATCH | Ensure nodev option set on /var/tmp partition" 35 | when: 36 | - prelim_mount_point_fs_and_options[mount_point] is defined 37 | - deb12cis_rule_1_1_2_5_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - mounts 43 | - rule_1.1.2.5.2 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | vars: 47 | mount_point: "/var/tmp" 48 | required_option: nodev 49 | notify: &mount_option_notify 50 | - "Remount {{ mount_point }}" 51 | ansible.builtin.set_fact: &mount_option_set_fact 52 | prelim_mount_point_fs_and_options: | 53 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 54 | changed_when: &mount_option_changed_when 55 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 56 | 57 | - name: "1.1.2.5.3 | PATCH | Ensure nosuid option set on /var/tmp partition" 58 | when: 59 | - prelim_mount_point_fs_and_options[mount_point] is defined 60 | - deb12cis_rule_1_1_2_5_3 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - mounts 66 | - rule_1.1.2.5.3 67 | - NIST800-53R5_AC-3 68 | - NIST800-53R5_MP-2 69 | vars: 70 | mount_point: "/var/tmp" 71 | required_option: nosuid 72 | notify: *mount_option_notify 73 | ansible.builtin.set_fact: 74 | <<: *mount_option_set_fact 75 | changed_when: *mount_option_changed_when 76 | 77 | - name: "1.1.2.5.4 | PATCH | Ensure noexec option set on /var/tmp partition" 78 | when: 79 | - prelim_mount_point_fs_and_options[mount_point] is defined 80 | - deb12cis_rule_1_1_2_5_4 81 | tags: 82 | - level1-server 83 | - level1-workstation 84 | - patch 85 | - mounts 86 | - rule_1.1.2.5.4 87 | - NIST800-53R5_AC-3 88 | - NIST800-53R5_MP-2 89 | vars: 90 | mount_point: "/var/tmp" 91 | required_option: noexec 92 | notify: *mount_option_notify 93 | ansible.builtin.set_fact: 94 | <<: *mount_option_set_fact 95 | changed_when: *mount_option_changed_when 96 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.6.1 | PATCH | Ensure /var/log is a separate partition" 4 | when: 5 | - deb12cis_rule_1_1_2_6_1 6 | - required_mount not in prelim_mount_names 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - audit 11 | - mounts 12 | - rule_1.1.2.6.1 13 | - NIST800-53R5_CM-7 14 | vars: 15 | warn_control_id: '1.1.2.6.1' 16 | required_mount: '/var/log' 17 | block: 18 | - name: "1.1.2.6.1 | AUDIT | Ensure /var/log is a separate partition | check for mount" 19 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 20 | changed_when: false 21 | failed_when: discovered_var_log_mount.rc not in [ 0, 1 ] 22 | register: discovered_var_log_mount 23 | 24 | - name: "1.1.2.6.1 | AUDIT | Ensure /var/log is a separate partition | Absent" 25 | when: discovered_var_log_mount is undefined 26 | ansible.builtin.debug: 27 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 28 | 29 | - name: "1.1.2.6.1 | AUDIT | Ensure /var/log is a separate partition | Present" 30 | when: discovered_var_log_mount is undefined 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | 34 | - name: "1.1.2.6.2 | PATCH | Ensure nodev option set on /var/log partition" 35 | when: 36 | - prelim_mount_point_fs_and_options[mount_point] is defined 37 | - deb12cis_rule_1_1_2_6_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - mounts 43 | - rule_1.1.2.6.2 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | vars: 47 | mount_point: "/var/log" 48 | required_option: nodev 49 | notify: &mount_option_notify 50 | - "Remount {{ mount_point }}" 51 | ansible.builtin.set_fact: &mount_option_set_fact 52 | prelim_mount_point_fs_and_options: | 53 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 54 | changed_when: &mount_option_changed_when 55 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 56 | 57 | - name: "1.1.2.6.3 | PATCH | Ensure nosuid option set on /var/log partition" 58 | when: 59 | - prelim_mount_point_fs_and_options[mount_point] is defined 60 | - deb12cis_rule_1_1_2_6_3 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - mounts 66 | - rule_1.1.2.6.3 67 | - NIST800-53R5_AC-3 68 | - NIST800-53R5_MP-2 69 | vars: 70 | mount_point: "/var/log" 71 | required_option: nosuid 72 | notify: *mount_option_notify 73 | ansible.builtin.set_fact: 74 | <<: *mount_option_set_fact 75 | changed_when: *mount_option_changed_when 76 | 77 | - name: "1.1.2.6.4 | PATCH | Ensure noexec option set on /var/log partition" 78 | when: 79 | - prelim_mount_point_fs_and_options[mount_point] is defined 80 | - deb12cis_rule_1_1_2_6_4 81 | tags: 82 | - level1-server 83 | - level1-workstation 84 | - patch 85 | - mounts 86 | - rule_1.1.2.6.4 87 | - NIST800-53R5_AC-3 88 | - NIST800-53R5_MP-2 89 | vars: 90 | mount_point: "/var/log" 91 | required_option: noexec 92 | notify: *mount_option_notify 93 | ansible.builtin.set_fact: 94 | <<: *mount_option_set_fact 95 | changed_when: *mount_option_changed_when 96 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.7.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.7.1 | PATCH | Ensure /var/log/audit is a separate partition" 4 | when: 5 | - deb12cis_rule_1_1_2_7_1 6 | - required_mount not in prelim_mount_names 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - audit 11 | - mounts 12 | - rule_1.1.2.7.1 13 | - NIST800-53R5_CM-7 14 | vars: 15 | warn_control_id: '1.1.2.7.1' 16 | required_mount: '/var/log/audit' 17 | block: 18 | - name: "1.1.2.7.1 | AUDIT | Ensure /var/log/audit is a separate partition | check for mount" 19 | ansible.builtin.command: findmnt -kn "{{ required_mount }}" 20 | changed_when: false 21 | failed_when: discovered_var_log_audit_mount.rc not in [ 0, 1 ] 22 | register: discovered_var_log_audit_mount 23 | 24 | - name: "1.1.2.7.1 | AUDIT | Ensure /var/log/audit is a separate partition | Absent" 25 | when: discovered_var_log_audit_mount is undefined 26 | ansible.builtin.debug: 27 | msg: "Warning!! {{ required_mount }} is not mounted on a separate partition" 28 | 29 | - name: "1.1.2.7.1 | AUDIT | Ensure /var/log/audit is a separate partition | Present" 30 | when: discovered_var_log_audit_mount is undefined 31 | ansible.builtin.import_tasks: 32 | file: warning_facts.yml 33 | 34 | - name: "1.1.2.7.2 | PATCH | Ensure nodev option set on /var/log/audit partition" 35 | when: 36 | - prelim_mount_point_fs_and_options[mount_point] is defined 37 | - deb12cis_rule_1_1_2_7_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - mounts 43 | - rule_1.1.2.7.2 44 | - NIST800-53R5_AC-3 45 | - NIST800-53R5_MP-2 46 | vars: 47 | mount_point: "/var/log/audit" 48 | required_option: nodev 49 | notify: &mount_option_notify 50 | - "Remount {{ mount_point }}" 51 | ansible.builtin.set_fact: &mount_option_set_fact 52 | prelim_mount_point_fs_and_options: | 53 | {{ prelim_mount_point_fs_and_options | combine({mount_point: {'options': (prelim_mount_point_fs_and_options[mount_point]['options'] + [required_option])}}, recursive=True) }} 54 | changed_when: &mount_option_changed_when 55 | - required_option not in prelim_mount_point_fs_and_options[mount_point]['original_options'] 56 | 57 | - name: "1.1.2.7.3 | PATCH | Ensure nosuid option set on /var/log/audit partition" 58 | when: 59 | - prelim_mount_point_fs_and_options[mount_point] is defined 60 | - deb12cis_rule_1_1_2_7_3 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - mounts 66 | - rule_1.1.2.7.3 67 | - NIST800-53R5_AC-3 68 | - NIST800-53R5_MP-2 69 | vars: 70 | mount_point: "/var/log/audit" 71 | required_option: nosuid 72 | notify: *mount_option_notify 73 | ansible.builtin.set_fact: 74 | <<: *mount_option_set_fact 75 | changed_when: *mount_option_changed_when 76 | 77 | - name: "1.1.2.7.4 | PATCH | Ensure noexec option set on /var/log/audit partition" 78 | when: 79 | - prelim_mount_point_fs_and_options[mount_point] is defined 80 | - deb12cis_rule_1_1_2_7_4 81 | tags: 82 | - level1-server 83 | - level1-workstation 84 | - patch 85 | - mounts 86 | - rule_1.1.2.7.4 87 | - NIST800-53R5_AC-3 88 | - NIST800-53R5_MP-2 89 | vars: 90 | mount_point: "/var/log/audit" 91 | required_option: noexec 92 | notify: *mount_option_notify 93 | ansible.builtin.set_fact: 94 | <<: *mount_option_set_fact 95 | changed_when: *mount_option_changed_when 96 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.2.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.2.1.1 | AUDIT | Ensure GPG keys are configured" 4 | when: deb12cis_rule_1_2_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - manual 9 | - patch 10 | - rule_1.2.1.1 11 | - NIST800-53R5_SI-2 12 | vars: 13 | warn_control_id: '1.2.1.2' 14 | block: 15 | - name: "1.2.1.1 | AUDIT | Ensure GPG keys are configured | list installed pubkey keys" 16 | ansible.builtin.command: "apt-key list" 17 | changed_when: false 18 | failed_when: false 19 | register: discovered_apt_keys 20 | 21 | - name: "1.2.1.1 | AUDIT | Ensure GPG keys are configured | Display key list" 22 | ansible.builtin.debug: 23 | msg: 24 | - "Warning!! Below are the apt gpg keys. Please review and make sure all align with site policy" 25 | - "{{ discovered_apt_keys.stdout_lines }}" 26 | 27 | - name: "1.2.1.1 | AUDIT | Ensure GPG keys are configured | Warn Count" 28 | ansible.builtin.import_tasks: 29 | file: warning_facts.yml 30 | 31 | - name: "1.2.1.2 | AUDIT | Ensure package manager repositories are configured" 32 | when: deb12cis_rule_1_2_1_2 33 | tags: 34 | - level1-server 35 | - level1-workstation 36 | - manual 37 | - audit 38 | - rule_1.2.1.2 39 | - NIST800-53R5_SI-2 40 | vars: 41 | warn_control_id: '1.2.1.2' 42 | block: 43 | - name: "1.2.1.2 | AUDIT | Ensure package manager repositories are configured | Get repo list" 44 | ansible.builtin.command: apt-cache policy 45 | changed_when: false 46 | failed_when: false 47 | register: discovered_apt_config 48 | check_mode: false 49 | 50 | - name: "1.2.1.2 | AUDIT | Ensure package manager repositories are configured | Display repo list" 51 | ansible.builtin.debug: 52 | msg: 53 | - "Warning!! Below are the configured repos. Please review and make sure all align with site policy" 54 | - "{{ discovered_apt_config.stdout_lines }}" 55 | 56 | - name: "1.2.1.2 | AUDIT | Ensure package manager repositories are configured | Warn Count" 57 | ansible.builtin.import_tasks: 58 | file: warning_facts.yml 59 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.2.2.1 | PATCH | Ensure updates, patches, and additional security software are installed" 4 | when: 5 | - deb12cis_rule_1_2_2_1 6 | - not system_is_ec2 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_1.2.2.1 12 | - NIST800-53R5_SI-2 13 | ansible.builtin.package: 14 | name: "*" 15 | state: latest 16 | notify: Change_requires_reboot 17 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.3.1.1 | PATCH | Ensure AppArmor is installed" 4 | when: 5 | - deb12cis_rule_1_3_1_1 6 | - not deb12cis_apparmor_disable 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_1.3.1.1 12 | - NIST800-53R5_AC-3 13 | ansible.builtin.package: 14 | name: 15 | - apparmor 16 | - apparmor-utils 17 | state: present 18 | 19 | - name: "1.3.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration" 20 | when: 21 | - deb12cis_rule_1_3_1_2 22 | - not deb12cis_apparmor_disable 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - scored 27 | - patch 28 | - rule_1.3.1.2 29 | - NIST800-53R5_AC-3 30 | ansible.builtin.lineinfile: 31 | path: /etc/default/grub 32 | regexp: '^GRUB_CMDLINE_LINUX_DEFAULT="((?:(?!{{ item }}).)*?)"$' 33 | line: GRUB_CMDLINE_LINUX_DEFAULT="\1 apparmor=1 security=apparmor" 34 | backrefs: true 35 | loop: 36 | - apparmor=1 security=apparmor 37 | notify: Update-grub 38 | 39 | # Controls 1.3.1.4 and 1.3.1.3 target the same setting and thus should not be run together. 40 | # Because control 1.3.1.4 is stricter than 1.3.1.3, we need to change the order -- 41 | # control 1.3.1.4 then registers the fact that is has run and thus keeps 1.3.1.3 from running. 42 | 43 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing" 44 | when: 45 | - deb12cis_rule_1_3_1_4 46 | - not deb12cis_apparmor_disable 47 | tags: 48 | - level2-server 49 | - level2-workstation 50 | - scored 51 | - patch 52 | - rule_1.3.1.4 53 | - apparmor 54 | block: 55 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Make sure that 1.3.1.3 is not run" 56 | ansible.builtin.set_fact: 57 | control_1_3_1_4_was_run: true 58 | deb12cis_apparmor_enforce_only: true 59 | changed_when: false 60 | 61 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Get pre apply enforce count" 62 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 63 | changed_when: false 64 | failed_when: false 65 | register: discovered_apparmor_pre_count 66 | 67 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Apply enforcing to /etc/apparmor.d profiles" 68 | ansible.builtin.shell: aa-enforce /etc/apparmor.d/* 69 | changed_when: false 70 | failed_when: false 71 | 72 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Get post apply enforce count" 73 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 74 | changed_when: false 75 | failed_when: false 76 | register: discovered_apparmor_post_count 77 | 78 | - name: "1.3.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | This flags for idempotency" 79 | when: discovered_apparmor_post_count.stdout 80 | ansible.builtin.debug: 81 | msg: Changed! The profiles in /etc/apparmor.d were set to enforcing 82 | changed_when: true 83 | 84 | - name: "1.3.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode" 85 | when: 86 | - deb12cis_rule_1_3_1_3 87 | - not deb12cis_apparmor_disable 88 | - not control_1_3_1_4_was_run 89 | tags: 90 | - level1-server 91 | - level1-workstation 92 | - patch 93 | - rule_1.3.1.3 94 | - apparmor 95 | block: 96 | - name: "1.3.1.3 | AUDIT | Ensure all AppArmor Profiles are in enforce or complain | Set deb12cis_apparmor_enforce_only true for GOSS" 97 | when: 98 | - deb12cis_apparmor_mode == "enforce" 99 | ansible.builtin.set_fact: 100 | deb12cis_apparmor_enforce_only: true 101 | changed_when: false 102 | 103 | - name: "1.3.1.3 | AUDIT | Ensure all AppArmor Profiles are in enforce or complain | Set deb12cis_apparmor_enforce_only false for GOSS" 104 | when: 105 | - deb12cis_apparmor_mode == "complain" 106 | ansible.builtin.set_fact: 107 | deb12cis_apparmor_enforce_only: false 108 | changed_when: false 109 | 110 | - name: "1.3.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Get pre apply enforce count" 111 | ansible.builtin.shell: apparmor_status | grep "profiles are in {{ deb12cis_apparmor_mode }} mode" | tr -d -c 0-9 112 | changed_when: false 113 | failed_when: false 114 | register: discovered_apparmor_pre_count 115 | 116 | - name: "1.3.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Apply complaining/enforcing to /etc/apparmor.d profiles" 117 | ansible.builtin.shell: aa-{{ deb12cis_apparmor_mode }} /etc/apparmor.d/* 118 | changed_when: false 119 | failed_when: false 120 | 121 | - name: "1.3.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Get post apply enforce count" 122 | ansible.builtin.shell: apparmor_status | grep "profiles are in {{ deb12cis_apparmor_mode }} mode" | tr -d -c 0-9 123 | changed_when: false 124 | failed_when: false 125 | register: discovered_apparmor_post_count 126 | 127 | - name: "1.3.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | This flags for idempotency" 128 | when: discovered_apparmor_post_count.stdout 129 | ansible.builtin.debug: 130 | msg: Changed! The profiles in /etc/apparmor.d were set to {{ deb12cis_apparmor_mode }} mode 131 | changed_when: true 132 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.4.1 | PATCH | Ensure bootloader password is set" 4 | when: 5 | - deb12cis_set_boot_pass 6 | - deb12cis_rule_1_4_1 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - grub 11 | - patch 12 | - rule_1.4.1 13 | - NIST800-53R5_AC-3 14 | block: 15 | - name: "1.4.1 | PATCH | Ensure bootloader password is set" 16 | ansible.builtin.template: 17 | src: etc/grub.d/00_user.j2 18 | dest: "{{ deb12cis_grub_user_file }}" 19 | owner: root 20 | group: root 21 | mode: 'u+x,go-w' 22 | notify: Update-grub 23 | 24 | - name: "1.4.1 | PATCH | Ensure bootloader password is set | allow unrestricted boot" 25 | when: not deb12cis_ask_passwd_to_boot 26 | ansible.builtin.lineinfile: 27 | path: "/etc/grub.d/10_linux" 28 | regexp: '(^CLASS="--class gnu-linux --class gnu --class os).*"$' 29 | line: '\g<1> --unrestricted"' 30 | backrefs: true 31 | notify: Update-grub 32 | 33 | - name: "1.4.2 | PATCH | Ensure access to bootloader config is configured " 34 | when: deb12cis_rule_1_4_2 35 | tags: 36 | - level1-server 37 | - level1-workstation 38 | - grub 39 | - patch 40 | - rule_1.4.2 41 | - NIST800-53R5_AC-3 42 | block: 43 | - name: "1.4.2 | PATCH | Ensure access to bootloader config is configured " 44 | ansible.builtin.file: 45 | path: /boot/grub/grub.cfg 46 | owner: root 47 | group: root 48 | mode: 'u-x,go-rwx' 49 | state: touch 50 | modification_time: preserve 51 | access_time: preserve 52 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.5.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.5.1 | PATCH | Ensure address space layout randomization (ASLR) is enabled" 4 | when: deb12cis_rule_1_5_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - sysctl 10 | - rule_1.5.1 11 | - NIST800-53R5_CM-6 12 | block: 13 | - name: "1.5.1 | PATCH | Ensure address space layout randomization (ASLR) is enabled" 14 | ansible.builtin.set_fact: 15 | deb12cis_sysctl_update: true 16 | 17 | - name: "1.5.1 | PATCH | Ensure address space layout randomization (ASLR) is enabled" 18 | ansible.builtin.debug: 19 | msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-kernel_sysctl.conf" 20 | 21 | - name: "1.5.2 | PATCH | Ensure ptrace_scope is restricted" 22 | when: deb12cis_rule_1_5_2 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - patch 27 | - sysctl 28 | - rule_1.5.2 29 | - NIST800-53R5_CM-6 30 | block: 31 | - name: "1.5.2 | PATCH | Ensure ptrace_scope is restricted" 32 | ansible.builtin.set_fact: 33 | deb12cis_sysctl_update: true 34 | 35 | - name: "1.5.2 | PATCH | Ensure ptrace_scope is restricted" 36 | ansible.builtin.debug: 37 | msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-kernel_sysctl.conf" 38 | 39 | - name: "1.5.3 | PATCH | Ensure core dumps are restricted" 40 | when: deb12cis_rule_1_5_3 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - patch 45 | - rule_1.5.3 46 | - coredump 47 | - NIST800-53R5_CM-6 48 | block: 49 | - name: "1.5.3 | PATCH | Ensure core dumps are restricted | kernel sysctl" 50 | ansible.posix.sysctl: 51 | name: fs.suid_dumpable 52 | value: '0' 53 | state: present 54 | sysctl_file: "{{ deb12cis_sysctl_kernel_conf }}" 55 | reload: true 56 | sysctl_set: true 57 | ignoreerrors: true 58 | 59 | - name: "1.5.3 | PATCH | Ensure core dumps are restricted | security limits" 60 | ansible.builtin.lineinfile: 61 | path: /etc/security/limits.d/99_zero_core.conf 62 | regexp: '^\* hard core' 63 | line: '* hard core 0' 64 | create: true 65 | owner: root 66 | group: root 67 | mode: 'u-x,go-wx' 68 | 69 | - name: "1.5.3 | PATCH | Ensure core dumps are restricted | sysctl.conf" 70 | ansible.builtin.lineinfile: 71 | path: /etc/sysctl.conf 72 | regexp: '^fs.suid_dumpable' 73 | line: fs.suid_dumpable=0 74 | owner: root 75 | group: root 76 | mode: 'u-x,go-wx' 77 | notify: Reload sysctl 78 | 79 | - name: "1.5.3 | PATCH | Ensure core dumps are restricted | coredump.conf" 80 | ansible.builtin.lineinfile: 81 | path: /etc/systemd/coredump.conf 82 | regexp: "{{ item.regexp }}" 83 | line: "{{ item.line }}" 84 | create: true 85 | owner: root 86 | group: root 87 | mode: 'u-x,go-wx' 88 | loop: 89 | - { regexp: '^Storage', line: 'Storage=none' } 90 | - { regexp: '^ProcessSizeMax', line: 'ProcessSizeMax=0' } 91 | 92 | - name: "1.5.4 | PATCH | Ensure prelink is not installed" 93 | when: deb12cis_rule_1_5_4 94 | tags: 95 | - level1-server 96 | - level1-workstation 97 | - patch 98 | - rule_1.5.4 99 | - prelink 100 | - NIST800-53R5_CM-1 101 | - NIST800-53R5_CM-3 102 | - NIST800-53R5_CM-6 103 | block: 104 | - name: "1.5.4 | PATCH | Ensure prelink is not installed | Restore binaries to normal" 105 | ansible.builtin.command: prelink -ua 106 | changed_when: false 107 | failed_when: false 108 | 109 | - name: "1.5.4 | PATCH | Ensure prelink is not installed| Remove prelink package" 110 | ansible.builtin.package: 111 | name: prelink 112 | state: absent 113 | purge: "{{ deb12cis_purge_apt }}" 114 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.6.1 | PATCH | Ensure message of the day is configured properly" 4 | when: deb12cis_rule_1_6_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_1.6.1 10 | - motd 11 | - NIST800-53R5_CM-1 12 | - NIST800-53R5_CM-3 13 | - NIST800-53R5_CM-6 14 | block: 15 | - name: "1.6.1 | PATCH | Ensure message of the day is configured properly | motd" 16 | ansible.builtin.template: 17 | src: etc/motd.j2 18 | dest: /etc/motd 19 | mode: 'u-x,go-wx' 20 | 21 | - name: "1.6.1 | PATCH | Ensure message of the day is configured properly | disable dynamic_motd" 22 | when: deb12cis_disable_dynamic_motd 23 | ansible.builtin.lineinfile: 24 | path: /etc/pam.d/sshd 25 | regexp: "{{ item.regexp }}" 26 | line: "{{ item.line }}" 27 | backrefs: true 28 | loop: 29 | - { regexp: '(session\s+optional\s+pam_motd.so\s+motd=/run/motd.dynamic)', line: '# \1' } 30 | - { regexp: '(session\s+optional\s+pam_motd.so noupdate)', line: '# \1' } 31 | - { regexp: '# Pam_motd.so disabled for CIS benchmark', line: '# Pam_motd.so disabled for CIS benchmark' } 32 | 33 | - name: "1.6.2 | PATCH | Ensure local login warning banner is configured properly" 34 | when: deb12cis_rule_1_6_2 35 | tags: 36 | - level1-server 37 | - level1-workstation 38 | - patch 39 | - rule_1.6.2 40 | - banner 41 | - NIST800-53R5_CM-1 42 | - NIST800-53R5_CM-3 43 | - NIST800-53R5_CM-6 44 | block: 45 | - name: "1.6.2 | PATCH | Ensure local login warning banner is configured properly | issue" 46 | ansible.builtin.template: 47 | src: etc/issue.j2 48 | dest: /etc/issue 49 | mode: 'u-x,go-wx' 50 | 51 | - name: "1.6.2 | PATCH | Ensure local login warning banner is kept on package upgrade | issue" 52 | community.general.dpkg_divert: 53 | path: /etc/issue 54 | 55 | - name: "1.6.3 | PATCH | Ensure remote login warning banner is configured properly" 56 | when: deb12cis_rule_1_6_3 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - patch 61 | - rule_1.6.3 62 | - banner 63 | - NIST800-53R5_CM-1 64 | - NIST800-53R5_CM-3 65 | - NIST800-53R5_CM-6 66 | block: 67 | - name: "1.6.3 | PATCH | Ensure remote login warning banner is configured properly | issue.net" 68 | ansible.builtin.template: 69 | src: etc/issue.net.j2 70 | dest: /etc/issue.net 71 | mode: 'u-x,go-wx' 72 | 73 | - name: "1.6.3 | PATCH | Ensure remote login warning banner is kept on package upgrade | issue.net" 74 | community.general.dpkg_divert: 75 | path: /etc/issue.net 76 | 77 | - name: "1.6.4 | PATCH | Ensure permissions on /etc/motd are configured" 78 | when: deb12cis_rule_1_6_4 79 | tags: 80 | - level1-server 81 | - level1-workstation 82 | - patch 83 | - rule_1.6.4 84 | - permissions 85 | - motd 86 | - NIST800-53R5_AC-3 87 | - NIST800-53R5_MP-2 88 | ansible.builtin.file: 89 | path: /etc/motd 90 | owner: root 91 | group: root 92 | mode: 'u-x,go-wx' 93 | 94 | - name: "1.6.5 | PATCH | Ensure permissions on /etc/issue are configured" 95 | when: 96 | - deb12cis_rule_1_6_5 97 | tags: 98 | - level1-server 99 | - level1-workstation 100 | - patch 101 | - rule_1.6.5 102 | - permissions 103 | - banner 104 | - NIST800-53R5_AC-3 105 | - NIST800-53R5_MP-2 106 | ansible.builtin.file: 107 | path: /etc/issue 108 | owner: root 109 | group: root 110 | mode: 'u-x,go-wx' 111 | 112 | - name: "1.6.6 | PATCH | Ensure permissions on /etc/issue.net are configured" 113 | when: deb12cis_rule_1_6_6 114 | tags: 115 | - level1-server 116 | - level1-workstation 117 | - patch 118 | - rule_1.6.6 119 | - permissions 120 | - banner 121 | - NIST800-53R5_AC-3 122 | - NIST800-53R5_MP-2 123 | ansible.builtin.file: 124 | path: /etc/issue.net 125 | owner: root 126 | group: root 127 | mode: 'u-x,go-wx' 128 | -------------------------------------------------------------------------------- /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.1.x | Configure Package Repositories" 36 | ansible.builtin.import_tasks: 37 | file: cis_1.2.1.x.yml 38 | 39 | - name: "SECTION | 1.2.2.x | Configure Package Updates" 40 | ansible.builtin.import_tasks: 41 | file: cis_1.2.2.x.yml 42 | 43 | - name: "SECTION | 1.3.1 | Configure Apparmor" 44 | ansible.builtin.import_tasks: 45 | file: cis_1.3.1.x.yml 46 | 47 | - name: "SECTION | 1.4 | Configure Bootloader" 48 | ansible.builtin.import_tasks: 49 | file: cis_1.4.x.yml 50 | 51 | - name: "SECTION | 1.5 | Additional Process Hardening" 52 | ansible.builtin.import_tasks: 53 | file: cis_1.5.x.yml 54 | 55 | - name: "SECTION | 1.6 | Command Line Warning Banners" 56 | ansible.builtin.import_tasks: 57 | file: cis_1.6.x.yml 58 | 59 | - name: "SECTION | 1.7 | Gnome Display Manager" 60 | ansible.builtin.import_tasks: 61 | file: cis_1.7.x.yml 62 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.2.1 | PATCH | Ensure NIS Client is not installed" 4 | when: 5 | - deb12cis_rule_2_2_1 6 | - not deb12cis_nis_server 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - rule_2.2.1 11 | - nis 12 | ansible.builtin.package: 13 | name: nis 14 | state: absent 15 | purge: "{{ deb12cis_purge_apt }}" 16 | 17 | - name: "2.2.2 | PATCH | Ensure rsh client is not installed" 18 | when: 19 | - deb12cis_rule_2_2_2 20 | - not deb12cis_rsh_client 21 | tags: 22 | - level1-server 23 | - level1-workstation 24 | - patch 25 | - rule_2.2.2 26 | - rsh 27 | ansible.builtin.package: 28 | name: rsh-client 29 | state: absent 30 | purge: "{{ deb12cis_purge_apt }}" 31 | 32 | - name: "2.2.3 | PATCH | Ensure talk client is not installed" 33 | when: 34 | - deb12cis_rule_2_2_3 35 | - not deb12cis_talk_client 36 | tags: 37 | - level1-server 38 | - level1-workstation 39 | - patch 40 | - rule_2.2.3 41 | - talk 42 | ansible.builtin.package: 43 | name: talk 44 | state: absent 45 | purge: "{{ deb12cis_purge_apt }}" 46 | 47 | - name: "2.2.4 | PATCH | Ensure telnet client is not installed" 48 | when: 49 | - deb12cis_rule_2_2_4 50 | - not deb12cis_telnet_client 51 | tags: 52 | - level1-server 53 | - level1-workstation 54 | - patch 55 | - rule_2.2.4 56 | - telnet 57 | ansible.builtin.package: 58 | name: 59 | - telnet 60 | - inetutils-telnet 61 | state: absent 62 | purge: "{{ deb12cis_purge_apt }}" 63 | 64 | - name: "2.2.5 | PATCH | Ensure ldap client is not installed" 65 | when: 66 | - deb12cis_rule_2_2_5 67 | - not deb12cis_ldap_clients_required 68 | tags: 69 | - level1-server 70 | - level1-workstation 71 | - patch 72 | - rule_2.2.5 73 | - ldap 74 | ansible.builtin.package: 75 | name: ldap-utils 76 | state: absent 77 | purge: "{{ deb12cis_purge_apt }}" 78 | 79 | - name: "2.2.6 | PATCH | Ensure ftp is not installed" 80 | when: 81 | - deb12cis_rule_2_2_6 82 | - not deb12cis_ftp_client 83 | tags: 84 | - level1-server 85 | - level1-workstation 86 | - patch 87 | - rule_2.2.6 88 | - ftp 89 | ansible.builtin.package: 90 | name: ftp 91 | state: absent 92 | purge: "{{ deb12cis_purge_apt }}" 93 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.3.1.1 | PATCH | Ensure a single time synchronization daemon is in use" 4 | when: deb12cis_rule_2_3_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_2.3.1.1 10 | - chrony 11 | - ntp 12 | - systemd-timesyncd 13 | - NIST800-53R5_AU-3 14 | - NIST800-53R5_AU-12 15 | block: 16 | - name: "2.3.1.1 | PATCH | Ensure a single time synchronization daemon is in use | Pkg installed" 17 | ansible.builtin.package: 18 | name: "{{ deb12cis_time_sync_tool }}" 19 | state: present 20 | 21 | - name: "2.3.1.1 | PATCH | Ensure a single time synchronization daemon is in use | other pkgs removed" 22 | when: item != deb12cis_time_sync_tool 23 | ansible.builtin.package: 24 | name: "{{ item }}" 25 | state: absent 26 | loop: 27 | - chrony 28 | - ntp 29 | 30 | - name: "2.3.1.1 | PATCH | Ensure a single time synchronization daemon is in use | mask service" 31 | when: 32 | - deb12cis_time_sync_tool != "systemd-timesyncd" 33 | - "'systemd-timesyncd' in ansible_facts.packages" 34 | ansible.builtin.service: 35 | name: systemd-timesyncd 36 | state: stopped 37 | enabled: false 38 | masked: true 39 | daemon_reload: true 40 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.3.2.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver" 4 | when: deb12cis_rule_2_3_2_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_2.3.2.1 10 | - timesyncd 11 | - NIST800-53R5_AU-7 12 | - NIST800-53R5_AU-8 13 | block: 14 | - name: "2.3.2.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver | create conf.d dir" 15 | ansible.builtin.file: 16 | path: /etc/systemd/timesyncd.conf.d 17 | owner: root 18 | group: root 19 | mode: 'u+x,go-w' 20 | state: directory 21 | 22 | - name: "2.3.2.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver | sources" 23 | ansible.builtin.template: 24 | src: "{{ item }}.j2" 25 | dest: "/{{ item }}" 26 | mode: 'u-x,go-wx' 27 | owner: root 28 | group: root 29 | loop: 30 | - "etc/systemd/timesyncd.conf.d/50-timesyncd.conf" 31 | notify: Restart timeservice 32 | 33 | - name: "2.3.2.2 | PATCH | Ensure systemd-timesyncd is enabled and running" 34 | when: deb12cis_rule_2_3_2_2 35 | tags: 36 | - level1-server 37 | - level1-workstation 38 | - rule_2.3.2.2 39 | - timesyncd 40 | - NIST800-53R5_AU-7 41 | - NIST800-53R5_AU-8 42 | block: 43 | - name: "2.3.2.2 | PATCH | Ensure systemd-timesyncd is enabled and running | enable if timesyncd" 44 | ansible.builtin.systemd: 45 | name: systemd-timesyncd 46 | state: started 47 | enabled: true 48 | 49 | - name: "2.3.2.2 | PATCH | Ensure systemd-timesyncd is enabled and running | disable other time sources | chrony" 50 | when: "'chrony' in ansible_facts.packages" 51 | ansible.builtin.systemd: 52 | name: chrony 53 | state: stopped 54 | enabled: false 55 | masked: true 56 | 57 | - name: "2.3.2.2 | PATCH | Ensure systemd-timesyncd is enabled and running | disable other time sources | ntp" 58 | when: "'ntp' in ansible_facts.packages" 59 | ansible.builtin.systemd: 60 | name: ntp 61 | state: stopped 62 | enabled: false 63 | masked: true 64 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.3.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.3.3.1 | PATCH | Ensure chrony is configured with authorized timeserver" 4 | when: deb12cis_rule_2_3_3_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_2.3.3.1 10 | - chrony 11 | - NIST800-53R5_AU-3 12 | - NIST800-53R5_AU-12 13 | block: 14 | - name: "2.3.3.1 | PATCH | Ensure chrony is configured with authorized timeserver | sources" 15 | ansible.builtin.template: 16 | src: "{{ item }}.j2" 17 | dest: "/{{ item }}" 18 | mode: 'u-x,go-wx' 19 | owner: root 20 | group: root 21 | loop: 22 | - etc/chrony/sources.d/pool.sources 23 | - etc/chrony/sources.d/server.sources 24 | notify: Restart timeservice 25 | 26 | - name: "2.3.3.1 | PATCH | Ensure chrony is configured with authorized timeserver | load sources" 27 | ansible.builtin.lineinfile: 28 | path: /etc/chrony/chrony.conf 29 | regexp: '^sourcedir /etc/chrony/sources.d' 30 | line: sourcedir /etc/chrony/sources.d 31 | notify: Restart timeservice 32 | 33 | - name: "2.3.3.2 | PATCH | Ensure chrony is running as user _chrony" 34 | when: deb12cis_rule_2_3_3_2 35 | tags: 36 | - level1-server 37 | - level1-workstation 38 | - patch 39 | - rule_2.3.3.2 40 | - chrony 41 | - NIST800-53R5_AU-8 42 | ansible.builtin.lineinfile: 43 | path: /etc/chrony/chrony.conf 44 | regexp: '^user _chrony' 45 | line: 'user _chrony' 46 | 47 | - name: "2.3.3.3 | PATCH | Ensure chrony is enabled and running" 48 | when: deb12cis_rule_2_3_3_3 49 | tags: 50 | - level1-server 51 | - level1-workstation 52 | - rule_2.3.3.3 53 | - chrony 54 | - NIST800-53R5_AU-8 55 | block: 56 | - name: "2.3.3.3 | PATCH | Ensure chrony is enabled and running" 57 | ansible.builtin.systemd: 58 | name: chrony 59 | state: started 60 | enabled: true 61 | 62 | - name: "2.3.3.3 | PATCH | Ensure chrony is enabled and running | disable other time sources | timesyncd" 63 | when: "'systemd-timesyncd' in ansible_facts.packages" 64 | ansible.builtin.systemd: 65 | name: systemd-timesyncd 66 | state: stopped 67 | enabled: false 68 | masked: true 69 | 70 | - name: "2.3.3.3 | PATCH | Ensure chrony is enabled and running | disable other time sources | ntpd" 71 | when: "'ntpd' in ansible_facts.packages" 72 | ansible.builtin.systemd: 73 | name: ntpd 74 | state: stopped 75 | enabled: false 76 | masked: true 77 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.4.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.4.1.1 | PATCH | Ensure cron daemon is enabled and active" 4 | when: deb12cis_rule_2_4_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_2.4.1.1 10 | - cron 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 | ansible.builtin.systemd: 17 | name: cron 18 | state: started 19 | enabled: true 20 | 21 | - name: "2.4.1.2 | PATCH | Ensure permissions on /etc/crontab are configured" 22 | when: deb12cis_rule_2_4_1_2 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - patch 27 | - rule_2.4.1.2 28 | - cron 29 | - NIST800-53R5_AC-3 30 | - NIST800-53R5_MP-2 31 | ansible.builtin.file: 32 | path: /etc/crontab 33 | owner: root 34 | group: root 35 | mode: 'u-x,go-rwx' 36 | 37 | - name: "2.4.1.3 | PATCH | Ensure permissions on /etc/cron.hourly are configured" 38 | when: deb12cis_rule_2_4_1_3 39 | tags: 40 | - level1-server 41 | - level1-workstation 42 | - patch 43 | - rule_2.4.1.3 44 | - cron 45 | - NIST800-53R5_AC-3 46 | - NIST800-53R5_MP-2 47 | ansible.builtin.file: 48 | path: /etc/cron.hourly 49 | owner: root 50 | group: root 51 | mode: 'u+x,go-rwx' 52 | 53 | - name: "2.4.1.4 | PATCH | Ensure permissions on /etc/cron.daily are configured" 54 | when: deb12cis_rule_2_4_1_4 55 | tags: 56 | - level1-server 57 | - level1-workstation 58 | - patch 59 | - rule_2.4.1.4 60 | - cron 61 | - NIST800-53R5_AC-3 62 | - NIST800-53R5_MP-2 63 | ansible.builtin.file: 64 | path: /etc/cron.daily 65 | owner: root 66 | group: root 67 | mode: 'u+x,go-rwx' 68 | 69 | - name: "2.4.1.5 | PATCH | Ensure permissions on /etc/cron.weekly are configured" 70 | when: deb12cis_rule_2_4_1_5 71 | tags: 72 | - level1-server 73 | - level1-workstation 74 | - patch 75 | - rule_2.4.1.5 76 | - cron 77 | - NIST800-53R5_AC-3 78 | - NIST800-53R5_MP-2 79 | ansible.builtin.file: 80 | path: /etc/cron.weekly 81 | owner: root 82 | group: root 83 | mode: 'u+x,go-rwx' 84 | 85 | - name: "2.4.1.6 | PATCH | Ensure permissions on /etc/cron.monthly are configured" 86 | when: deb12cis_rule_2_4_1_6 87 | tags: 88 | - level1-server 89 | - level1-workstation 90 | - patch 91 | - rule_2.4.1.6 92 | - cron 93 | - NIST800-53R5_AC-3 94 | - NIST800-53R5_MP-2 95 | ansible.builtin.file: 96 | path: /etc/cron.monthly 97 | owner: root 98 | group: root 99 | mode: 'u+x,go-rwx' 100 | 101 | - name: "2.4.1.7 | PATCH | Ensure permissions on /etc/cron.d are configured" 102 | when: deb12cis_rule_2_4_1_7 103 | tags: 104 | - level1-server 105 | - level1-workstation 106 | - patch 107 | - rule_2.4.1.7 108 | - cron 109 | ansible.builtin.file: 110 | path: /etc/cron.d 111 | owner: root 112 | group: root 113 | mode: 'u+x,go-rwx' 114 | 115 | - name: "2.4.1.8 | PATCH | Ensure cron is restricted to authorized users" 116 | when: 117 | - deb12cis_rule_2_4_1_8 118 | tags: 119 | - level1-server 120 | - level1-workstation 121 | - patch 122 | - rule_2.4.1.8 123 | - cron 124 | block: 125 | - name: "2.4.1.8 | PATCH | Ensure cron is restricted to authorized users | Remove cron.deny" 126 | ansible.builtin.file: 127 | path: /etc/cron.deny 128 | state: absent 129 | 130 | - name: "2.4.1.8 | PATCH | Ensure cron is restricted to authorized users | Check for cron.allow" 131 | ansible.builtin.stat: 132 | path: /etc/cron.allow 133 | register: discovered_cron_allow_status 134 | 135 | - name: "2.4.1.8 | PATCH | Ensure cron is restricted to authorized users | Create cron.allow if doesn't exist" 136 | when: not discovered_cron_allow_status.stat.exists 137 | ansible.builtin.file: 138 | path: /etc/cron.allow 139 | owner: root 140 | group: root 141 | mode: 'u-x,g-wx,o-rwx' 142 | state: touch 143 | 144 | - name: "2.4.1.8 | PATCH | Ensure cron is restricted to authorized users | Update cron.allow if exists" 145 | when: discovered_cron_allow_status.stat.exists 146 | ansible.builtin.file: 147 | path: /etc/cron.allow 148 | owner: root 149 | group: root 150 | mode: 'u-x,g-wx,o-rwx' 151 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.4.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.4.2.1 | PATCH | Ensure at is restricted to authorized users" 4 | when: deb12cis_rule_2_4_2_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_2.4.2.1 10 | - cron 11 | block: 12 | - name: "2.4.2.1 | PATCH | Ensure at is restricted to authorized users | Remove at.deny" 13 | ansible.builtin.file: 14 | path: /etc/at.deny 15 | state: absent 16 | 17 | - name: "2.4.2.1 | PATCH | Ensure at is restricted to authorized users | Check for at.allow" 18 | ansible.builtin.stat: 19 | path: /etc/at.allow 20 | register: discovered_at_allow_status 21 | 22 | - name: "2.4.2.1 | PATCH | Ensure at is restricted to authorized users | Create at.allow if doesn't exist" 23 | when: not discovered_at_allow_status.stat.exists 24 | ansible.builtin.file: 25 | path: /etc/at.allow 26 | owner: root 27 | group: root 28 | mode: 'u-x,g-wx,o-rwx' 29 | state: touch 30 | 31 | - name: "2.4.2.1 | PATCH | Ensure at is restricted to authorized users | update at.allow if exists" 32 | when: discovered_at_allow_status.stat.exists 33 | ansible.builtin.file: 34 | path: /etc/at.allow 35 | owner: root 36 | group: root 37 | mode: 'u-x,g-wx,o-rwx' 38 | -------------------------------------------------------------------------------- /tasks/section_2/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 2.1.x | Configure Server Services" 4 | ansible.builtin.import_tasks: 5 | file: cis_2.1.x.yml 6 | 7 | - name: "SECTION | 2.2.x | Configure Clients Services" 8 | ansible.builtin.import_tasks: 9 | file: cis_2.2.x.yml 10 | 11 | - name: "SECTION | 2.3.1.x | Time service " 12 | ansible.builtin.import_tasks: 13 | file: cis_2.3.1.x.yml 14 | 15 | - name: "SECTION | 2.3.2.x | Configure systemd-timesyncd" 16 | when: deb12cis_time_sync_tool == "systemd-timesyncd" 17 | ansible.builtin.import_tasks: 18 | file: cis_2.3.2.x.yml 19 | 20 | - name: "SECTION | 2.3.3.x | Configure Chrony" 21 | when: deb12cis_time_sync_tool == "chrony" 22 | ansible.builtin.import_tasks: 23 | file: cis_2.3.3.x.yml 24 | 25 | - name: "SECTION | 2.4.1.x | Configure Cron" 26 | ansible.builtin.import_tasks: 27 | file: cis_2.4.1.x.yml 28 | 29 | - name: "SECTION | 2.4.2.x | Configure At" 30 | ansible.builtin.import_tasks: 31 | file: cis_2.4.2.x.yml 32 | -------------------------------------------------------------------------------- /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 deb12cis_ipv6_required to specify if you have IPv6 in use 5 | - name: "3.1.1 | PATCH | Ensure IPv6 status is identified" 6 | when: 7 | - not deb12cis_ipv6_required 8 | - deb12cis_rule_3_1_1 9 | tags: 10 | - level1-server 11 | - level1-workstation 12 | - manual 13 | - patch 14 | - ipv6 15 | - networking 16 | - rule_3.1.1 17 | - NIST800-53R5_CM-7 18 | block: 19 | - name: "3.1.1 | PATCH | Ensure IPv6 status is identified | refresh" 20 | ansible.builtin.set_fact: 21 | deb12cis_sysctl_update: true 22 | deb12cis_flush_ipv6_route: true 23 | 24 | - name: "3.1.1 | PATCH | Ensure IPv6 status is identified | disable" 25 | ansible.builtin.debug: 26 | msg: "Control being set via Handler 'update sysctl' which writes to /etc/sysctl.d/60-disable_ipv6.conf" 27 | 28 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled" 29 | when: 30 | - deb12cis_rule_3_1_2 31 | - prelim_wireless_modules is defined 32 | tags: 33 | - level1-server 34 | - patch 35 | - rule_3.1.2 36 | - wireless 37 | - NIST800-53R5_CM-7 38 | vars: 39 | warn_control_id: '3.1.2' 40 | block: 41 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Create modprobe.d file" 42 | ansible.builtin.lineinfile: 43 | path: /etc/modprobe.d/{{ item }}.conf 44 | regexp: '^(#)?install true(\\s|$)' 45 | line: install {{ item }} true 46 | mode: 'u-x,go-wx' 47 | create: true 48 | loop: "{{ prelim_wireless_modules.stdout_lines }}" 49 | 50 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | blacklist" 51 | ansible.builtin.lineinfile: 52 | path: /etc/modprobe.d/blacklist.conf 53 | regexp: "^(#)?blacklist {{ item }}(\\s|$)" 54 | line: "blacklist {{ item }}" 55 | create: true 56 | mode: 'u-x,go-rwx' 57 | loop: "{{ prelim_wireless_modules.stdout_lines }}" 58 | 59 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use" 60 | when: deb12cis_rule_3_1_3 61 | tags: 62 | - level1-server 63 | - level2-workstation 64 | - patch 65 | - bluetooth 66 | - rule_3.1.3 67 | - NIST800-53R5_CM-7 68 | block: 69 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use | pkg" 70 | when: 71 | - not deb12cis_bluetooth_service 72 | - not deb12cis_bluetooth_mask 73 | ansible.builtin.package: 74 | name: bluez 75 | state: absent 76 | 77 | - name: "3.1.3 | PATCH | Ensure bluetooth services are not in use | mask" 78 | when: 79 | - not deb12cis_bluetooth_service 80 | - deb12cis_bluetooth_mask 81 | notify: Systemd_daemon_reload 82 | ansible.builtin.systemd: 83 | name: bluetooth.service 84 | enabled: false 85 | state: stopped 86 | masked: true 87 | -------------------------------------------------------------------------------- /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: deb12cis_rule_3_2_1 5 | tags: 6 | - level2-server 7 | - level2-workstation 8 | - patch 9 | - rule_3.2.1 10 | - dccp 11 | - NIST800-53R5_CM-7 12 | - NIST800-53R5_SI-4 13 | block: 14 | - name: "3.2.1 | PATCH | Ensure dccp kernel module is not available | modprobe" 15 | ansible.builtin.lineinfile: 16 | path: /etc/modprobe.d/dccp.conf 17 | regexp: '^(#)?install dccp(\\s|$)' 18 | line: "{{ item }}" 19 | create: true 20 | mode: 'u-x,go-wx' 21 | loop: 22 | - install dccp /bin/true 23 | - blacklist dccp 24 | 25 | - name: "3.2.1 | PATCH | Ensure dccp kernel module is not available | blacklist" 26 | ansible.builtin.lineinfile: 27 | path: /etc/modprobe.d/blacklist.conf 28 | regexp: "^(#)?blacklist dccp(\\s|$)" 29 | line: "blacklist dccp" 30 | create: true 31 | mode: 'u-x,go-rwx' 32 | 33 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available" 34 | when: deb12cis_rule_3_2_2 35 | tags: 36 | - level2-server 37 | - level2-workstation 38 | - patch 39 | - rule_3.2.2 40 | - tipc 41 | - NIST800-53R5_CM-7 42 | - NIST800-53R5_SI-4 43 | block: 44 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available | modprobe" 45 | ansible.builtin.lineinfile: 46 | path: /etc/modprobe.d/tipc.conf 47 | regexp: '^(#)?install tipc(\\s|$)' 48 | line: "{{ item }}" 49 | create: true 50 | mode: 'u-x,go-wx' 51 | loop: 52 | - install tipc /bin/true 53 | - blacklist tipc 54 | 55 | - name: "3.2.2 | PATCH | Ensure tipc kernel module is not available | blacklist" 56 | ansible.builtin.lineinfile: 57 | path: /etc/modprobe.d/blacklist.conf 58 | regexp: "^(#)?blacklist tipc(\\s|$)" 59 | line: "blacklist tipc" 60 | create: true 61 | mode: 'u-x,go-rwx' 62 | 63 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available" 64 | when: deb12cis_rule_3_2_3 65 | tags: 66 | - level2-server 67 | - level2-workstation 68 | - patch 69 | - rule_3.2.3 70 | - rds 71 | - NIST800-53R5_CM-7 72 | - NIST800-53R5_SI-4 73 | block: 74 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available | modprobe" 75 | ansible.builtin.lineinfile: 76 | path: /etc/modprobe.d/rds.conf 77 | regexp: '^(#)?install rds(\\s|$)' 78 | line: "{{ item }}" 79 | create: true 80 | mode: 'u-x,go-wx' 81 | loop: 82 | - install rds /bin/true 83 | - blacklist rds 84 | 85 | - name: "3.2.3 | PATCH | Ensure rds kernel module is not available | blacklist" 86 | ansible.builtin.lineinfile: 87 | path: /etc/modprobe.d/blacklist.conf 88 | regexp: "^(#)?blacklist rds(\\s|$)" 89 | line: "blacklist rds" 90 | create: true 91 | mode: 'u-x,go-rwx' 92 | 93 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available" 94 | when: deb12cis_rule_3_2_4 95 | tags: 96 | - level2-server 97 | - level2-workstation 98 | - patch 99 | - rule_3.2.4 100 | - sctp 101 | - NIST800-53R5_CM-7 102 | - NIST800-53R5_SI-4 103 | block: 104 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available | modprobe" 105 | ansible.builtin.lineinfile: 106 | path: /etc/modprobe.d/sctp.conf 107 | regexp: '^(#)?install sctp(\\s|$)' 108 | line: "{{ item }}" 109 | create: true 110 | mode: 'u-x,go-wx' 111 | loop: 112 | - install sctp /bin/true 113 | - blacklist sctp 114 | 115 | - name: "3.2.4 | PATCH | Ensure sctp kernel module is not available | blacklist" 116 | ansible.builtin.lineinfile: 117 | path: /etc/modprobe.d/blacklist.conf 118 | regexp: "^(#)?blacklist sctp(\\s|$)" 119 | line: "blacklist sctp" 120 | create: true 121 | mode: 'u-x,go-rwx' 122 | -------------------------------------------------------------------------------- /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 | Configure Network Kernel Modules" 8 | ansible.builtin.import_tasks: 9 | file: cis_3.2.x.yml 10 | 11 | - name: "SECTION | 3.3.x | Configure Network Kernel Parameters" 12 | ansible.builtin.import_tasks: 13 | file: cis_3.3.x.yml 14 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.1 | PATCH | Ensure ufw is installed" 4 | when: 5 | - deb12cis_rule_4_1_1 6 | - "'ufw' not in ansible_facts.packages" 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_4.1.1 12 | - apt 13 | - ufw 14 | - NIST800-53R5_SC-7 15 | ansible.builtin.package: 16 | name: ufw 17 | state: present 18 | 19 | - name: "4.1.2 | PATCH | Ensure iptables-persistent is not installed with ufw" 20 | when: 21 | - deb12cis_rule_4_1_2 22 | - "'iptables-persistent' in ansible_facts.packages" 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - patch 27 | - rule_4.1.2 28 | - ufw 29 | - NIST800-53R5_SC-7 30 | ansible.builtin.package: 31 | name: iptables-persistent 32 | state: absent 33 | 34 | # Adding the allow OpenSSH rule while enabling ufw to allow ansible to run after enabling 35 | - name: "4.1.3 | PATCH | Ensure ufw service is enabled" 36 | when: deb12cis_rule_4_1_3 37 | tags: 38 | - level1-server 39 | - level1-workstation 40 | - patch 41 | - rule_4.1.3 42 | - ufw 43 | - NIST800-53R5_SC-7 44 | block: 45 | - name: "4.1.3 | PATCH | Ensure ufw service is enabled | ssh port enabled" 46 | community.general.ufw: 47 | rule: allow 48 | name: OpenSSH 49 | state: enabled 50 | 51 | - name: "4.1.3 | PATCH | Ensure ufw service is enabled | service" 52 | ansible.builtin.systemd: 53 | name: ufw 54 | enabled: true 55 | state: started 56 | 57 | - name: "4.1.4 | PATCH | Ensure loopback traffic is configured" 58 | when: deb12cis_rule_4_1_4 59 | tags: 60 | - level1-server 61 | - level1-workstation 62 | - patch 63 | - rule_4.1.4 64 | - ufw 65 | - NIST800-53R5_SC-7 66 | block: 67 | - name: "4.1.4 | PATCH | Ensure loopback traffic is configured | Set allow in ufw rules" 68 | community.general.ufw: 69 | rule: allow 70 | direction: in 71 | interface: lo 72 | notify: Reload ufw 73 | 74 | - name: "4.1.4 | PATCH | Ensure loopback traffic is configured | Set allow out ufw rules" 75 | community.general.ufw: 76 | rule: allow 77 | direction: out 78 | interface: lo 79 | notify: Reload ufw 80 | 81 | - name: "4.1.4 | PATCH | Ensure loopback traffic is configured | Set deny ufw rules IPv4" 82 | community.general.ufw: 83 | rule: deny 84 | direction: in 85 | from_ip: 127.0.0.0/8 86 | notify: Reload ufw 87 | 88 | - name: "4.1.4 | PATCH | Ensure loopback traffic is configured | Set deny ufw rules IPv6" 89 | when: deb12cis_ipv6_required 90 | community.general.ufw: 91 | rule: deny 92 | direction: in 93 | from_ip: '::1' 94 | notify: Reload ufw 95 | 96 | - name: "4.1.5 | PATCH | Ensure ufw outbound connections are configured" 97 | when: deb12cis_rule_4_1_5 98 | tags: 99 | - level1-server 100 | - level1-workstation 101 | - patch 102 | - rule_4.1.5 103 | - ufw 104 | - NIST800-53R5_SC-7 105 | block: 106 | - name: "4.1.5 | PATCH | Ensure ufw outbound connections are configured | Custom ports" 107 | when: deb12cis_ufw_allow_out_ports != "all" 108 | community.general.ufw: 109 | rule: allow 110 | direction: out 111 | to_port: '{{ item }}' 112 | with_items: 113 | - "{{ deb12cis_ufw_allow_out_ports }}" 114 | notify: Reload ufw 115 | 116 | - name: "4.1.5 | PATCH | Ensure ufw outbound connections are configured | Allow all" 117 | when: "'all' in deb12cis_ufw_allow_out_ports" 118 | community.general.ufw: 119 | rule: allow 120 | direction: out 121 | notify: Reload ufw 122 | 123 | - name: "4.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports" 124 | when: deb12cis_rule_4_1_6 125 | tags: 126 | - level1-server 127 | - level1-workstation 128 | - audit 129 | - rule_4.1.6 130 | - ufw 131 | - NIST800-53R5_SC-7 132 | vars: 133 | warn_control_id: '4.1.6' 134 | block: 135 | - name: "4.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Get list of open ports" 136 | ansible.builtin.command: ss -4tuln 137 | changed_when: false 138 | failed_when: false 139 | check_mode: false 140 | register: discovered_ufw_open_listen_portss 141 | 142 | - name: "4.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Get list of firewall rules" 143 | ansible.builtin.command: ufw status 144 | changed_when: false 145 | failed_when: false 146 | check_mode: false 147 | register: discovered_ufw_current_firewall_rules 148 | 149 | - name: "4.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Message out settings" 150 | ansible.builtin.debug: 151 | msg: 152 | - "Warning!! Below are the listening ports and firewall rules" 153 | - "Please create firewall rule for any open ports if not already done" 154 | - "*****---Open Listen Ports---*****" 155 | - "{{ discovered_ufw_open_listen_portss.stdout_lines }}" 156 | - "*****---Firewall Rules---*****" 157 | - "{{ discovered_ufw_current_firewall_rules.stdout_lines }}" 158 | 159 | - name: "4.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Set warning count" 160 | ansible.builtin.import_tasks: 161 | file: warning_facts.yml 162 | 163 | - name: "4.1.7 | PATCH | Ensure ufw default deny firewall policy" 164 | when: deb12cis_rule_4_1_7 165 | tags: 166 | - level1-server 167 | - level1-workstation 168 | - patch 169 | - rule_4.1.7 170 | - ufw 171 | - NIST800-53R5_SC-7 172 | community.general.ufw: 173 | default: deny 174 | direction: "{{ item }}" 175 | loop: 176 | - incoming 177 | - outgoing 178 | - routed 179 | notify: Reload ufw 180 | 181 | # Added after ufw is installed and the file and base config is a prereq 182 | - name: "Optional | PATCH | UFW firewall force to use /etc/sysctl.conf settings" 183 | when: 184 | - deb12cis_rule_4_1_1 185 | - deb12cis_firewall_package == "ufw" 186 | - deb12cis_ufw_use_sysctl 187 | tags: 188 | - level1-server 189 | - level1-workstation 190 | - patch 191 | - rule_4.1.1 192 | - apt 193 | - ufw 194 | - NIST800-53R5_SC-7 195 | ansible.builtin.lineinfile: 196 | path: /etc/default/ufw 197 | regexp: ^IPT_SYSCTL=.* 198 | line: IPT_SYSCTL=/etc/sysctl.conf 199 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.3.1.1 | PATCH | Ensure iptables packages are installed" 4 | when: 5 | - deb12cis_rule_4_3_1_1 6 | - deb12cis_firewall_package == "iptables" 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_4.3.1.1 12 | - iptables 13 | - NIST800-53R5_CA-9 14 | - NIST800-53R5_SC-7 15 | ansible.builtin.package: 16 | name: ['iptables', 'iptables-persistent'] 17 | state: present 18 | 19 | - name: "4.3.1.2 | PATCH | Ensure nftables is not installed with iptables" 20 | when: 21 | - deb12cis_rule_4_3_1_2 22 | - deb12cis_firewall_package == "iptables" 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - patch 27 | - rule_4.3.1.2 28 | - iptables 29 | - NIST800-53R5_CA-9 30 | - NIST800-53R5_CM-7 31 | ansible.builtin.package: 32 | name: nftables 33 | state: absent 34 | purge: "{{ deb12cis_purge_apt }}" 35 | 36 | - name: "4.3.1.3 | PATCH | Ensure ufw is uninstalled or disabled with iptables" 37 | when: 38 | - deb12cis_rule_4_3_1_3 39 | - deb12cis_firewall_package == "iptables" 40 | tags: 41 | - level1-server 42 | - level1-workstation 43 | - patch 44 | - rule_4.3.1.3 45 | - iptables 46 | - NIST800-53R5_CA-9 47 | - NIST800-53R5_CM-7 48 | ansible.builtin.package: 49 | name: ufw 50 | state: absent 51 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.3.2.1 | PATCH | Ensure iptables default deny firewall policy" 4 | when: 5 | - deb12cis_rule_4_3_2_1 6 | - deb12cis_ipv4_required 7 | - not system_is_ec2 8 | tags: 9 | - level1-server 10 | - level1-workstation 11 | - patch 12 | - rule_4.3.2.1 13 | - iptables 14 | - NIST800-53R5_CA-9 15 | - NIST800-53R5_SC-7 16 | block: 17 | - name: "4.3.2.1 | PATCH | Ensure iptables default deny firewall policy | Configure SSH to be allowed in" 18 | ansible.builtin.iptables: 19 | chain: INPUT 20 | protocol: tcp 21 | destination_port: 22 22 | jump: ACCEPT 23 | ctstate: 'NEW,ESTABLISHED' 24 | notify: Iptables persistent 25 | 26 | - name: "4.3.2.1 | PATCH | Ensure iptables default deny firewall policy | Configure SSH to be allowed out" 27 | ansible.builtin.iptables: 28 | chain: OUTPUT 29 | protocol: tcp 30 | source_port: 22 31 | jump: ACCEPT 32 | ctstate: 'NEW,ESTABLISHED' 33 | notify: Iptables persistent 34 | 35 | - name: "4.3.2.1 | PATCH | Ensure iptables default deny firewall policy | Enable apt traffic" 36 | ansible.builtin.iptables: 37 | chain: INPUT 38 | ctstate: 'ESTABLISHED' 39 | jump: ACCEPT 40 | notify: Iptables persistent 41 | 42 | - name: "4.3.2.1 | PATCH | Ensure iptables default deny firewall policy | Set drop items" 43 | ansible.builtin.iptables: 44 | policy: DROP 45 | chain: "{{ item }}" 46 | notify: Iptables persistent 47 | loop: 48 | - INPUT 49 | - FORWARD 50 | - OUTPUT 51 | 52 | - name: "4.3.2.2 | PATCH | Ensure iptables loopback traffic is configured" 53 | when: 54 | - deb12cis_rule_4_3_2_2 55 | - deb12cis_firewall_package == "iptables" 56 | - deb12cis_ipv4_required 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - patch 61 | - rule_4.3.2.2 62 | - iptables 63 | - NIST800-53R5_CA-9 64 | - NIST800-53R5_SC-7 65 | block: 66 | - name: "4.3.2.2 | PATCH | Ensure iptables loopback traffic is configured | INPUT loopback ACCEPT" 67 | ansible.builtin.iptables: 68 | action: append 69 | chain: INPUT 70 | in_interface: lo 71 | jump: ACCEPT 72 | notify: Iptables persistent 73 | 74 | - name: "4.3.2.2 | PATCH | Ensure iptables loopback traffic is configured | OUTPUT loopback ACCEPT" 75 | ansible.builtin.iptables: 76 | action: append 77 | chain: OUTPUT 78 | out_interface: lo 79 | jump: ACCEPT 80 | notify: Iptables persistent 81 | 82 | - name: "4.3.2.2 | PATCH | Ensure iptables loopback traffic is configured | OUTPUT loopback ACCEPT" 83 | ansible.builtin.iptables: 84 | action: append 85 | chain: INPUT 86 | source: 127.0.0.0/8 87 | jump: DROP 88 | notify: Iptables persistent 89 | 90 | - name: "4.3.2.3 | PATCH | Ensure iptables outbound and established connections are configured" 91 | when: 92 | - deb12cis_rule_4_3_2_3 93 | - deb12cis_firewall_package == "iptables" 94 | - deb12cis_ipv4_required 95 | tags: 96 | - level1-server 97 | - level1-workstation 98 | - patch 99 | - rule_4.3.2.3 100 | - iptables 101 | - NIST800-53R5_CA-9 102 | - NIST800-53R5_SC-7 103 | ansible.builtin.iptables: 104 | action: append 105 | chain: '{{ item.chain }}' 106 | protocol: '{{ item.protocol }}' 107 | match: state 108 | ctstate: '{{ item.ctstate }}' 109 | jump: ACCEPT 110 | notify: Iptables persistent 111 | with_items: 112 | - { chain: OUTPUT, protocol: tcp, ctstate: 'NEW,ESTABLISHED' } 113 | - { chain: OUTPUT, protocol: udp, ctstate: 'NEW,ESTABLISHED' } 114 | - { chain: OUTPUT, protocol: icmp, ctstate: 'NEW,ESTABLISHED' } 115 | - { chain: INPUT, protocol: tcp, ctstate: 'ESTABLISHED' } 116 | - { chain: INPUT, protocol: udp, ctstate: 'ESTABLISHED' } 117 | - { chain: INPUT, protocol: icmp, ctstate: 'ESTABLISHED' } 118 | 119 | - name: "4.3.2.4 | AUDIT | Ensure iptables firewall rules exist for all open ports" 120 | when: 121 | - deb12cis_rule_4_3_2_4 122 | - deb12cis_firewall_package == "iptables" 123 | - deb12cis_ipv4_required 124 | tags: 125 | - level1-server 126 | - level1-workstation 127 | - audit 128 | - rule_4.3.2.4 129 | - iptables 130 | - NIST800-53R5_CA-9 131 | - NIST800-53R5_SC-7 132 | vars: 133 | warn_control_id: '4.3.2.4' 134 | block: 135 | - name: "4.3.2.4 | AUDIT | Ensure iptables firewall rules exist for all open ports | Get list of open ports" 136 | ansible.builtin.command: ss -4tuln 137 | changed_when: false 138 | failed_when: false 139 | check_mode: false 140 | register: discovered_ipv4_open_ports 141 | 142 | - name: "4.3.2.4 | AUDIT | Ensure iptables firewall rules exist for all open ports | Get list of rules" 143 | ansible.builtin.command: iptables -L INPUT -v -n 144 | changed_when: false 145 | failed_when: false 146 | check_mode: false 147 | register: discovered_ipv4_iptables_current_rules 148 | 149 | - name: "4.3.2.4 | AUDIT | Ensure iptables firewall rules exist for all open ports | Warn about settings" 150 | ansible.builtin.debug: 151 | msg: 152 | - "Warning!! Below is the list the open ports and current rules" 153 | - "Please create a rule for any open port that does not have a current rule" 154 | - "Open Ports:" 155 | - "{{ discovered_ipv4_open_ports.stdout_lines }}" 156 | - "Current Rules:" 157 | - "{{ discovered_ipv4_iptables_current_rules.stdout_lines }}" 158 | 159 | - name: "4.3.2.4 | AUDIT | Ensure iptables firewall rules exist for all open ports | Set warning count" 160 | ansible.builtin.import_tasks: 161 | file: warning_facts.yml 162 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.3.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.3.3.1 | PATCH | Ensure ip6tables default deny firewall policy" 4 | when: deb12cis_rule_4_3_3_1 5 | tags: 6 | - level1-server 7 | - level1-workstationå 8 | - patch 9 | - rule_4.3.3.1 10 | - ip6tables 11 | - NIST800-53R5_CA-9 12 | - NIST800-53R5_SC-7 13 | block: 14 | - name: "4.3.3.1 | PATCH | Ensure ip6tables default deny firewall policy | Configure SSH to be allowed out" 15 | ansible.builtin.iptables: 16 | chain: OUTPUT 17 | protocol: tcp 18 | source_port: 22 19 | jump: ACCEPT 20 | ctstate: 'NEW,ESTABLISHED' 21 | ip_version: ipv6 22 | notify: Ip6tables persistent 23 | 24 | - name: "4.3.3.1 | PATCH | Ensure ip6tables default deny firewall policy | Enable apt traffic" 25 | ansible.builtin.iptables: 26 | chain: INPUT 27 | ctstate: 'ESTABLISHED' 28 | jump: ACCEPT 29 | ip_version: ipv6 30 | notify: Ip6tables persistent 31 | 32 | - name: "4.3.3.1 | PATCH | Ensure ip6tables default deny firewall policy | Set drop items" 33 | ansible.builtin.iptables: 34 | policy: DROP 35 | chain: "{{ item }}" 36 | ip_version: ipv6 37 | notify: Ip6tables persistent 38 | loop: 39 | - INPUT 40 | - FORWARD 41 | - OUTPUT 42 | 43 | - name: "4.3.3.2 | PATCH | Ensure ip6tables loopback traffic is configured" 44 | when: 45 | - deb12cis_rule_4_3_3_2 46 | - not deb12cis_ipv4_required 47 | tags: 48 | - level1-server 49 | - level1-workstation 50 | - patch 51 | - rule_4.3.3.2 52 | - ip6tables 53 | - NIST800-53R5_CA-9 54 | - NIST800-53R5_SC-7 55 | block: 56 | - name: "4.3.3.2 | PATCH | Ensure ip6tables loopback traffic is configured | INPUT loopback ACCEPT" 57 | ansible.builtin.iptables: 58 | action: append 59 | chain: INPUT 60 | in_interface: lo 61 | jump: ACCEPT 62 | ip_version: ipv6 63 | notify: Ip6tables persistent 64 | 65 | - name: "4.3.3.2 | PATCH | Ensure ip6tables loopback traffic is configured | OUTPUT loopback ACCEPT" 66 | ansible.builtin.iptables: 67 | action: append 68 | chain: OUTPUT 69 | out_interface: lo 70 | jump: ACCEPT 71 | ip_version: ipv6 72 | notify: Ip6tables persistent 73 | 74 | - name: "4.3.3.2 | PATCH | Ensure ip6tables loopback traffic is configured | INPUT loopback drop" 75 | ansible.builtin.iptables: 76 | action: append 77 | chain: INPUT 78 | source: ::1 79 | jump: DROP 80 | ip_version: ipv6 81 | notify: Ip6tables persistent 82 | 83 | - name: "4.3.3.3 | PATCH | Ensure ip6tables outbound and established connections are configured" 84 | when: 85 | - deb12cis_rule_4_3_3_3 86 | - not deb12cis_ipv4_required 87 | tags: 88 | - level1-server 89 | - level1-workstation 90 | - patch 91 | - rule_4.3.3.3 92 | - ip6tables 93 | - NIST800-53R5_CA-9 94 | - NIST800-53R5_SC-7 95 | ansible.builtin.iptables: 96 | action: append 97 | chain: '{{ item.chain }}' 98 | protocol: '{{ item.protocol }}' 99 | match: state 100 | ctstate: '{{ item.ctstate }}' 101 | jump: ACCEPT 102 | ip_version: ipv6 103 | notify: Ip6tables persistent 104 | loop: 105 | - { chain: OUTPUT, protocol: tcp, ctstate: 'NEW,ESTABLISHED' } 106 | - { chain: OUTPUT, protocol: udp, ctstate: 'NEW,ESTABLISHED' } 107 | - { chain: OUTPUT, protocol: icmp, ctstate: 'NEW,ESTABLISHED' } 108 | - { chain: INPUT, protocol: tcp, ctstate: 'ESTABLISHED' } 109 | - { chain: INPUT, protocol: udp, ctstate: 'ESTABLISHED' } 110 | - { chain: INPUT, protocol: icmp, ctstate: 'ESTABLISHED' } 111 | 112 | - name: "4.3.3.4 | AUDIT | Ensure ip6tables firewall rules exist for all open ports" 113 | when: 114 | - deb12cis_rule_4_3_3_4 115 | - not deb12cis_ipv4_required 116 | tags: 117 | - level1-server 118 | - level1-workstation 119 | - audit 120 | - rule_4.3.3.4 121 | - ip6tables 122 | - NIST800-53R5_CA-9 123 | - NIST800-53R5_SC-7 124 | vars: 125 | warn_control_id: '4.3.3.4' 126 | block: 127 | - name: "4.3.3.4 | AUDIT | Ensure ip6tables firewall rules exist for all open ports | Get list of open ports" 128 | ansible.builtin.command: ss -6tuln 129 | changed_when: false 130 | failed_when: false 131 | check_mode: false 132 | register: discovered_ipv6_open_ports 133 | 134 | - name: "4.3.3.4 | AUDIT | Ensure ip6tables firewall rules exist for all open ports | Get list of rules" 135 | ansible.builtin.command: ip6tables -L INPUT -v -n 136 | changed_when: false 137 | failed_when: false 138 | check_mode: false 139 | register: discovered_ipv6_iptables_current_rules 140 | 141 | - name: "4.3.3.4 | AUDIT | Ensure ip6tables firewall rules exist for all open ports | Warn about settings" 142 | ansible.builtin.debug: 143 | msg: 144 | - "Warning!! Below is the list the open ports and current rules" 145 | - "Please create a rule for any open port that does not have a current rule" 146 | - "Open Ports:" 147 | - "{{ discovered_ipv6_open_ports.stdout_lines }}" 148 | - "Current Rules:" 149 | - "{{ discovered_ipv6_iptables_current_rules.stdout_lines }}" 150 | 151 | - name: "4.3.3.4 | AUDIT | Ensure ip6tables firewall rules exist for all open ports | Set warning count" 152 | ansible.builtin.import_tasks: 153 | file: warning_facts.yml 154 | -------------------------------------------------------------------------------- /tasks/section_4/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 4.1 | Configure UnComplicatedFirewall" 4 | when: deb12cis_firewall_package == "ufw" 5 | ansible.builtin.import_tasks: 6 | file: cis_4.1.x.yml 7 | 8 | - name: "SECTION | 4.2 | Configure nftables software" 9 | when: deb12cis_firewall_package == "nftables" 10 | ansible.builtin.import_tasks: 11 | file: cis_4.2.x.yml 12 | 13 | - name: "SECTION | 4.3.1.x | Configure iptables software" 14 | when: deb12cis_firewall_package == "iptables" 15 | ansible.builtin.import_tasks: 16 | file: cis_4.3.1.x.yml 17 | 18 | - name: "SECTION | 4.3.2.x | Configure ipv4 iptables" 19 | when: deb12cis_firewall_package == "iptables" 20 | ansible.builtin.import_tasks: 21 | file: cis_4.3.2.x.yml 22 | 23 | - name: "SECTION | 4.3.3.x | Configure ipv6 iptables" 24 | when: 25 | - deb12cis_firewall_package == "iptables" 26 | - deb12cis_ipv6_required 27 | ansible.builtin.import_tasks: 28 | file: cis_4.3.3.x.yml 29 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.2.1 | PATCH | Ensure sudo is installed" 4 | when: deb12cis_rule_5_2_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - sudo 10 | - rule_5.2.1 11 | - NIST800-53R5_AC-6 12 | ansible.builtin.package: 13 | name: "{{ deb12cis_sudo_package }}" 14 | state: present 15 | 16 | - name: "5.2.2 | PATCH | Ensure sudo commands use pty" 17 | when: deb12cis_rule_5_2_2 18 | tags: 19 | - level1-server 20 | - level1-workstation 21 | - patch 22 | - sudo 23 | - rule_5.2.2 24 | - NIST800-53R5_AC-6 25 | ansible.builtin.lineinfile: 26 | path: /etc/sudoers 27 | regexp: ^(Defaults\s*)use_pty 28 | line: \1use_pty 29 | backrefs: true 30 | validate: '/usr/sbin/visudo -cf %s' 31 | 32 | - name: "5.2.3 | PATCH | Ensure sudo log file exists" 33 | when: deb12cis_rule_5_2_3 34 | tags: 35 | - level1-server 36 | - level1-workstation 37 | - patch 38 | - sudo 39 | - rule_5.2.3 40 | - NIST800-53R5_AU-3 41 | - NIST800-53R5_AU-12 42 | ansible.builtin.lineinfile: 43 | path: /etc/sudoers 44 | regexp: ^Defaults\s*logfile= 45 | line: Defaults logfile="{{ deb12cis_sudolog_location }}" 46 | validate: '/usr/sbin/visudo -cf %s' 47 | 48 | - name: "5.2.4 | PATCH | Ensure users must provide password for escalation" 49 | when: deb12cis_rule_5_2_4 50 | tags: 51 | - level2-server 52 | - level2-workstation 53 | - patch 54 | - sudo 55 | - rule_5.2.4 56 | - NIST800-53R5_AC-6 57 | block: 58 | - name: "5.2.4 | AUDIT | Ensure users must provide password for escalation | discover accts with NOPASSWD" 59 | ansible.builtin.shell: grep -Ei '(nopasswd)' /etc/sudoers /etc/sudoers.d/* | cut -d':' -f1 60 | become: true 61 | changed_when: false 62 | failed_when: false 63 | register: discovered_sudo_nopasswd 64 | 65 | - name: "5.2.4 | PATCH | Ensure users must provide password for escalation" 66 | when: discovered_sudo_nopasswd.stdout | length > 0 67 | ansible.builtin.replace: 68 | path: "{{ item }}" 69 | regexp: '^((?!#|{% for name in deb12cis_sudoers_exclude_nopasswd_list %}{{ name }}{% if not loop.last -%}|{%- endif -%}{% endfor %}).*)NOPASSWD(.*)' 70 | replace: '\1PASSWD\2' 71 | validate: '/usr/sbin/visudo -cf %s' 72 | loop: "{{ discovered_sudo_nopasswd.stdout_lines }}" 73 | 74 | - name: "5.2.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" 75 | when: deb12cis_rule_5_2_5 76 | tags: 77 | - level1-server 78 | - level1-workstation 79 | - patch 80 | - sudo 81 | - rule_5.2.5 82 | - NIST800-53R5_AC-6 83 | block: 84 | - name: "5.2.5 | AUDIT | Ensure re-authentication for privilege escalation is not disabled globally" 85 | ansible.builtin.shell: grep -Ei '(!authenticate)' /etc/sudoers /etc/sudoers.d/* | cut -d':' -f1 86 | become: true 87 | changed_when: false 88 | failed_when: false 89 | register: discovered_sudo_reauthenticate 90 | 91 | - name: "5.2.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" 92 | when: discovered_sudo_reauthenticate.stdout | length > 0 93 | ansible.builtin.replace: 94 | path: "{{ item }}" 95 | regexp: '^([^#].*)!authenticate(.*)' 96 | replace: '\1authenticate\2' 97 | validate: '/usr/sbin/visudo -cf %s' 98 | loop: "{{ discovered_sudo_reauthenticate.stdout_lines }}" 99 | 100 | - name: "5.2.6 | PATCH | Ensure sudo authentication timeout is configured correctly" 101 | when: deb12cis_rule_5_2_6 102 | tags: 103 | - level1-server 104 | - level1-workstation 105 | - patch 106 | - sudo 107 | - rule_5.2.6 108 | - NIST800-53R5_AC-6 109 | block: 110 | - name: "5.2.6 | AUDIT | Ensure sudo authentication timeout is configured correctly | Get files with timeout set" 111 | ansible.builtin.shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort 112 | changed_when: false 113 | failed_when: false 114 | register: discovered_sudo_timeout_files 115 | 116 | - name: "5.2.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" 117 | ansible.builtin.lineinfile: 118 | path: /etc/sudoers 119 | regexp: 'Defaults timestamp_timeout=' 120 | line: "Defaults timestamp_timeout={{ deb12cis_sudo_timestamp_timeout }}" 121 | validate: '/usr/sbin/visudo -cf %s' 122 | when: discovered_sudo_timeout_files.stdout | length == 0 123 | 124 | - name: "5.2.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" 125 | ansible.builtin.replace: 126 | path: "{{ item }}" 127 | regexp: 'timestamp_timeout=(\d+)' 128 | replace: "timestamp_timeout={{ deb12cis_sudo_timestamp_timeout }}" 129 | validate: '/usr/sbin/visudo -cf %s' 130 | loop: "{{ discovered_sudo_timeout_files.stdout_lines }}" 131 | when: discovered_sudo_timeout_files.stdout | length > 0 132 | 133 | - name: "5.2.7 | PATCH | Ensure access to the su command is restricted" 134 | when: deb12cis_rule_5_2_7 135 | tags: 136 | - level1-server 137 | - level1-workstation 138 | - patch 139 | - sudo 140 | - rule_5.2.7 141 | - NIST800-53R5_AC-3 142 | - NIST800-53R5_MP-2 143 | block: 144 | - name: "5.2.7 | PATCH | Ensure access to the su command is restricted | Ensure sugroup exists" 145 | ansible.builtin.group: 146 | name: "{{ deb12cis_sugroup }}" 147 | state: present 148 | register: discovered_sugroup 149 | 150 | - name: "5.2.7 | PATCH | Ensure access to the su command is restricted | remove users from group" 151 | ansible.builtin.lineinfile: 152 | path: /etc/group 153 | regexp: '^{{ deb12cis_sugroup }}(:.:.*:).*$' 154 | line: '{{ deb12cis_sugroup }}\g<1>' 155 | backrefs: true 156 | 157 | - name: "5.2.7 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" 158 | ansible.builtin.lineinfile: 159 | path: /etc/pam.d/su 160 | regexp: '^(#)?auth\s+required\s+pam_wheel\.so' 161 | line: 'auth required pam_wheel.so use_uid group={{ deb12cis_sugroup }}' 162 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.1.1 | PATCH | Ensure latest version of pam is installed" 4 | when: 5 | - deb12cis_rule_5_3_1_1 6 | - ansible_facts.packages['libpam-runtime'][0]['version'] is version('1.5.2-6', '<=') or 7 | "'libpam-runtime' not in ansible_facts.packages" 8 | tags: 9 | - level1-server 10 | - level1-workstation 11 | - patch 12 | - pam 13 | - rule_5.3.1.1 14 | - NIST800-53R5_NA 15 | ansible.builtin.package: 16 | name: libpam-runtime 17 | state: latest 18 | 19 | - name: "5.3.1.2 | PATCH | Ensure libpam-modules is installed" 20 | when: 21 | - deb12cis_rule_5_3_1_2 22 | - ansible_facts.packages['libpam-modules'][0]['version'] is version('1.5.2-6', '<=') or 23 | "'libpam-modules' not in ansible_facts.packages" 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - patch 28 | - pam 29 | - rule_5.3.1.2 30 | - NIST800-53R5_NA 31 | ansible.builtin.package: 32 | name: libpam-modules 33 | state: latest 34 | 35 | - name: "5.3.1.3 | PATCH | Ensure libpam-pwquality is installed" 36 | when: 37 | - deb12cis_rule_5_3_1_3 38 | - "'libpam-pwquality' not in ansible_facts.packages" 39 | tags: 40 | - level1-server 41 | - level1-workstation 42 | - patch 43 | - pam 44 | - rule_5.3.1.3 45 | - NIST800-53R5_NA 46 | ansible.builtin.package: 47 | name: libpam-pwquality 48 | state: latest 49 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.2.1 | PATCH | Ensure pam_unix module is enabled" 4 | when: 5 | - deb12cis_rule_5_3_2_1 6 | - deb12cis_disruption_high 7 | - deb12cis_pam_auth_unix 8 | - deb12cis_pam_create_pamunix_file 9 | tags: 10 | - level1-server 11 | - level1-workstation 12 | - patch 13 | - rule_5.3.2.1 14 | - Pam_auth_update 15 | - pam_unix 16 | - NIST800-53R5_IA-5 17 | ansible.builtin.template: 18 | src: "{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwunix_file }}.j2" 19 | dest: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwunix_file }}" 20 | owner: root 21 | group: root 22 | mode: 'u-x,go-rwx' 23 | notify: Pam_auth_update_pwunix 24 | 25 | - name: "5.3.2.2 | PATCH | Ensure pam_faillock module is enabled" 26 | when: 27 | - deb12cis_rule_5_3_2_2 28 | - deb12cis_disruption_high 29 | - deb12cis_pam_auth_faillock 30 | - deb12cis_pam_create_faillock_files 31 | tags: 32 | - level1-server 33 | - level1-workstation 34 | - patch 35 | - rule_5.3.2.2 36 | - Pam_auth_update 37 | - pam_faillock 38 | - NIST800-53R5_NA 39 | ansible.builtin.template: 40 | src: "{{ deb12cis_pam_confd_dir }}{{ item }}.j2" 41 | dest: "/{{ deb12cis_pam_confd_dir }}{{ item }}" 42 | owner: root 43 | group: root 44 | mode: 'u-x,go-rwx' 45 | loop: 46 | - "{{ deb12cis_pam_faillock_file }}" 47 | - "{{ deb12cis_pam_faillock_notify_file }}" 48 | notify: 49 | - Pam_auth_update_pwfaillock 50 | - Pam_auth_update_pwfaillock_notify 51 | 52 | - name: "5.3.2.3 | PATCH | Ensure pam_pwquality module is enabled" 53 | when: 54 | - deb12cis_rule_5_3_2_3 55 | - deb12cis_disruption_high 56 | - deb12cis_pam_create_pwquality_files 57 | - deb12cis_pam_auth_pwquality 58 | tags: 59 | - level1-server 60 | - level1-workstation 61 | - patch 62 | - rule_5.3.2.3 63 | - Pam_auth_update 64 | - pam_quality 65 | - NIST800-53R5_NA 66 | ansible.builtin.template: 67 | src: "{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwquality_file }}.j2" 68 | dest: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwquality_file }}" 69 | owner: root 70 | group: root 71 | mode: 'u-x,go-rwx' 72 | notify: Pam_auth_update_pwquality 73 | 74 | - name: "5.3.2.4 | PATCH | Ensure pam_pwhistory module is enabled" 75 | when: 76 | - deb12cis_rule_5_3_2_4 77 | - deb12cis_disruption_high 78 | - deb12cis_pam_create_pwhistory_files 79 | - deb12cis_pam_auth_pwhistory 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - patch 84 | - rule_5.3.2.4 85 | - Pam_auth_update 86 | - pam_history 87 | - NIST800-53R5_NA 88 | ansible.builtin.template: 89 | src: "{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwhistory_file }}.j2" 90 | dest: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwhistory_file }}" 91 | owner: root 92 | group: root 93 | mode: 'u-x,go-rwx' 94 | notify: Pam_auth_update_pwhistory 95 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.3.1.1 | PATCH | Ensure password failed attempts lockout is configured" 4 | when: deb12cis_rule_5_3_3_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - rule_5.3.3.1.1 10 | - pam 11 | - NIST800-53R5_NA 12 | block: 13 | - name: "5.3.3.1.1 | PATCH | Ensure password failed attempts lockout is configured | configure faillock.conf" 14 | ansible.builtin.lineinfile: 15 | path: /etc/security/faillock.conf 16 | regexp: '^deny' 17 | line: "deny = {{ deb12cis_faillock_deny }}" 18 | insertafter: '^# end of pam-auth-update config' 19 | create: true 20 | mode: 'u-x,go-wx' 21 | 22 | - name: "5.3.3.1.1 | AUDIT | Ensure password failed attempts lockout is configured | discover pam config with deny" 23 | ansible.builtin.shell: grep -Pl -- '\bpam_faillock\.so\h+([^#\n\r]+\h+)?deny\b' /usr/share/pam-configs/* 24 | register: discovered_faillock_deny_files 25 | changed_when: false 26 | failed_when: discovered_faillock_deny_files.rc not in [ 0, 1 ] 27 | 28 | - name: "5.3.3.1.1 | PATCH | Ensure password failed attempts lockout is configured | if exists remove deny from faillock line in pam-auth conf files" 29 | when: discovered_faillock_deny_files.stdout | length > 0 30 | ansible.builtin.replace: 31 | path: "{{ item.path }}" 32 | regexp: '(*.pam_faillock.so\s*)deny\s*=\s*\d+\b(.*)' 33 | replace: \1\2 34 | loop: "{{ prelim_pam_conf_files.files }}" 35 | 36 | - name: "5.3.3.1.2 | PATCH | Ensure password unlock time is configured" 37 | when: deb12cis_rule_5_3_3_1_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - patch 42 | - rule_5.3.3.1.2 43 | - pam 44 | - NIST800-53R5_NA 45 | block: 46 | - name: "5.3.3.1.2 | PATCH | Ensure password unlock time is configured | configure faillock.conf" 47 | ansible.builtin.lineinfile: 48 | path: /etc/security/faillock.conf 49 | regexp: '^unlock_time' 50 | line: "unlock_time = {{ deb12cis_faillock_unlock_time }}" 51 | insertafter: '^# end of pam-auth-update config' 52 | create: true 53 | mode: 'u-x,go-wx' 54 | 55 | - name: "5.3.3.1.2 | AUDIT | Ensure password unlock time is configured | discover pam config with unlock_time" 56 | ansible.builtin.shell: grep -Pl -- '\bpam_faillock\.so\h+([^#\n\r]+\h+)?unlock_time\b' /usr/share/pam-configs/* 57 | register: discovered_faillock_unlock_files 58 | changed_when: false 59 | failed_when: discovered_faillock_unlock_files.rc not in [ 0, 1 ] 60 | 61 | - name: "5.3.3.1.2 | PATCH | Ensure password unlock time is configured | if exists remove unlock_time from faillock line in pam-auth conf files" 62 | when: discovered_faillock_unlock_files.stdout | length > 0 63 | ansible.builtin.replace: 64 | path: "{{ item.path }}" 65 | regexp: '(*.pam_faillock.so\s*)unlock_time\s*=\s*\b(.*)' 66 | replace: \1\2 67 | loop: "{{ prelim_pam_conf_files.files }}" 68 | 69 | - name: "5.3.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account" 70 | when: deb12cis_rule_5_3_3_1_3 71 | tags: 72 | - level2-server 73 | - level2-workstation 74 | - patch 75 | - rule_5.3.3.1.3 76 | - pam 77 | - NIST800-53R5_NA 78 | block: 79 | - name: "5.3.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account | configure faillock.conf" 80 | ansible.builtin.lineinfile: 81 | path: /etc/security/faillock.conf 82 | regexp: '^{{ deb12cis_pamroot_lock_option }}' 83 | line: "{{ deb12cis_pamroot_lock_string }}" 84 | insertafter: '^# end of pam-auth-update config' 85 | create: true 86 | mode: 'u-x,go-wx' 87 | 88 | - name: "5.3.3.1.3 | AUDIT | Ensure password failed attempts lockout includes root account | discover pam config with unlock_time" 89 | ansible.builtin.shell: grep -Pl -- '\bpam_faillock\.so\h+([^#\n\r]+\h+)?(even_deny_root\b|root_unlock_time\s*=\s*\d+\b)' /usr/share/pam-configs/* 90 | register: discovered_faillock_rootlock_files 91 | changed_when: false 92 | failed_when: discovered_faillock_rootlock_files.rc not in [ 0, 1 ] 93 | 94 | - name: "5.3.3.1.3 | PATCH | Ensure password failed attempts lockout includes root account | if exists remove unlock_time from faillock line in pam-auth conf files" 95 | when: discovered_faillock_rootlock_files.stdout | length > 0 96 | ansible.builtin.replace: 97 | path: "{{ item.path }}" 98 | regexp: '(*.pam_faillock.so\s*)(even_deny_root\b|root_unlock_time\s*=\s*\d+\b)(.*)' 99 | replace: \1\3 100 | loop: "{{ prelim_pam_conf_files.files }}" 101 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.3.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.3.3.1 | PATCH | Ensure password history remember is configured" 4 | when: 5 | - deb12cis_rule_5_3_3_3_1 6 | - deb12cis_disruption_high 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_5.3.3.3.1 12 | - pam 13 | - NIST800-53R5_IA-5 14 | block: 15 | - name: "5.3.3.3.1 | AUDIT | Ensure password history remember is configured | Check existing files" 16 | ansible.builtin.shell: grep -Psi -- '^\s*password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+([^#\n\r]+\s+)?remember=\d+\b' /etc/pam.d/common-password 17 | register: discovered_pwhistory_remember 18 | changed_when: false 19 | failed_when: discovered_pwhistory_remember.rc not in [0, 1] 20 | 21 | - name: "5.3.3.3.1 | PATCH | Ensure password number of changed characters is configured | Ensure remember is set" 22 | when: discovered_pwhistory_remember.stdout | length > 0 23 | ansible.builtin.lineinfile: 24 | path: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwhistory_file }}" 25 | regexp: ^(password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+)(.*)(remember=\d+) 26 | line: '\1\2\3 remember={{ deb12cis_pamd_pwhistory_remember }}' 27 | backrefs: true 28 | notify: Pam_auth_update_pwhistory 29 | 30 | - name: "5.3.3.3.2 | PATCH | Ensure password history is enforced for the root user" 31 | when: 32 | - deb12cis_rule_5_3_3_3_2 33 | - deb12cis_disruption_high 34 | tags: 35 | - level1-server 36 | - level1-workstation 37 | - patch 38 | - rule_5.3.3.3.2 39 | - pam 40 | - NIST800-53R5_IA-5 41 | block: 42 | - name: "5.3.3.3.2 | AUDIT | Ensure password history is enforced for the root user | Check existing files" 43 | ansible.builtin.shell: grep -Psi -- '^\s*password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+([^#\n\r]+\s+)?enforce_for_root\b' /etc/pam.d/common-password 44 | register: discovered_pwhistory_enforce_for_root 45 | changed_when: false 46 | failed_when: discovered_pwhistory_enforce_for_root.rc not in [0, 1] 47 | 48 | - name: "5.3.3.3.2 | PATCH | Ensure password history is enforced for the root user | Ensure remember is set" 49 | when: discovered_pwhistory_enforce_for_root.stdout | length > 0 50 | ansible.builtin.lineinfile: 51 | path: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwhistory_file }}" 52 | regexp: ^(password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+)(.*)(enforce_for_root) 53 | line: '\1\2\3 enforce_for_root' 54 | backrefs: true 55 | notify: Pam_auth_update_pwhistory 56 | 57 | - name: "5.3.3.3.3 | PATCH | Ensure pam_pwhistory includes use_authtok" 58 | when: 59 | - deb12cis_rule_5_3_3_3_3 60 | - deb12cis_disruption_high 61 | tags: 62 | - level1-server 63 | - level1-workstation 64 | - patch 65 | - rule_5.3.3.3.3 66 | - pam 67 | - NIST800-53R5_IA-5 68 | block: 69 | - name: "5.3.3.3.3 | AUDIT | Ensure pam_pwhistory includes use_authtok | Check existing files" 70 | ansible.builtin.shell: grep -Psi -- '^\s*password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+([^#\n\r]+\s+)?use_authtok\b' /etc/pam.d/common-password 71 | register: discovered_pwhistory_use_authtok 72 | changed_when: false 73 | failed_when: discovered_pwhistory_use_authtok.rc not in [0, 1] 74 | 75 | - name: "5.3.3.3.3 | PATCH | Ensure pam_pwhistory includes use_authtok | Ensure remember is set" 76 | when: discovered_pwhistory_use_authtok.stdout | length > 0 77 | ansible.builtin.lineinfile: 78 | path: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwhistory_file }}" 79 | regexp: ^(password\s+[^#\n\r]+\s+pam_pwhistory\.so\s+)(.*)(use_authtok) 80 | line: '\1\2\3 use_authtok' 81 | backrefs: true 82 | notify: Pam_auth_update_pwhistory 83 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.3.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.3.4.1 | PATCH | Ensure pam_unix does not include nullok" 4 | when: 5 | - deb12cis_rule_5_3_3_4_1 6 | - deb12cis_disruption_high 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_5.3.3.4.1 12 | - pam 13 | - NIST800-53R5_NA 14 | block: 15 | - name: "5.3.3.4.1 | PATCH | Ensure pam_unix does not include nullok | capture state" 16 | ansible.builtin.shell: grep -E "pam_unix.so.*nullok" /etc/pam.d/common-* /usr/share/pam-configs/* | cut -d ':' -f1 | uniq 17 | changed_when: false 18 | failed_when: discovered_pam_unix_nullok.rc not in [ 0, 1 ] 19 | register: discovered_pam_unix_nullok 20 | 21 | - name: "5.3.3.4.1 | PATCH | Ensure pam_unix does not include nullok | Ensure nullok removed" 22 | when: discovered_pam_unix_nullok.stdout | length > 0 23 | ansible.builtin.replace: 24 | path: "{{ item }}" 25 | regexp: nullok 26 | replace: '' 27 | loop: "{{ discovered_pam_unix_nullok.stdout_lines }}" 28 | notify: Pam_auth_update_pwunix 29 | 30 | - name: "5.3.3.4.2 | PATCH | Ensure pam_unix does not include remember" 31 | when: deb12cis_rule_5_3_3_4_2 32 | tags: 33 | - level1-server 34 | - level1-workstation 35 | - patch 36 | - pam 37 | - rule_5.3.3.4.2 38 | - NIST800-53R5_NA 39 | block: 40 | - name: "5.3.3.4.2 | AUDIT | Ensure pam_unix does not include remember | capture state" 41 | ansible.builtin.shell: grep -PH -- '^\h*^\h*[^#\n\r]+\h+pam_unix\.so\b' /etc/pam.d/common-{password,auth,account,session,session-noninteractive} | grep -Pv -- '\bremember=\d\b' 42 | changed_when: false 43 | failed_when: discovered_pam_unix_remember.rc not in [ 0, 1 ] 44 | register: discovered_pam_unix_remember 45 | 46 | - name: "5.3.3.4.2 | PATCH | Ensure pam_unix does not include remember | Ensure remember removed" 47 | when: discovered_pam_unix_remember.stdout | length > 0 48 | ansible.builtin.replace: 49 | path: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwunix_file }}" 50 | regexp: remember=\d+ 51 | replace: '' 52 | notify: Pam_auth_update_pwunix 53 | 54 | - name: "5.3.3.4.3 | PATCH | Ensure pam_unix includes a strong password hashing algorithm" 55 | when: deb12cis_rule_5_3_3_4_3 56 | tags: 57 | - level1-server 58 | - level1-workstation 59 | - patch 60 | - pam 61 | - rule_5.3.3.4.3 62 | - NIST800-53R5_IA-5 63 | block: 64 | - name: "5.3.3.4.3 | AUDIT | Ensure pam_unix includes a strong password hashing algorithm | capture state" 65 | ansible.builtin.shell: grep -PH -- '^\h*password\h+([^#\n\r]+)\h+pam_unix\.so\h+([^#\n\r]+\h+)?("{{ deb12cis_passwd_hash_algo }}")\b' /etc/pam.d/common-password 66 | changed_when: false 67 | failed_when: discovered_pam_unix_pwhash.rc not in [ 0, 1 ] 68 | register: discovered_pam_unix_pwhash 69 | 70 | - name: "5.3.3.4.3 | PATCH | Ensure pam_unix includes a strong password hashing algorithm | Ensure hash algorithm set" 71 | when: discovered_pam_unix_pwhash.stdout | length > 0 72 | ansible.builtin.replace: 73 | path: "/{{ deb12cis_pam_confd_dir }}{{ deb12cis_pam_pwunix_file }}" 74 | regexp: "(md5|bigcrypt|sha256|blowfish|gost_yescrypt|sha512|yescrypt)" 75 | replace: '{{ deb12cis_passwd_hash_algo }}' 76 | notify: Pam_auth_update_pwunix 77 | 78 | - name: "5.3.3.4.4 | PATCH | Ensure pam_unix includes use_authtok" 79 | when: deb12cis_rule_5_3_3_4_4 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - patch 84 | - pam 85 | - rule_5.3.3.4.4 86 | - NIST800-53R5_IA-5 87 | block: 88 | - name: "5.3.3.4.4 | PATCH | Ensure pam_unix includes use_authtok | capture state" 89 | ansible.builtin.shell: grep -PH -- '^\h*password\h+([^#\n\r]+)\h+pam_unix\.so\h+([^#\n\r]+\h+)?use_authtok\b' /etc/pam.d/common-password 90 | changed_when: false 91 | failed_when: discovered_pam_unix_authtok.rc not in [ 0, 1 ] 92 | register: discovered_pam_unix_authtok 93 | 94 | - name: "5.3.3.4.4 | PATCH | Ensure pam_unix includes use_authtok | pam_files" 95 | when: 96 | - discovered_pam_unix_authtok is defined 97 | - discovered_pam_unix_authtok | length > 0 98 | ansible.builtin.lineinfile: 99 | path: "/etc/pam.d/common-password" 100 | regexp: ^(\s*password\s+[success=end.*]\s+pam_unix\.so)(.*)\s+use_authtok\s*=\s*\S+(.*$) 101 | line: \1\2\3 use_authtok 102 | backrefs: true 103 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.4.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.4.3.1 | PATCH | Ensure nologin is not listed in /etc/shells" 4 | when: deb12cis_rule_5_4_3_1 5 | tags: 6 | - level2-server 7 | - level2-workstation 8 | - patch 9 | - shells 10 | - rule_5.4.3.1 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 | ansible.builtin.replace: 17 | path: /etc/shells 18 | regexp: nologin 19 | replace: "" 20 | 21 | - name: "5.4.3.2 | PATCH | Ensure default user shell timeout is configured" 22 | when: deb12cis_rule_5_4_3_2 23 | tags: 24 | - level1-server 25 | - level1-workstation 26 | - patch 27 | - shell 28 | - rule_5.4.3.2 29 | ansible.builtin.blockinfile: 30 | path: "{{ item.path }}" 31 | state: "{{ item.state }}" 32 | marker: "# {mark} - CIS benchmark - Ansible-lockdown" 33 | create: true 34 | mode: 'u-x,go-wx' 35 | block: | 36 | TMOUT={{ deb12cis_shell_session_timeout }} 37 | readonly TMOUT 38 | export TMOUT 39 | loop: 40 | - { path: "{{ deb12cis_shell_session_file }}", state: present } 41 | - { path: /etc/profile, state: "{{ (deb12cis_shell_session_file == '/etc/profile') | ternary('present', 'absent') }}" } 42 | 43 | - name: "5.4.3.3 | PATCH | Ensure default user umask is configured" 44 | when: deb12cis_rule_5_4_3_3 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - patch 49 | - umask 50 | - rule_5.4.3.3 51 | - NIST800-53R5_AC-3 52 | - NIST800-53R5_MP-2 53 | ansible.builtin.replace: 54 | path: "{{ item.path }}" 55 | regexp: (?i)(umask\s+\d*) 56 | replace: '{{ item.line }} {{ deb12cis_bash_umask }}' 57 | loop: 58 | - { path: '/etc/profile', line: 'umask' } 59 | - { path: '/etc/login.defs', line: 'UMASK' } 60 | -------------------------------------------------------------------------------- /tasks/section_5/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 5.1 | Configure SSH Server" 4 | ansible.builtin.import_tasks: 5 | file: cis_5.1.x.yml 6 | 7 | - name: "SECTION | 5.2 | Configure privilege escalation" 8 | when: not system_is_container 9 | ansible.builtin.import_tasks: 10 | file: cis_5.2.x.yml 11 | 12 | - name: "SECTION | 5.3.1.x | Configure PAM software packages" 13 | when: not system_is_container 14 | ansible.builtin.import_tasks: 15 | file: cis_5.3.1.x.yml 16 | 17 | - name: "SECTION | 5.3.2.x | Configure pam-auth-update" 18 | when: not system_is_container 19 | ansible.builtin.import_tasks: 20 | file: cis_5.3.2.x.yml 21 | 22 | - name: "SECTION | 5.3.3.1.x | Configure pam_faillock module" 23 | when: not system_is_container 24 | ansible.builtin.import_tasks: 25 | file: cis_5.3.3.1.x.yml 26 | 27 | - name: "SECTION | 5.3.3.2.x | Configure pam_pwquality module" 28 | when: not system_is_container 29 | ansible.builtin.import_tasks: 30 | file: cis_5.3.3.2.x.yml 31 | 32 | - name: "SECTION | 5.3.3.3.x | Configure pam_pwhistory module" 33 | when: not system_is_container 34 | ansible.builtin.import_tasks: 35 | file: cis_5.3.3.3.x.yml 36 | 37 | - name: "SECTION | 5.3.3.4.x | Configure pam_unix module" 38 | when: not system_is_container 39 | ansible.builtin.import_tasks: 40 | file: cis_5.3.3.4.x.yml 41 | 42 | - name: "SECTION | 5.4.1.x | Configure shadow password suite parameters" 43 | when: not system_is_container 44 | ansible.builtin.import_tasks: 45 | file: cis_5.4.1.x.yml 46 | 47 | - name: "SECTION | 5.4.2.x | Configure root and system accounts and environment" 48 | when: not system_is_container 49 | ansible.builtin.import_tasks: 50 | file: cis_5.4.2.x.yml 51 | 52 | - name: "SECTION | 5.4.3.x | Configure user default environment" 53 | when: not system_is_container 54 | ansible.builtin.import_tasks: 55 | file: cis_5.4.3.x.yml 56 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.1.1 | PATCH | Ensure AIDE is installed" 4 | when: 5 | - deb12cis_rule_6_1_1 6 | - deb12cis_config_aide 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - rule_6.1.1 12 | - aide 13 | block: 14 | - name: "6.1.1 | PATCH | Ensure AIDE is installed" 15 | when: 16 | - "'aide' not in ansible_facts.packages or 17 | 'aide-common' not in ansible_facts.packages" 18 | ansible.builtin.package: 19 | name: ['aide', 'aide-common'] 20 | state: present 21 | update_cache: true 22 | register: discovered_aide_pkgs_added 23 | 24 | - name: "6.1.1 | PATCH | Ensure AIDE is installed | Recapture packages" 25 | when: discovered_aide_pkgs_added.skipped is not defined 26 | ansible.builtin.package_facts: 27 | manager: auto 28 | 29 | - name: "6.1.1 | AUDIT | Ensure AIDE is installed | Check file exists" 30 | ansible.builtin.stat: 31 | path: "{{ deb12cis_aide_db_file }}" 32 | register: discovered_aide_db_file 33 | 34 | - name: "6.1.1 | AUDIT | Ensure AIDE is installed | Check current db file age" 35 | when: discovered_aide_db_file.stat.exists 36 | ansible.builtin.find: 37 | path: "{{ deb12cis_aide_db_file | dirname }}" 38 | pattern: "{{ deb12cis_aide_db_file | basename }}" 39 | age: "{{ deb12cis_aide_db_file_age }}" 40 | register: discovered_aide_db_age 41 | 42 | - name: "6.1.1 | PATCH | Ensure AIDE is installed | Configure AIDE" 43 | when: 44 | - not ansible_check_mode 45 | - not discovered_aide_db_file.stat.exists or 46 | (discovered_aide_db_age.files | length > 0) or 47 | deb12cis_aide_db_recreate 48 | block: 49 | - name: "6.1.1 | PATCH | Ensure AIDE is installed | Remove current db file" 50 | ansible.builtin.file: 51 | path: "{{ deb12cis_aide_db_file }}" 52 | state: absent 53 | 54 | - name: "6.1.1 | PATCH | Ensure AIDE is installed | Configure AIDE" 55 | when: not ansible_check_mode 56 | ansible.builtin.command: "{{ aide_initiate_command }}" 57 | changed_when: true 58 | args: 59 | creates: "{{ deb12cis_aide_db_file }}" 60 | async: "{{ deb12cis_aide_init_async }}" 61 | poll: "{{ deb12cis_aide_init_poll }}" 62 | 63 | - name: "6.1.2 | PATCH | Ensure filesystem integrity is regularly checked" 64 | when: 65 | - deb12cis_rule_6_1_2 66 | - not system_is_ec2 67 | tags: 68 | - level1-server 69 | - level1-workstation 70 | - aide 71 | - file_integrity 72 | - patch 73 | - rule_6.1.2 74 | - NIST800-53R5_AU-2 75 | block: 76 | - name: "6.1.2 | PATCH | Ensure filesystem integrity is regularly checked" 77 | when: deb12cis_aide_scan == "cron" 78 | ansible.builtin.cron: 79 | name: Run AIDE integrity check 80 | cron_file: "{{ deb12cis_aide_cron_file }}" 81 | user: "{{ deb12cis_aide_cron_user }}" 82 | minute: "{{ deb12cis_aide_cron_minute | default('0') }}" 83 | hour: "{{ deb12cis_aide_cron_hour | default('5') }}" 84 | day: "{{ ddeb12cis_aide_cron_day | default('*') }}" 85 | month: "{{ deb12cis_aide_cron_month | default('*') }}" 86 | weekday: "{{ deb12cis_aide_cron_weekday | default('*') }}" 87 | job: "{{ deb12cis_aide_cron_job }}" 88 | 89 | - name: "6.1.2 | PATCH | Ensure filesystem integrity is regularly checked | aide service" 90 | when: deb12cis_aide_scan == "timer" 91 | ansible.builtin.systemd: 92 | name: aidecheck.service 93 | enabled: true 94 | 95 | - name: "6.1.2 | PATCH | Ensure filesystem integrity is regularly checked | aide service" 96 | when: deb12cis_aide_scan == "timer" 97 | ansible.builtin.systemd: 98 | name: aidecheck.timer 99 | state: started 100 | enabled: true 101 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.2.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.2.1.1.1 | PATCH | Ensure journald service is enabled and active" 4 | when: deb12cis_rule_6_2_1_1_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - audit 9 | - journald 10 | - rule_6.2.1.1.1 11 | - NIST800-53R5_AU-2 12 | - NIST800-53R5_AU-7 13 | - NIST800-53R5_AU-12 14 | ansible.builtin.systemd: 15 | name: systemd-journald.service 16 | masked: false 17 | state: started 18 | 19 | - name: "6.2.1.1.2 | PATCH | Ensure journald log file access is configured" 20 | when: deb12cis_rule_6_2_1_1_2 21 | tags: 22 | - level1-server 23 | - level1-workstation 24 | - audit 25 | - journald 26 | - rule_6.2.1.1.2 27 | - NIST800-53R5_AU-2 28 | - NIST800-53R5_AU-3 29 | - NIST800-53R5_AU-12 30 | - NIST800-53R5_SI-5 31 | - NIST800-53R5_MP-2 32 | block: 33 | - name: "6.2.1.1.2 | PATCH | Ensure journald log file access is configured | Default file permissions" 34 | ansible.builtin.file: 35 | path: /usr/lib/tmpfiles.d/systemd.conf 36 | mode: 'u-x,g-wx,o-rwx' 37 | 38 | - name: "6.2.1.1.2 | AUDIT | Ensure journald log file access is configured | Check for override file" 39 | ansible.builtin.stat: 40 | path: /etc/tmpfiles.d/systemd.conf 41 | register: discovered_journald_tmpfile_override 42 | 43 | - name: "6.2.1.1.2 | AUDIT | Ensure journald log file access is configured | If override file check for journal" 44 | when: discovered_journald_tmpfile_override.stat.exists 45 | ansible.builtin.shell: grep -E 'z /var/log/journal/%m/system.journal \d*' /usr/lib/tmpfiles.d/systemd.conf 46 | register: discovered_journald_fileperms_override 47 | changed_when: false 48 | failed_when: discovered_journald_fileperms_override.rc not in [ 0, 1 ] 49 | 50 | - name: "6.2.1.1.2 | AUDIT | Ensure journald log file access is configured | Warning if override found" 51 | when: 52 | - discovered_journald_tmpfile_override.stat.exists 53 | - discovered_journald_fileperms_override.stdout | length > 0 54 | ansible.builtin.debug: 55 | msg: "Warning!! - tmpfiles override found /usr/lib/tmpfiles.d/systemd.conf affecting journald files please confirm matches site policy" 56 | 57 | - name: "6.2.1.1.2 | AUDIT | Ensure journald log file access is configured | Warning if override found" 58 | when: 59 | - discovered_journald_tmpfile_override.stat.exists 60 | - discovered_journald_fileperms_override.stdout | length > 0 61 | ansible.builtin.import_tasks: 62 | file: warning_facts.yml 63 | vars: 64 | warn_control_id: '6.2.1.1.2' 65 | 66 | - name: "6.2.1.1.3 | PATCH | Ensure journald log file rotation is configured" 67 | when: deb12cis_rule_6_2_1_1_3 68 | tags: 69 | - level1-server 70 | - level1-workstation 71 | - patch 72 | - journald 73 | - rule_6.2.1.1.3 74 | - NIST800-53R5_AU-2 75 | - NIST800-53R5_AU-7 76 | - NIST800-53R5_AU-12 77 | notify: Restart journald 78 | block: 79 | - name: "6.2.1.1.3 | PATCH | Ensure journald.conf.d log exists | Create directory" 80 | ansible.builtin.file: 81 | path: /etc/systemd/journald.conf.d 82 | state: directory 83 | owner: root 84 | group: root 85 | mode: '0750' 86 | 87 | - name: "6.2.1.1.3 | PATCH | Ensure journald log file rotation is configured | Add file" 88 | ansible.builtin.template: 89 | src: etc/systemd/journald.conf.d/rotation.conf.j2 90 | dest: /etc/systemd/journald.conf.d/rotation.conf 91 | owner: root 92 | group: root 93 | mode: 'u-x,g-wx,o-rwx' 94 | 95 | - name: "6.2.1.1.3 | PATCH | Ensure journald log file rotation is configured | comment out current entries" 96 | ansible.builtin.replace: 97 | path: /etc/systemd/journald.conf 98 | regexp: "{{ item }}" 99 | replace: '#\1' 100 | loop: 101 | - '^(\s*SystemMaxUse\s*=.*)' 102 | - '^(\s*SystemKeepFree\s*=.*)' 103 | - '^(\s*RuntimeMaxUse\s*=)' 104 | - '^(\s*RuntimeKeepFree\s*=.*)' 105 | - '^(\s*MaxFileSec\s*=.*)' 106 | 107 | - name: "6.2.1.1.4 | PATCH | Ensure journald ForwardToSyslog is disabled" 108 | when: deb12cis_rule_6_2_1_1_4 109 | tags: 110 | - level1-server 111 | - level1-workstation 112 | - patch 113 | - journald 114 | - syslog 115 | - rule_6.2.1.1.4 116 | - NIST800-53R5_AU-2 117 | - NIST800-53R5_AU-7 118 | - NIST800-53R5_AU-12 119 | block: 120 | - name: "6.2.1.1.4 | PATCH | Ensure journald ForwardToSyslog is disabled | Add file" 121 | ansible.builtin.template: 122 | src: etc/systemd/journald.conf.d/forwardtosyslog.conf.j2 123 | dest: /etc/systemd/journald.conf.d/forwardtosyslog.conf 124 | owner: root 125 | group: root 126 | mode: 'u-x,g-wx,o-rwx' 127 | 128 | - name: "6.2.1.1.4 | PATCH | Ensure journald ForwardToSyslog is disabled | comment out current entries" 129 | ansible.builtin.replace: 130 | path: /etc/systemd/journald.conf 131 | regexp: ^(\s*ForwardToSyslog) 132 | replace: '#\1' 133 | 134 | - name: "6.2.1.1.5 | PATCH | Ensure journald Storage is configured" 135 | when: deb12cis_rule_6_2_1_1_5 136 | tags: 137 | - level1-server 138 | - level1-workstation 139 | - patch 140 | - journald 141 | - rule_6.2.1.1.5 142 | - NIST800-53R5_AU-3 143 | - NIST800-53R5_AU-12 144 | notify: Restart journald 145 | block: 146 | - name: "6.2.1.1.5 | PATCH | Ensure journald Storage is configured | Add file" 147 | ansible.builtin.template: 148 | src: etc/systemd/journald.conf.d/storage.conf.j2 149 | dest: /etc/systemd/journald.conf.d/storage.conf 150 | owner: root 151 | group: root 152 | mode: 'u-x,g-wx,o-rwx' 153 | 154 | - name: "6.2.1.1.5 | PATCH | Ensure journald Storage is configured | comment out current entries" 155 | ansible.builtin.replace: 156 | path: /etc/systemd/journald.conf 157 | regexp: (?i)^(\s*storage=) 158 | replace: '#\1' 159 | 160 | - name: "6.2.1.1.6 | PATCH | Ensure journald Compress is configured" 161 | when: deb12cis_rule_6_2_1_1_6 162 | tags: 163 | - level1-server 164 | - level1-workstation 165 | - patch 166 | - journald 167 | - rule_6.2.1.1.6 168 | - NIST800-53R5_AU-4 169 | notify: Restart journald 170 | block: 171 | - name: "6.2.1.1.6 | PATCH | Ensure journald Compress is configured | Add file" 172 | ansible.builtin.template: 173 | src: etc/systemd/journald.conf.d/storage.conf.j2 # Added to the same file as 6.2.1.1.4 174 | dest: /etc/systemd/journald.conf.d/storage.conf 175 | owner: root 176 | group: root 177 | mode: 'u-x,g-wx,o-rwx' 178 | 179 | - name: "6.2.1.1.6 | PATCH | Ensure journald Compress is configured | comment out current entries" 180 | ansible.builtin.replace: 181 | path: /etc/systemd/journald.conf 182 | regexp: (?i)^(\s*compress=) 183 | replace: '#\1' 184 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.2.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.2.1.2.1 | PATCH | Ensure systemd-journal-remote is installed" 4 | when: 5 | - deb12cis_rule_6_2_1_2_1 6 | - not deb12cis_system_is_log_server 7 | tags: 8 | - level1-server 9 | - level1-workstation 10 | - patch 11 | - journald 12 | - rule_6.2.1.2.1 13 | - NIST800-53R5_AU-2 14 | - NIST800-53R5_AU-7 15 | - NIST800-53R5_AU-12 16 | ansible.builtin.package: 17 | name: systemd-journal-remote 18 | state: present 19 | 20 | - name: "6.2.1.2.2 | PATCH | Ensure systemd-journal-remote authentication is configured" 21 | when: 22 | - deb12cis_rule_6_2_1_2_2 23 | - not deb12cis_system_is_log_server 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - patch 28 | - journald 29 | - rule_6.2.1.2.2 30 | - NIST800-53R5_AU-2 31 | - NIST800-53R5_AU-12 32 | notify: Restart journald 33 | ansible.builtin.lineinfile: 34 | path: /etc/systemd/journal-upload.conf 35 | regexp: "{{ item.regexp }}" 36 | line: "{{ item.line }}" 37 | loop: 38 | - { regexp: 'URL=', line: 'URL={{ deb12cis_journal_upload_url }}'} 39 | - { regexp: 'ServerKeyFile=', line: 'ServerKeyFile={{ deb12cis_journal_upload_serverkeyfile }}'} 40 | - { regexp: 'ServerCertificateFile=', line: 'ServerCertificateFile={{ deb12cis_journal_servercertificatefile }}'} 41 | - { regexp: 'TrustedCertificateFile=', line: 'TrustedCertificateFile={{ deb12cis_journal_trustedcertificatefile }}'} 42 | 43 | - name: "6.2.1.2.3 | PATCH | Ensure systemd-journal-upload is enabled and active" 44 | when: 45 | - not deb12cis_system_is_log_server 46 | - deb12cis_rule_6_2_1_2_3 47 | tags: 48 | - level1-server 49 | - level1-workstation 50 | - patch 51 | - journald 52 | - rule_6.2.1.2.3 53 | - NIST800-53R5_AU-2 54 | - NIST800-53R5_AU-12 55 | ansible.builtin.systemd: 56 | name: systemd-journal-upload 57 | masked: false 58 | enabled: true 59 | 60 | - name: "6.2.1.2.4 | PATCH | Ensure systemd-journal-remote service is not in use" 61 | when: 62 | - not deb12cis_system_is_log_server 63 | - deb12cis_rule_6_2_1_2_4 64 | tags: 65 | - level1-server 66 | - level1-workstation 67 | - patch 68 | - journald 69 | - rule_6.2.1.2.4 70 | - NIST800-53R5_AU-2 71 | - NIST800-53R5_AU-7 72 | - NIST800-53R5_AU-12 73 | ansible.builtin.systemd: 74 | name: "{{ item }}" 75 | state: stopped 76 | enabled: false 77 | masked: true 78 | loop: 79 | - systemd-journal-remote.socket 80 | - systemd-journal-remote.service 81 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.2.2.1 | PATCH | Ensure access to all logfiles has been configured" 4 | when: deb12cis_rule_6_2_2_1 5 | tags: 6 | - level1-server 7 | - level1-workstation 8 | - patch 9 | - logfiles 10 | - rule_6.2.2.1 11 | - NIST800-53R5_AC-3 12 | - NIST800-53R5_MP-2 13 | block: 14 | - name: "6.2.2.1 | AUDIT | Ensure access to all logfiles has been configured | find log files" 15 | ansible.builtin.shell: find /var/log/ -type f -exec ls {} \; 16 | changed_when: false 17 | failed_when: false 18 | register: discovered_logfiles 19 | 20 | - name: "6.2.2.1 | PATCH | Ensure access to all logfiles has been configured | change permissions SSSD min 660" 21 | when: 22 | - discovered_logfiles.stdout_lines | length > 0 23 | - item is match("/var/log/(gdm|sssd)") 24 | ansible.builtin.file: 25 | path: "{{ item }}" 26 | mode: 'ug-x,o-rwx' 27 | failed_when: discovered_logfile_list.state not in '[ file, absent ]' 28 | register: discovered_logfile_list 29 | loop: "{{ discovered_logfiles.stdout_lines }}" 30 | 31 | - name: "6.2.2.1 | PATCH | Ensure access to all logfiles has been configured | change permissions tmp min 664" 32 | when: 33 | - discovered_logfiles.stdout_lines | length > 0 34 | - item is match("/var/log/((u|b|w)tmp*|lastlog)") 35 | ansible.builtin.file: 36 | path: "{{ item }}" 37 | mode: 'ug-x,o-wx' 38 | failed_when: discovered_logfile_list.state not in '[ file, absent ]' 39 | register: discovered_logfile_list 40 | loop: "{{ discovered_logfiles.stdout_lines }}" 41 | 42 | - name: "6.2.2.1 | PATCH | Ensure access to all logfiles has been configured | change permissions else all 640" 43 | when: 44 | - discovered_logfiles.stdout_lines | length > 0 45 | - item is not match("/var/log/((u|b|w)tmp*|lastlog|sssd)") 46 | ansible.builtin.file: 47 | path: "{{ item }}" 48 | mode: 'u-x,g-wx,o-rwx' 49 | failed_when: discovered_logfile_list.state not in '[ file, absent ]' 50 | register: discovered_logfile_list 51 | loop: "{{ discovered_logfiles.stdout_lines }}" 52 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.3.1.1 | PATCH | Ensure auditd is installed" 4 | when: deb12cis_rule_6_3_1_1 5 | tags: 6 | - level2-server 7 | - level2-workstation 8 | - patch 9 | - auditd 10 | - rule_6.3.1.1 11 | - NIST800-53R5_AU-2 12 | - NIST800-53R5_AU-3 13 | - NIST800-53R5_AU-12 14 | - NIST800-53R5_SI-5 15 | block: 16 | - name: "6.3.1.1 | PATCH | Ensure auditd is installed | Install auditd packages" 17 | when: '"auditd" not in ansible_facts.packages' 18 | ansible.builtin.package: 19 | name: auditd 20 | state: present 21 | 22 | - name: "6.3.1.1 | PATCH | Ensure auditd is installed | Install audispd-plugins packages" 23 | when: '"audispd-plugins" not in ansible_facts.packages' 24 | ansible.builtin.package: 25 | name: audispd-plugins 26 | state: present 27 | 28 | - name: "6.3.1.2 | PATCH | Ensure auditd service is enabled and active" 29 | when: deb12cis_rule_6_3_1_2 30 | tags: 31 | - level2-server 32 | - level2-workstation 33 | - patch 34 | - auditd 35 | - rule_6.3.1.2 36 | - NIST800-53R5_AU-2 37 | - NIST800-53R5_AU-12 38 | - NIST800-53R5_SI-5 39 | ansible.builtin.systemd: 40 | name: auditd 41 | state: started 42 | enabled: true 43 | 44 | - name: "6.3.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled" 45 | when: deb12cis_rule_6_3_1_3 46 | tags: 47 | - level2-server 48 | - level2-workstation 49 | - patch 50 | - auditd 51 | - grub 52 | - rule_6.3.1.3 53 | ansible.builtin.lineinfile: 54 | path: /etc/default/grub 55 | regexp: '^GRUB_CMDLINE_LINUX_DEFAULT="((?:(?!audit=\d).)*?)"$' 56 | line: GRUB_CMDLINE_LINUX_DEFAULT="\1 audit=1" 57 | backrefs: true 58 | notify: Update-grub 59 | 60 | - name: "6.3.1.4 | PATCH | Ensure audit_backlog_limit is sufficient" 61 | when: deb12cis_rule_6_3_1_4 62 | tags: 63 | - level2-server 64 | - level2-workstation 65 | - patch 66 | - auditd 67 | - grub 68 | - rule_6.3.1.4 69 | - NIST800-53R5_AU-2 70 | - NIST800-53R5_AU-3 71 | - NIST800-53R5_AU-12 72 | ansible.builtin.lineinfile: 73 | path: /etc/default/grub 74 | regexp: '^GRUB_CMDLINE_LINUX_DEFAULT="((?:(?!audit_backlog_limit=\d+\b).)*?)"$' 75 | line: GRUB_CMDLINE_LINUX_DEFAULT="\1 audit_backlog_limit={{ deb12cis_audit_back_log_limit }}" 76 | backrefs: true 77 | notify: Update-grub 78 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "6.3.2.1 | PATCH | Ensure audit log storage size is configured" 4 | when: deb12cis_rule_6_3_2_1 5 | tags: 6 | - level2-server 7 | - level2-workstation 8 | - patch 9 | - auditd 10 | - rule_6.3.2.1 11 | - NIST800-53R5_AU-8 12 | ansible.builtin.lineinfile: 13 | path: /etc/audit/auditd.conf 14 | regexp: "^max_log_file( |=)" 15 | line: "max_log_file = {{ deb12cis_auditd_max_log_file_size }}" 16 | notify: Restart auditd 17 | 18 | - name: "6.3.2.2 | PATCH | Ensure audit logs are not automatically deleted" 19 | when: deb12cis_rule_6_3_2_2 20 | tags: 21 | - level2-server 22 | - level2-workstation 23 | - patch 24 | - auditd 25 | - rule_6.3.2.2 26 | - NIST800-53R5_AU-8 27 | ansible.builtin.lineinfile: 28 | path: /etc/audit/auditd.conf 29 | regexp: "^max_log_file_action" 30 | line: "max_log_file_action = {{ deb12cis_auditd_max_log_file_action }}" 31 | notify: Restart auditd 32 | 33 | - name: "6.3.2.3 | PATCH | Ensure system is disabled when audit logs are full" 34 | when: deb12cis_rule_6_3_2_3 35 | tags: 36 | - level2-server 37 | - level2-workstation 38 | - patch 39 | - auditd 40 | - rule_6.3.2.3 41 | - NIST800-53R5_AU-2 42 | - NIST800-53R5_AU-8 43 | - NIST800-53R5_AU-12 44 | - NIST800-53R5_SI-5 45 | ansible.builtin.lineinfile: 46 | path: /etc/audit/auditd.conf 47 | regexp: "{{ item.regexp }}" 48 | line: "{{ item.line }}" 49 | notify: Restart auditd 50 | loop: 51 | - { regexp: '^disk_full_action', line: 'disk_full_action = {{ deb12cis_auditd_disk_full_action }}' } 52 | - { regexp: '^disk_error_action', line: 'disk_error_action = {{ deb12cis_auditd_disk_error_action }}' } 53 | 54 | - name: "6.3.2.4 | PATCH | Ensure system warns when audit logs are low on space" 55 | when: deb12cis_rule_6_3_2_4 56 | tags: 57 | - level2-server 58 | - level2-workstation 59 | - patch 60 | - auditd 61 | - rule_6.3.2.4 62 | - NIST800-53R5_AU-2 63 | - NIST800-53R5_AU-8 64 | - NIST800-53R5_AU-12 65 | - NIST800-53R5_SI-5 66 | ansible.builtin.lineinfile: 67 | path: /etc/audit/auditd.conf 68 | regexp: "{{ item.regexp }}" 69 | line: "{{ item.line }}" 70 | notify: Restart auditd 71 | loop: 72 | - { regexp: '^space_left_action', line: 'space_left_action = {{ deb12cis_auditd_space_left_action }}' } 73 | - { regexp: '^admin_space_left_action', line: 'admin_space_left_action = {{ deb12cis_auditd_admin_space_left_action }}' } 74 | 75 | - name: "PATCH | Configure other keys for auditd.conf" 76 | when: 77 | - deb12cis_auditd_extra_conf.keys() | length > 0 78 | - deb12cis_auditd_extra_conf_usage 79 | tags: 80 | - level2-server 81 | - level2-workstation 82 | - patch 83 | - auditd 84 | - NIST800-53R5_AU-2 85 | - NIST800-53R5_AU-8 86 | - NIST800-53R5_AU-12 87 | - NIST800-53R5_SI-5 88 | ansible.builtin.lineinfile: 89 | path: /etc/audit/auditd.conf 90 | regexp: "^{{ item }}( |=)" 91 | line: "{{ item }} = {{ deb12cis_auditd_extra_conf[item] }}" 92 | loop: "{{ deb12cis_auditd_extra_conf.keys() }}" 93 | notify: Restart auditd 94 | -------------------------------------------------------------------------------- /tasks/section_6/cis_6.3.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: | 4 | "6.3.4.1 | PATCH | Ensure audit log files mode is configured" 5 | "6.3.4.2 | PATCH | Ensure only authorized users own audit log files" 6 | "6.3.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" 7 | when: 8 | - deb12cis_rule_6_3_4_1 or 9 | deb12cis_rule_6_3_4_2 or 10 | deb12cis_rule_6_3_4_3 11 | tags: 12 | - level2-server 13 | - level2-workstation 14 | - patch 15 | - auditd 16 | - rule_6.3.4.1 17 | - rule_6.3.4.2 18 | - rule_6.3.4.3 19 | - NIST800-53R5_AU-3 20 | ansible.builtin.file: 21 | path: "{{ prelim_auditd_logfile.stdout }}" 22 | mode: 'o-x,g-wx,o-rwx' 23 | owner: root 24 | group: root 25 | 26 | - name: "6.3.4.4 | PATCH | Ensure the audit log file directory mode is configured" 27 | when: deb12cis_rule_6_3_4_4 28 | tags: 29 | - level2-server 30 | - level2-workstation 31 | - patch 32 | - auditd 33 | - rule_6.3.4.4 34 | - NIST800-53R5_AU-3 35 | ansible.builtin.file: 36 | path: "{{ prelim_auditd_logfile.stdout | dirname }}" 37 | state: directory 38 | mode: 'g-w,o-rwx' 39 | 40 | - name: "6.3.4.5 | PATCH | Ensure audit configuration files mode is configured" 41 | when: deb12cis_rule_6_3_4_5 42 | tags: 43 | - level2-server 44 | - level2-workstation 45 | - patch 46 | - auditd 47 | - rule_6.3.4.5 48 | - NIST800-53R5_AU-3 49 | ansible.builtin.file: 50 | path: "{{ item.path }}" 51 | mode: 'u-x,g-wx,o-rwx' 52 | failed_when: discovered_auditd_file_list.state not in '[ file, absent ]' 53 | register: discovered_auditd_file_list 54 | loop: "{{ prelim_auditd_conf_files.files }}" 55 | loop_control: 56 | label: "{{ item.path }}" 57 | 58 | - name: "6.3.4.6 | PATCH | Ensure audit configuration files are owned by root" 59 | when: deb12cis_rule_6_3_4_6 60 | tags: 61 | - level2-server 62 | - level2-workstation 63 | - patch 64 | - auditd 65 | - rule_6.3.4.6 66 | - NIST800-53R5_AU-3 67 | ansible.builtin.file: 68 | path: "{{ item.path }}" 69 | owner: root 70 | loop: "{{ prelim_auditd_conf_files.files | default([]) }}" 71 | loop_control: 72 | label: "{{ item.path }}" 73 | 74 | - name: "6.3.4.7 | PATCH | Ensure audit configuration files belong to group root" 75 | when: deb12cis_rule_6_3_4_7 76 | tags: 77 | - level2-server 78 | - level2-workstation 79 | - patch 80 | - auditd 81 | - rule_6.3.4.7 82 | - NIST800-53R5_AU-3 83 | ansible.builtin.file: 84 | path: "{{ item.path }}" 85 | group: root 86 | loop: "{{ prelim_auditd_conf_files.files | default([]) }}" 87 | loop_control: 88 | label: "{{ item.path }}" 89 | 90 | - name: "6.3.4.8 | PATCH | Ensure audit tools mode is configured" 91 | when: deb12cis_rule_6_3_4_8 92 | tags: 93 | - level2-server 94 | - level2-workstation 95 | - patch 96 | - auditd 97 | - rule_6.3.4.8 98 | - NIST800-53R5_AU-3 99 | ansible.builtin.file: 100 | path: "{{ item }}" 101 | mode: 'go-w' 102 | loop: "{{ audit_bins }}" 103 | 104 | - name: "6.3.4.9 | PATCH | Ensure audit tools are owned by root" 105 | when: deb12cis_rule_6_3_4_9 106 | tags: 107 | - level2-server 108 | - level2-workstation 109 | - patch 110 | - auditd 111 | - rule_6.3.4.9 112 | ansible.builtin.file: 113 | path: "{{ item }}" 114 | owner: root 115 | group: root 116 | loop: "{{ audit_bins }}" 117 | 118 | - name: "6.3.4.10 | PATCH | Ensure audit tools belong to group root" 119 | when: deb12cis_rule_6_3_4_10 120 | tags: 121 | - level2-server 122 | - level2-workstation 123 | - patch 124 | - auditd 125 | - rule_6.3.4.10 126 | - NIST800-53R5_AU-3 127 | ansible.builtin.file: 128 | path: "{{ item }}" 129 | group: root 130 | loop: "{{ audit_bins }}" 131 | -------------------------------------------------------------------------------- /tasks/section_6/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 6.1 | Configure Integrity Checking" 4 | ansible.builtin.import_tasks: 5 | file: cis_6.1.x.yml 6 | 7 | - name: "SECTION | 6.2.1.1.x | Configure systemd-journald service" 8 | ansible.builtin.import_tasks: 9 | file: cis_6.2.1.1.x.yml 10 | 11 | - name: "SECTION | 6.2.1.2.x | Configure journald-remote" 12 | when: deb12cis_syslog == 'journald' 13 | ansible.builtin.import_tasks: 14 | file: cis_6.2.1.2.x.yml 15 | 16 | - name: "SECTION | 6.2. | Configure Logfiles" 17 | ansible.builtin.import_tasks: 18 | file: cis_6.2.2.x.yml 19 | 20 | - name: "SECTION | 6.3.1 | Configure auditd Service" 21 | ansible.builtin.import_tasks: 22 | file: cis_6.3.1.x.yml 23 | 24 | - name: "SECTION | 6.3.2 | Configure Data Retention" 25 | ansible.builtin.import_tasks: 26 | file: cis_6.3.2.x.yml 27 | 28 | - name: "SECTION | 6.3.3 | Configure auditd Rules" 29 | ansible.builtin.import_tasks: 30 | file: cis_6.3.3.x.yml 31 | 32 | - name: "SECTION | 6.3.4 | Configure auditd File Access" 33 | ansible.builtin.import_tasks: 34 | file: cis_6.3.4.x.yml 35 | -------------------------------------------------------------------------------- /tasks/section_7/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 7.1 | System File Permissions" 4 | ansible.builtin.import_tasks: 5 | file: cis_7.1.x.yml 6 | 7 | - name: "SECTION | 7.2 | Local User and Group Settings" 8 | ansible.builtin.import_tasks: 9 | file: cis_7.2.x.yml 10 | -------------------------------------------------------------------------------- /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 }} | AUDIT | Set fact for manual task warning." # noqa name[template] 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/98_auditd_exception.rules.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | ### YOUR CHANGES WILL BE LOST! 5 | 6 | # This file contains users whose actions are not logged by auditd 7 | {% if deb12cis_allow_auditd_uid_user_exclusions %} 8 | {% for user in deb12cis_auditd_uid_exclude %} 9 | -a never,user -F uid!={{ user }} -F auid!={{ user }} 10 | {% endfor %} 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /templates/audit/99_auditd.rules.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | ### YOUR CHANGES WILL BE LOST! 5 | 6 | 7 | # This template will set all of the auditd configurations via a handler in the role in one task instead of individually 8 | {% if deb12cis_rule_6_3_3_1 %} 9 | -w /etc/sudoers -p wa -k scope 10 | -w /etc/sudoers.d -p wa -k scope 11 | {% endif %} 12 | {% if deb12cis_rule_6_3_3_2 %} 13 | {% set syscalls = ["execve"] %} 14 | {% set arch_syscalls = [] %} 15 | {% for syscall in syscalls %} 16 | {% if syscall in supported_syscalls %} 17 | {{ arch_syscalls.append( syscall) }} 18 | {% endif %} 19 | {% endfor %} 20 | -a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S {{ arch_syscalls|join(',') }} -k user_emulation 21 | -a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S {{ arch_syscalls|join(',') }} -k user_emulation 22 | {% endif %} 23 | {% if deb12cis_rule_6_3_3_3 %} 24 | -w {{ deb12cis_sudolog_location }} -p wa -k sudo_log_file 25 | {% endif %} 26 | {% if deb12cis_rule_6_3_3_4 %} 27 | {% set syscalls = ["adjtimex","settimeofday","clock_settime"] %} 28 | {% set arch_syscalls = [] %} 29 | {% for syscall in syscalls %} 30 | {% if syscall in supported_syscalls %} 31 | {{ arch_syscalls.append( syscall) }} 32 | {% endif %} 33 | {% endfor %} 34 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -k time-change 35 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -k time-change 36 | -w /etc/localtime -p wa -k time-change 37 | {% endif %} 38 | {% if deb12cis_rule_6_3_3_5 %} 39 | {% set syscalls = ["sethostname","setdomainname"] %} 40 | {% set arch_syscalls = [] %} 41 | {% for syscall in syscalls %} 42 | {% if syscall in supported_syscalls %} 43 | {{ arch_syscalls.append( syscall) }} 44 | {% endif %} 45 | {% endfor %} 46 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -k system-locale 47 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -k system-locale 48 | -w /etc/issue -p wa -k system-locale 49 | -w /etc/issue.net -p wa -k system-locale 50 | -w /etc/hosts -p wa -k system-locale 51 | -w /etc/networks -p wa -k system-locale 52 | -w /etc/network -p wa -k system-locale 53 | -w /etc/netplan -p wa -k system-locale 54 | {% endif %} 55 | {% if deb12cis_rule_6_3_3_6 %} 56 | {% if discovered_privilege_processes is defined %} 57 | {% for proc in discovered_privilege_processes.stdout_lines -%} 58 | -a always,exit -F path={{ proc }} -F perm=x -F auid>=1000 -F auid!=unset -k privileged 59 | {% endfor %} 60 | {% endif %} 61 | {% endif %} 62 | {% if deb12cis_rule_6_3_3_7 %} 63 | {% set syscalls = ["creat","open","openat","truncate","ftruncate"] %} 64 | {% set arch_syscalls = [] %} 65 | {% for syscall in syscalls %} 66 | {% if syscall in supported_syscalls %} 67 | {{ arch_syscalls.append( syscall) }} 68 | {% endif %} 69 | {% endfor %} 70 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access 71 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access 72 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access 73 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access 74 | {% endif %} 75 | {% if deb12cis_rule_6_3_3_8 %} 76 | -w /etc/group -p wa -k identity 77 | -w /etc/passwd -p wa -k identity 78 | -w /etc/gshadow -p wa -k identity 79 | -w /etc/shadow -p wa -k identity 80 | -w /etc/security/opasswd -p wa -k identity 81 | -w /etc/nsswitch.conf -p wa -k identity 82 | -w /etc/pam.conf -p wa -k identity 83 | -w /etc/pam.d -p wa -k identity 84 | {% endif %} 85 | {% if deb12cis_rule_6_3_3_9 %} 86 | {% set syscalls = ["chmod","fchmod","fchmodat"] %} 87 | {% set arch_syscalls = [] %} 88 | {% for syscall in syscalls %} 89 | {% if syscall in supported_syscalls %} 90 | {{ arch_syscalls.append( syscall) }} 91 | {% endif %} 92 | {% endfor %} 93 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 94 | {% set syscalls = ["chown","fchown","lchown","fchownat"] %} 95 | {% set arch_syscalls = [] %} 96 | {% for syscall in syscalls %} 97 | {% if syscall in supported_syscalls %} 98 | {{ arch_syscalls.append( syscall) }} 99 | {% endif %} 100 | {% endfor %} 101 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 102 | {% set syscalls = ["etxattr","lsetxattr","fsetxattr","removexattr","lremovexattr","fremovexattr"] %} 103 | {% set arch_syscalls = [] %} 104 | {% for syscall in syscalls %} 105 | {% if syscall in supported_syscalls %} 106 | {{ arch_syscalls.append( syscall) }} 107 | {% endif %} 108 | {% endfor %} 109 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 110 | {% set syscalls = ["chmod","fchmod","fchmodat"] %} 111 | {% set arch_syscalls = [] %} 112 | {% for syscall in syscalls %} 113 | {% if syscall in supported_syscalls %} 114 | {{ arch_syscalls.append( syscall) }} 115 | {% endif %} 116 | {% endfor %} 117 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 118 | {% set syscalls = ["chown","fchown","lchown","fchownat"] %} 119 | {% set arch_syscalls = [] %} 120 | {% for syscall in syscalls %} 121 | {% if syscall in supported_syscalls %} 122 | {{ arch_syscalls.append( syscall) }} 123 | {% endif %} 124 | {% endfor %} 125 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 126 | {% set syscalls = ["setxattr","lsetxattr","fsetxattr","removexattr","lremovexattr","fremovexattr"] %} 127 | {% set arch_syscalls = [] %} 128 | {% for syscall in syscalls %} 129 | {% if syscall in supported_syscalls %} 130 | {{ arch_syscalls.append( syscall) }} 131 | {% endif %} 132 | {% endfor %} 133 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k perm_mod 134 | {% endif %} 135 | {% if deb12cis_rule_6_3_3_10 %} 136 | {% set syscalls = ["mount"] %} 137 | {% set arch_syscalls = [] %} 138 | {% for syscall in syscalls %} 139 | {% if syscall in supported_syscalls %} 140 | {{ arch_syscalls.append( syscall) }} 141 | {% endif %} 142 | {% endfor %} 143 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k mounts 144 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k mounts 145 | {% endif %} 146 | {% if deb12cis_rule_6_3_3_11 %} 147 | -w /var/run/utmp -p wa -k session 148 | -w /var/log/wtmp -p wa -k session 149 | -w /var/log/btmp -p wa -k session 150 | {% endif %} 151 | {% if deb12cis_rule_6_3_3_12 %} 152 | -w /var/log/lastlog -p wa -k logins 153 | -w /var/run/faillock -p wa -k logins 154 | {% endif %} 155 | {% if deb12cis_rule_6_3_3_13 %} 156 | {% set syscalls = ["unlink","unlinkat","rename","renameat"] %} 157 | {% set arch_syscalls = [] %} 158 | {% for syscall in syscalls %} 159 | {% if syscall in supported_syscalls %} 160 | {{ arch_syscalls.append( syscall) }} 161 | {% endif %} 162 | {% endfor %} 163 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k delete 164 | -a always,exit -F arch=b32 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=unset -k delete 165 | {% endif %} 166 | {% if deb12cis_rule_6_3_3_14 %} 167 | -w /etc/apparmor/ -p wa -k MAC-policy 168 | -w /etc/apparmor.d/ -p wa -k MAC-policy 169 | {% endif %} 170 | {% if deb12cis_rule_6_3_3_15 %} 171 | -a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>={{ prelim_min_int_uid }} -F auid!=unset -k perm_chng 172 | {% endif %} 173 | {% if deb12cis_rule_6_3_3_16 %} 174 | -a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>={{ prelim_min_int_uid }} -F auid!=unset -k perm_chng 175 | {% endif %} 176 | {% if deb12cis_rule_6_3_3_17 %} 177 | -a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>={{ prelim_min_int_uid }} -F auid!=unset -k priv_chng 178 | {% endif %} 179 | {% if deb12cis_rule_6_3_3_18 %} 180 | -a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>={{ prelim_min_int_uid }} -F auid!=unset -k usermod 181 | {% endif %} 182 | {% if deb12cis_rule_6_3_3_19 %} 183 | -a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=-1 -k kernel_modules 184 | {% set syscalls = ["init_module","finit_module","delete_module"] %} 185 | {% set arch_syscalls = [] %} 186 | {% for syscall in syscalls %} 187 | {% if syscall in supported_syscalls %} 188 | {{ arch_syscalls.append( syscall) }} 189 | {% endif %} 190 | {% endfor %} 191 | -a always,exit -F arch=b64 -S {{ arch_syscalls|join(',') }} -F auid>=1000 -F auid!=-1 -k kernel_modules 192 | {% endif %} 193 | {% if deb12cis_rule_6_3_3_20 %} 194 | -e 2 195 | 196 | {% endif %} 197 | -------------------------------------------------------------------------------- /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 = {{ deb12cis_level_1 }} 11 | level_2_hardening_enabled = {{ deb12cis_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/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 deb12cis_time_synchronization_servers -%} 6 | server {{ server }} {{ deb12cis_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/chrony/sources.d/pool.sources.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | {% for pool in deb12cis_time_pool %} 6 | pool {{ pool.name }} {{ pool.options }} 7 | {% endfor %} 8 | -------------------------------------------------------------------------------- /templates/etc/chrony/sources.d/server.sources.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | {% for server in deb12cis_time_servers %} 6 | server {{ server.name }} {{ server.options }} 7 | {% endfor %} 8 | -------------------------------------------------------------------------------- /templates/etc/cron.d/aide.cron.j2: -------------------------------------------------------------------------------- 1 | # Run AIDE integrity check 2 | ## Ansible controlled file 3 | # Added as part of ansible-lockdown CIS baseline 4 | # provided by Mindpoint Group - A Tyto Athene Company 5 | ### YOUR CHANGES WILL BE LOST! 6 | # CIS 1.3.2 7 | 8 | {{ deb12cis_aide_cron['aide_minute'] }} {{ deb12cis_aide_cron['aide_hour'] }} {{ deb12cis_aide_cron['aide_month'] }} {{ deb12cis_aide_cron['aide_weekday'] }} {{ deb12cis_aide_cron['aide_job'] }} 9 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-automount_lock.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | # Lock desktop media-handling automount setting 6 | /org/gnome/desktop/media-handling/automount 7 | 8 | # Lock desktop media-handling automount-open 9 | /org/gnome/desktop/media-handling/automount-open 10 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-autorun_lock.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | # Lock desktop media-handling settings 6 | /org/gnome/desktop/media-handling/autorun-never 7 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-media-automount.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | [org/gnome/desktop/media-handling] 6 | automount=false 7 | automount-open=false 8 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-media-autorun.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | [org/gnome/desktop/media-handling] 6 | autorun-never=true 7 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-screensaver.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | # Specify the dconf path 6 | [org/gnome/desktop/session] 7 | 8 | # Number of seconds of inactivity before the screen goes blank 9 | # Set to 0 seconds if you want to deactivate the screensaver. 10 | idle-delay=uint32 {{ deb12cis_screensaver_idle_delay }} 11 | 12 | # Specify the dconf path 13 | [org/gnome/desktop/screensaver] 14 | 15 | # Number of seconds after the screen is blank before locking the screen 16 | lock-delay=uint32 {{ deb12cis_screensaver_lock_delay }} 17 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-screensaver_lock.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | # Lock desktop screensaver idle-delay setting 6 | /org/gnome/desktop/session/idle-delay 7 | 8 | # Lock desktop screensaver lock-delay setting 9 | /org/gnome/desktop/screensaver/lock-delay 10 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/gdm.d/01-banner-message.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | [org/gnome/login-screen] 6 | banner-message-enable=true 7 | banner-message-text="{{ deb12cis_warning_banner }}" 8 | -------------------------------------------------------------------------------- /templates/etc/grub.d/00_user.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of ansible-lockdown CIS baseline 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | cat < /dev/null; then # Check if the module is currently loaded 22 | l_output2+=(" - Kernel module: \"$l_mod_name\" is loaded" "") 23 | fi 24 | } 25 | while IFS= read -r -d $'\0' l_module_dir; do 26 | a_available_modules+=("$(basename "$l_module_dir")") 27 | done < <(find "$(readlink -f /lib/modules/"$(uname -r)"/kernel/fs)" -mindepth 1 -maxdepth 1 -type d ! -empty -print0) 28 | while IFS= read -r l_exclude; do 29 | if grep -Pq -- "\b$l_exclude\b" <<< "${a_cve_exists[*]}"; then 30 | a_output2+=(" - ** WARNING: kernel module: \"$l_exclude\" has a CVE and is currently mounted! **") 31 | elif 32 | grep -Pq -- "\b$l_exclude\b" <<< "${a_available_modules[*]}"; then 33 | a_output+=(" - Kernel module: \"$l_exclude\" is currently mounted - do NOT unload or disable") 34 | fi 35 | ! grep -Pq -- "\b$l_exclude\b" <<< "${a_ignore[*]}" && a_ignore+=("$l_exclude") 36 | done < <(findmnt -knD | awk '{print $2}' | sort -u) 37 | while IFS= read -r l_config; do 38 | a_modprope_config+=("$l_config") 39 | done < <(modprobe --showconfig | grep -P '^\h*(blacklist|install)') 40 | for l_mod_name in "${a_available_modules[@]}"; do # Iterate over all filesystem modules 41 | [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}" 42 | if grep -Pq -- "\b$l_mod_name\b" <<< "${a_ignore[*]}"; then 43 | a_excluded+=(" - Kernel module: \"$l_mod_name\"") 44 | else 45 | f_module_chk 46 | fi 47 | done 48 | # Output findings 49 | 50 | echo "### Script can be found at ${BASH_SOURCE} ##" 51 | if [ "${#a_output2[@]}" -le 0 ]; then 52 | printf '%s\n' "" " - No unused filesystem kernel modules are enabled" "${a_output[@]}" "" 53 | else 54 | printf '%s\n' "" "-- Audit Result: --" " ** REVIEW the following **" "${a_output2[@]}" 55 | # Changed return value to capture error 56 | exit 99 57 | #[ "${#a_output[@]}" -gt 0 ] && printf '%s\n' "" "-- Correctly set: --" "${a_output[@]}" "" 58 | fi 59 | } 60 | {% endraw %} 61 | -------------------------------------------------------------------------------- /templates/usr/share/pam-configs/faillock.j2: -------------------------------------------------------------------------------- 1 | Name: Enable pam_faillock to deny access 2 | Default: yes 3 | Priority: 0 4 | Auth-Type: Primary 5 | Auth: 6 | [default=die] pam_faillock.so authfail 7 | -------------------------------------------------------------------------------- /templates/usr/share/pam-configs/faillock_notify.j2: -------------------------------------------------------------------------------- 1 | Name: Notify of failed login attempts and reset count upon success 2 | Default: yes 3 | Priority: 1024 4 | Auth-Type: Primary 5 | Auth: 6 | requisite pam_faillock.so preauth 7 | Account-Type: Primary 8 | Account: 9 | required pam_faillock.so 10 | -------------------------------------------------------------------------------- /templates/usr/share/pam-configs/pam_unix.j2: -------------------------------------------------------------------------------- 1 | Name: Unix authentication 2 | Default: yes 3 | Priority: 256 4 | Auth-Type: Primary 5 | Auth: 6 | [success=end default=ignore] pam_unix.so try_first_pass 7 | Auth-Initial: 8 | [success=end default=ignore] pam_unix.so 9 | Account-Type: Primary 10 | Account: 11 | [success=end new_authtok_reqd=done default=ignore] pam_unix.so 12 | Account-Initial: 13 | [success=end new_authtok_reqd=done default=ignore] pam_unix.so 14 | Session-Type: Additional 15 | Session: 16 | required pam_unix.so 17 | Session-Initial: 18 | required pam_unix.so 19 | Password-Type: Primary 20 | Password: 21 | [success=end default=ignore] pam_unix.so obscure{% if deb12cis_rule_5_3_3_4_4 %} use_authtok{% endif %} try_first_pass{% if deb12cis_rule_5_3_3_4_3 %} {{ deb12cis_passwd_hash_algo }}{% endif %} 22 | Password-Initial: 23 | [success=end default=ignore] pam_unix.so obscure{% if deb12cis_rule_5_3_3_4_3 %} {{ deb12cis_passwd_hash_algo }}{% endif %} 24 | -------------------------------------------------------------------------------- /templates/usr/share/pam-configs/pwhistory.j2: -------------------------------------------------------------------------------- 1 | Name: pwhistory password history checking 2 | Default: yes 3 | Priority: 1024 4 | Password-Type: Primary 5 | Password: 6 | requisite pam_pwhistory.so enforce_for_root try_first_pass{% if deb12cis_rule_5_3_3_3_1 %} remember={{ deb12cis_pamd_pwhistory_remember }}{% endif %}{% if deb12cis_rule_5_3_3_3_2 %} enforce_for_root{% endif %}{% if deb12cis_rule_5_3_3_3_3 %} use_authtok{% endif %} 7 | -------------------------------------------------------------------------------- /templates/usr/share/pam-configs/pwquality.j2: -------------------------------------------------------------------------------- 1 | Name: Pwquality password strength checking 2 | Default: yes 3 | Priority: 1024 4 | Conflicts: cracklib 5 | Password-Type: Primary 6 | Password: 7 | requisite pam_pwquality.so retry=3 {# # pragma: allowlist secret #} 8 | Password-Initial: requisite 9 | -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-lockdown/DEBIAN12-CIS/899d3539e5e2062d989705ffe7b3386c42debf73/vars/Debian.yml -------------------------------------------------------------------------------- /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/is_container.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # File to skip controls if container 4 | # Based on standard image no changes 5 | # it expected all pkgs required for the container are alreday installed 6 | 7 | ## controls 8 | 9 | # Firewall 10 | deb12cis_firewall: None 11 | 12 | # SElinux 13 | deb12cis_selinux_disable: true 14 | 15 | ## Related individual rules 16 | # Aide 17 | deb12cis_rule_1_4_1: false 18 | deb12cis_rule_1_4_2: false 19 | 20 | # auditd 21 | deb12cis_rule_4_1_1_1: false 22 | deb12cis_rule_4_1_2_1: false 23 | deb12cis_rule_4_1_2_2: false 24 | deb12cis_rule_4_1_2_3: false 25 | 26 | # time sync 27 | deb12cis_rule_2_2_1_1: false 28 | deb12cis_rule_2_2_1_2: false 29 | 30 | # cron 31 | deb12cis_rule_5_1_1: false 32 | deb12cis_rule_5_1_2: false 33 | deb12cis_rule_5_1_3: false 34 | deb12cis_rule_5_1_4: false 35 | deb12cis_rule_5_1_5: false 36 | deb12cis_rule_5_1_6: false 37 | deb12cis_rule_5_1_7: false 38 | deb12cis_rule_5_1_8: false 39 | 40 | # crypto 41 | deb12cis_rule_1_10: false 42 | 43 | # grub 44 | deb12cis_rule_1_5_1: false 45 | deb12cis_rule_1_5_2: false 46 | deb12cis_rule_1_5_3: false 47 | 48 | ## mounts 49 | # /tmp 50 | deb12cis_rule_1_1_2: false 51 | deb12cis_rule_1_1_3: false 52 | deb12cis_rule_1_1_4: false 53 | deb12cis_rule_1_1_5: false 54 | # /var 55 | deb12cis_rule_1_1_6: false 56 | # /var/tmp 57 | deb12cis_rule_1_1_7: false 58 | deb12cis_rule_1_1_8: false 59 | deb12cis_rule_1_1_9: false 60 | deb12cis_rule_1_1_10: false 61 | # /var/log 62 | deb12cis_rule_1_1_11: false 63 | # /var/log/audit 64 | deb12cis_rule_1_1_12: false 65 | # /home 66 | deb12cis_rule_1_1_13: false 67 | deb12cis_rule_1_1_14: false 68 | # /dev/shm 69 | deb12cis_rule_1_1_15: false 70 | deb12cis_rule_1_1_16: false 71 | deb12cis_rule_1_1_17: false 72 | # usb-storage 73 | deb12cis_rule_1_1_23: false 74 | 75 | # logging 76 | deb12cis_rule_4_2_1_1: false 77 | deb12cis_rule_4_2_1_2: false 78 | deb12cis_rule_4_2_1_3: false 79 | deb12cis_rule_4_2_1_4: false 80 | deb12cis_rule_4_2_1_5: false 81 | deb12cis_rule_4_2_1_6: false 82 | deb12cis_rule_4_2_2_1: false 83 | deb12cis_rule_4_2_2_2: false 84 | deb12cis_rule_4_2_2_3: false 85 | 86 | # systemd 87 | 88 | # Users/passwords/accounts 89 | deb12cis_rule_5_5_2: false 90 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for DEB12-CIS 3 | 4 | min_ansible_version: 2.10.1 5 | deb12cis_allowed_crypto_policies: 6 | - 'DEFAULT' 7 | - 'FUTURE' 8 | - 'FIPS' 9 | 10 | deb12cis_allowed_crypto_policies_modules: 11 | - 'OSPP' 12 | - 'AD-SUPPORT' 13 | - 'AD-SUPPORT-LEGACY' 14 | - 'NO-SHA1' 15 | - 'NO-SSHCBC' 16 | - 'NO-SSHETM' 17 | - 'NO-SSHWEAKCIPHER' 18 | - 'NO-SSHWEAKMAC' 19 | - 'NO-WEAKMAC' 20 | 21 | # Used to control warning summary 22 | warn_control_list: "" 23 | warn_count: 0 24 | 25 | gpg_key_package: "{{ ansible_facts.distribution | lower }}-gpg-keys" 26 | 27 | ## Control 6.3.3.x - Audit template 28 | # This variable governs if the auditd logic should be executed(if value is true). 29 | # NOTE: The current default value is likely to be overriden(via 'set_fact') by other further tasks(in sub-section 'Auditd rules'). 30 | update_audit_template: false 31 | 32 | # Defaults 33 | ## Usage on containerized images 34 | # The role discovers dynamically (in tasks/main.yml) whether it 35 | # is executed on a container image and sets the variable 36 | # system_is_container the true. Otherwise, the default value 37 | # 'false' is left unchanged. 38 | system_is_container: false 39 | # The filename of the existing yml file in role's 'vars/' sub-directory 40 | # to be used for managing the role-behavior when a container was detected: 41 | # (de)activating rules or for other tasks(e.g. disabling Selinux or a specific 42 | # firewall-type). 43 | container_vars_file: is_container.yml 44 | # deb12cis is left off the front of this var for consistency in testing pipeline 45 | # system_is_ec2 toggle will disable tasks that fail on Amazon EC2 instances. Set true to skip and false to run tasks 46 | system_is_ec2: false 47 | 48 | # Aide initiate command for new DB creation 49 | aide_initiate_command: aideinit -y -f 50 | 51 | # Audit vars 52 | audit_bins: 53 | - /sbin/auditctl 54 | - /sbin/aureport 55 | - /sbin/ausearch 56 | - /sbin/autrace 57 | - /sbin/auditd 58 | - /sbin/augenrules 59 | --------------------------------------------------------------------------------