├── .ansible-lint ├── .config └── .secrets.baseline ├── .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 ├── handlers └── main.yml ├── meta └── main.yml ├── site.yml ├── tasks ├── LE_audit_setup.yml ├── audit_only.yml ├── auditd.yml ├── fetch_audit_output.yml ├── main.yml ├── parse_etc_password.yml ├── post_remediation_audit.yml ├── pre_remediation_audit.yml ├── prelim.yml ├── section_1 │ ├── cis_1.1.1.x.yml │ ├── cis_1.1.10.yml │ ├── cis_1.1.2.x.yml │ ├── cis_1.1.3.x.yml │ ├── cis_1.1.4.x.yml │ ├── cis_1.1.5.x.yml │ ├── cis_1.1.6.x.yml │ ├── cis_1.1.7.x.yml │ ├── cis_1.1.8.x.yml │ ├── cis_1.1.9.yml │ ├── cis_1.2.x.yml │ ├── cis_1.3.x.yml │ ├── cis_1.4.x.yml │ ├── cis_1.5.x.yml │ ├── cis_1.6.x.yml │ ├── cis_1.7.x.yml │ ├── cis_1.8.x.yml │ ├── cis_1.9.yml │ └── main.yml ├── section_2 │ ├── cis_2.1.1.x.yml │ ├── cis_2.1.2.x.yml │ ├── cis_2.1.3.x.yml │ ├── cis_2.1.4.x.yml │ ├── cis_2.2.x.yml │ ├── cis_2.3.x.yml │ ├── cis_2.4.yml │ └── main.yml ├── section_3 │ ├── cis_3.1.x.yml │ ├── cis_3.2.x.yml │ ├── cis_3.3.x.yml │ ├── cis_3.5.1.x.yml │ ├── cis_3.5.2.x.yml │ ├── cis_3.5.3.x.yml │ └── main.yml ├── section_4 │ ├── cis_4.1.1.x.yml │ ├── cis_4.1.2.x.yml │ ├── cis_4.1.3.x.yml │ ├── cis_4.1.4.x.yml │ ├── cis_4.2.1.1.x.yml │ ├── cis_4.2.1.x.yml │ ├── cis_4.2.2.x.yml │ ├── cis_4.2.3.yml │ └── main.yml ├── section_5 │ ├── cis_5.1.x.yml │ ├── cis_5.2.x.yml │ ├── cis_5.3.x.yml │ ├── cis_5.4.x.yml │ ├── cis_5.5.x.yml │ └── main.yml ├── section_6 │ ├── cis_6.1.x.yml │ ├── cis_6.2.x.yml │ └── main.yml └── warning_facts.yml ├── templates ├── ansible_vars_goss.yml.j2 ├── audit │ ├── 98_auditd_exception.rules.j2 │ └── 99_auditd.rules.j2 ├── chrony.conf.j2 ├── etc │ ├── ansible │ │ └── compliance_facts.j2 │ ├── chrony │ │ └── sources.d │ │ │ ├── pool.sources.j2 │ │ │ └── server.sources.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 │ ├── grub.d │ │ └── 00_user.j2 │ ├── issue.j2 │ ├── issue.net.j2 │ ├── motd.j2 │ ├── sysctl.d │ │ ├── 60-disable_ipv6.conf.j2 │ │ └── 60-kernel_sysctl.conf.j2 │ └── systemd │ │ ├── system │ │ └── tmp.mount.j2 │ │ └── timesyncd.conf.d │ │ └── 50-timesyncd.conf.j2 └── ntp.conf.j2 └── vars ├── audit.yml ├── is_container.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parseable: true 4 | quiet: true 5 | skip_list: 6 | - 'schema' 7 | - 'no-changed-when' 8 | - 'var-spacing' 9 | - 'experimental' 10 | - 'name[play]' 11 | - 'name[casing]' 12 | - 'name[template]' 13 | - 'key-order[task]' 14 | - 'yaml[line-length]' 15 | - '204' 16 | - '305' 17 | - '303' 18 | - '403' 19 | - '306' 20 | - '602' 21 | - '208' 22 | use_default_rules: true 23 | verbosity: 0 24 | -------------------------------------------------------------------------------- /.config/.secrets.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.4.0", 3 | "plugins_used": [ 4 | { 5 | "name": "ArtifactoryDetector" 6 | }, 7 | { 8 | "name": "AWSKeyDetector" 9 | }, 10 | { 11 | "name": "AzureStorageKeyDetector" 12 | }, 13 | { 14 | "name": "Base64HighEntropyString", 15 | "limit": 4.5 16 | }, 17 | { 18 | "name": "BasicAuthDetector" 19 | }, 20 | { 21 | "name": "CloudantDetector" 22 | }, 23 | { 24 | "name": "DiscordBotTokenDetector" 25 | }, 26 | { 27 | "name": "GitHubTokenDetector" 28 | }, 29 | { 30 | "name": "HexHighEntropyString", 31 | "limit": 3.0 32 | }, 33 | { 34 | "name": "IbmCloudIamDetector" 35 | }, 36 | { 37 | "name": "IbmCosHmacDetector" 38 | }, 39 | { 40 | "name": "JwtTokenDetector" 41 | }, 42 | { 43 | "name": "KeywordDetector", 44 | "keyword_exclude": "" 45 | }, 46 | { 47 | "name": "MailchimpDetector" 48 | }, 49 | { 50 | "name": "NpmDetector" 51 | }, 52 | { 53 | "name": "PrivateKeyDetector" 54 | }, 55 | { 56 | "name": "SendGridDetector" 57 | }, 58 | { 59 | "name": "SlackDetector" 60 | }, 61 | { 62 | "name": "SoftlayerDetector" 63 | }, 64 | { 65 | "name": "SquareOAuthDetector" 66 | }, 67 | { 68 | "name": "StripeDetector" 69 | }, 70 | { 71 | "name": "TwilioKeyDetector" 72 | } 73 | ], 74 | "filters_used": [ 75 | { 76 | "path": "detect_secrets.filters.allowlist.is_line_allowlisted" 77 | }, 78 | { 79 | "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", 80 | "min_level": 2 81 | }, 82 | { 83 | "path": "detect_secrets.filters.heuristic.is_indirect_reference" 84 | }, 85 | { 86 | "path": "detect_secrets.filters.heuristic.is_likely_id_string" 87 | }, 88 | { 89 | "path": "detect_secrets.filters.heuristic.is_lock_file" 90 | }, 91 | { 92 | "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" 93 | }, 94 | { 95 | "path": "detect_secrets.filters.heuristic.is_potential_uuid" 96 | }, 97 | { 98 | "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" 99 | }, 100 | { 101 | "path": "detect_secrets.filters.heuristic.is_sequential_string" 102 | }, 103 | { 104 | "path": "detect_secrets.filters.heuristic.is_swagger_file" 105 | }, 106 | { 107 | "path": "detect_secrets.filters.heuristic.is_templated_secret" 108 | }, 109 | { 110 | "path": "detect_secrets.filters.regex.should_exclude_file", 111 | "pattern": [ 112 | ".config/.gitleaks-report.json", 113 | "tasks/parse_etc_password.yml" 114 | ] 115 | } 116 | ], 117 | "results": {}, 118 | "generated_at": "2023-09-25T11:18:30Z" 119 | } 120 | -------------------------------------------------------------------------------- /.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 | - id: detect-private-key 15 | 16 | # git checks 17 | - id: check-merge-conflict 18 | - id: check-added-large-files 19 | - id: check-case-conflict 20 | 21 | # General checks 22 | - id: trailing-whitespace 23 | name: Trim Trailing Whitespace 24 | description: This hook trims trailing whitespace. 25 | entry: trailing-whitespace-fixer 26 | language: python 27 | types: [text] 28 | args: [--markdown-linebreak-ext=md] 29 | - id: end-of-file-fixer 30 | 31 | # Scan for passwords 32 | - repo: https://github.com/Yelp/detect-secrets 33 | rev: v1.5.0 34 | hooks: 35 | - id: detect-secrets 36 | args: [ '--baseline', '.config/.secrets.baseline' ] 37 | 38 | - repo: https://github.com/gitleaks/gitleaks 39 | rev: v8.26.0 40 | hooks: 41 | - id: gitleaks 42 | args: ['--baseline-path', '.config/.gitleaks-report.json'] 43 | 44 | - repo: https://github.com/ansible-community/ansible-lint 45 | rev: v25.5.0 46 | hooks: 47 | - id: ansible-lint 48 | name: Ansible-lint 49 | description: This hook runs ansible-lint. 50 | entry: python3 -m ansiblelint --force-color site.yml -c .ansible-lint 51 | language: python 52 | # do not pass files to ansible-lint, see: 53 | # https://github.com/ansible/ansible-lint/issues/611 54 | pass_filenames: false 55 | always_run: true 56 | additional_dependencies: 57 | # https://github.com/pre-commit/pre-commit/issues/1526 58 | # If you want to use specific version of ansible-core or ansible, feel 59 | # free to override `additional_dependencies` in your own hook config 60 | # file. 61 | - ansible-core>=2.10.1 62 | 63 | - repo: https://github.com/adrienverge/yamllint.git 64 | rev: v1.37.1 # or higher tag 65 | hooks: 66 | - id: yamllint 67 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | 5 | ignore: | 6 | tests/ 7 | molecule/ 8 | .github/ 9 | .gitlab-ci.yml 10 | *molecule.yml 11 | 12 | rules: 13 | indentation: 14 | # Requiring 4 space indentation 15 | spaces: 4 16 | # Requiring consistent indentation within a file, either indented or not 17 | indent-sequences: consistent 18 | braces: 19 | max-spaces-inside: 1 20 | level: error 21 | brackets: 22 | max-spaces-inside: 1 23 | level: error 24 | empty-lines: 25 | max: 1 26 | line-length: disable 27 | key-duplicates: enable 28 | new-line-at-end-of-file: enable 29 | new-lines: 30 | type: unix 31 | trailing-spaces: enable 32 | truthy: 33 | allowed-values: ['true', 'false'] 34 | check-keys: true 35 | -------------------------------------------------------------------------------- /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 or own fork 9 | 4) Pull requests 10 | a) From within the repo: All pull requests go into the devel branch. There are automated checks for signed commits, signoff in commit message, and functional testing 11 | b) From a forked repo: All pull requests will go into a staging branch within the repo. There are automated checks for signed commits, signoff in commit message, and functional testing when going from staging to devel 12 | 4) All pull requests go into the devel branch. There are automated checks for signed commits, signoff in commit message, and functional testing) 13 | 5) Be open and nice to each other 14 | 15 | Workflow 16 | -------- 17 | - Your work is done in your own individual branch. Make sure to to Signed-off and GPG sign all commits you intend to merge 18 | - 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. 19 | - 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 20 | Signing your contribution 21 | ------------------------- 22 | 23 | We've chosen to use the Developer's Certificate of Origin (DCO) method 24 | that is employed by the Linux Kernel Project, which provides a simple 25 | way to contribute to MindPoint Group projects. 26 | 27 | The process is to certify the below DCO 1.1 text 28 | :: 29 | 30 | Developer's Certificate of Origin 1.1 31 | 32 | By making a contribution to this project, I certify that: 33 | 34 | (a) The contribution was created in whole or in part by me and I 35 | have the right to submit it under the open source license 36 | indicated in the file; or 37 | 38 | (b) The contribution is based upon previous work that, to the best 39 | of my knowledge, is covered under an appropriate open source 40 | license and I have the right under that license to submit that 41 | work with modifications, whether created in whole or in part 42 | by me, under the same open source license (unless I am 43 | permitted to submit under a different license), as indicated 44 | in the file; or 45 | 46 | (c) The contribution was provided directly to me by some other 47 | person who certified (a), (b) or (c) and I have not modified 48 | it. 49 | 50 | (d) I understand and agree that this project and the contribution 51 | are public and that a record of the contribution (including all 52 | personal information I submit with it, including my sign-off) is 53 | maintained indefinitely and may be redistributed consistent with 54 | this project or the open source license(s) involved. 55 | :: 56 | 57 | Then, when it comes time to submit a contribution, include the 58 | following text in your contribution commit message: 59 | 60 | :: 61 | 62 | Signed-off-by: Joan Doe 63 | 64 | :: 65 | 66 | 67 | This message can be entered manually, or if you have configured git 68 | with the correct `user.name` and `user.email`, you can use the `-s` 69 | option to `git commit` to automatically include the signoff message. 70 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Debian11CIS 2 | 3 | ## Based on CIS V1.0.0 4 | 5 | # Release 1.0.1 6 | 7 | - QA Fixes June 2025 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debian 11 CIS 2 | 3 | ## Configure a Debian 11 machine to be [CIS](https://www.cisecurity.org/cis-benchmarks/) compliant 4 | 5 | ### Based on CIS Debian Linux 11 LTS Benchmark v1.0.0 [Release](https://downloads.cisecurity.org/#/) 6 | 7 | ![Org Stars](https://img.shields.io/github/stars/ansible-lockdown?label=Org%20Stars&style=social) 8 | ![Stars](https://img.shields.io/github/stars/ansible-lockdown/Debian11-cis?label=Repo%20Stars&style=social) 9 | ![Forks](https://img.shields.io/github/forks/ansible-lockdown/Debian11-cis?style=social) 10 | ![followers](https://img.shields.io/github/followers/ansible-lockdown?style=social) 11 | [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/AnsibleLockdown.svg?style=social&label=Follow%20%40AnsibleLockdown)](https://twitter.com/AnsibleLockdown) 12 | 13 | ![Discord Badge](https://img.shields.io/discord/925818806838919229?logo=discord) 14 | 15 | ![Release Branch](https://img.shields.io/badge/Release%20Branch-Main-brightgreen) 16 | ![Release Tag](https://img.shields.io/github/v/release/ansible-lockdown/Debian11-CIS) 17 | ![Release Date](https://img.shields.io/github/release-date/ansible-lockdown/Debian11-CIS) 18 | 19 | [![Main Pipeline Status](https://github.com/ansible-lockdown/Debian11-CIS/actions/workflows/main_pipeline_validation.yml/badge.svg?)](https://github.com/ansible-lockdown/Debian11-CIS/actions/workflows/main_pipeline_validation.yml) 20 | 21 | [![Devel Pipeline Status](https://github.com/ansible-lockdown/Debian11-CIS/actions/workflows/devel_pipeline_validation.yml/badge.svg?)](https://github.com/ansible-lockdown/Debian11-CIS/actions/workflows/devel_pipeline_validation.yml) 22 | ![Devel Commits](https://img.shields.io/github/commit-activity/m/ansible-lockdown/Debian11-CIS/devel?color=dark%20green&label=Devel%20Branch%20Commits) 23 | 24 | ![Issues Open](https://img.shields.io/github/issues-raw/ansible-lockdown/Debian11-CIS?label=Open%20Issues) 25 | ![Issues Closed](https://img.shields.io/github/issues-closed-raw/ansible-lockdown/Debian11-CIS?label=Closed%20Issues&&color=success) 26 | ![Pull Requests](https://img.shields.io/github/issues-pr/ansible-lockdown/Debian11-CIS?label=Pull%20Requests) 27 | 28 | ![License](https://img.shields.io/github/license/ansible-lockdown/Debian11-CIS?label=License) 29 | 30 | --- 31 | 32 | ## Looking for support? 33 | 34 | [Lockdown Enterprise](https://www.lockdownenterprise.com#GH_AL_DEB11_cis) 35 | 36 | [Ansible support](https://www.mindpointgroup.com/cybersecurity-products/ansible-counselor#GH_AL_DEB11_cis) 37 | 38 | ### Community 39 | 40 | On our [Discord Server](https://www.lockdownenterprise.com/discord) to ask questions, discuss features, or just chat with other Ansible-Lockdown users 41 | 42 | ## Caution(s) 43 | 44 | This role **will make changes to the system** that could break things. This is not an auditing tool but rather a remediation tool to be used after an audit has been conducted. 45 | 46 | This role was developed against a clean install of the Operating System. If you are implementing to an existing system please review this role for any site specific changes that are needed. 47 | 48 | ## Documentation 49 | 50 | - [Read The Docs](https://ansible-lockdown.readthedocs.io/en/latest/) 51 | - [Getting Started](https://www.lockdownenterprise.com/docs/getting-started-with-lockdown#GH_AL_DEB11_cis) 52 | - [Customizing Roles](https://www.lockdownenterprise.com/docs/customizing-lockdown-enterprise#GH_AL_DEB11_cis) 53 | - [Per-Host Configuration](https://www.lockdownenterprise.com/docs/per-host-lockdown-enterprise-configuration#GH_AL_DEB11_cis) 54 | - [Getting the Most Out of the Role](https://www.lockdownenterprise.com/docs/get-the-most-out-of-lockdown-enterprise#GH_AL_DEB11_cis) 55 | 56 | ## Requirements 57 | 58 | **General:** 59 | 60 | - Basic knowledge of Ansible, below are some links to the Ansible documentation to help get started if you are unfamiliar with Ansible 61 | - [Main Ansible documentation page](https://docs.ansible.com) 62 | - [Ansible Getting Started](https://docs.ansible.com/ansible/latest/user_guide/intro_getting_started.html) 63 | - [Tower User Guide](https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html) 64 | - [Ansible Community Info](https://docs.ansible.com/ansible/latest/community/index.html) 65 | - Functioning Ansible and/or Tower Installed, configured, and running. This includes all of the base Ansible/Tower configurations, needed packages installed, and infrastructure setup. 66 | - Please read through the tasks in this role to gain an understanding of what each control is doing. Some of the tasks are disruptive and can have unintended consequences in a live production system. Also familiarize yourself with the variables in the defaults/main.yml file or the 67 | 68 | **Technical Dependencies:** 69 | 70 | - Running Ansible/Tower setup (this role is tested against Ansible version 2.9.1 and newer) 71 | - Python3 Ansible run environment 72 | 73 | ## Auditing (new) 74 | 75 | This can be turned on or off within the defaults/main.yml file with the variable run_audit. The value is false by default, please refer to the wiki for more details. 76 | 77 | This is a much quicker, very lightweight, checking (where possible) config compliance and live/running settings. 78 | 79 | A new form of auditing has been developed, by using a small (12MB) go binary called [goss](https://github.com/goss-org/goss) along with the relevant configurations to check. Without the need for infrastructure or other tooling. 80 | This audit will not only check the config has the correct setting but aims to capture if it is running with that configuration also trying to remove [false positives](https://www.mindpointgroup.com/blog/is-compliance-scanning-still-relevant/) in the process. 81 | 82 | Refer to [Debian11-CIS-Audit](https://github.com/ansible-lockdown/Debian11-CIS-Audit). 83 | 84 | Further audit documentation can be found at [Read The Docs](https://ansible-lockdown.readthedocs.io/en/latest/) 85 | 86 | ## Role Variables 87 | 88 | This role is designed that the end user should not have to edit the tasks themselves. All customizing should be done via the defaults/main.yml file or with extra vars within the project, job, workflow, etc. 89 | 90 | ## Branches 91 | 92 | - **devel** - This is the default branch and the working development branch. Community pull requests will pull into this branch 93 | - **main** - This is the release branch 94 | - **reports** - This is a protected branch for our scoring reports, no code should ever go here 95 | - **gh-pages** - This is the github pages branch 96 | - **all other branches** - Individual community member branches 97 | 98 | ## Community Contribution 99 | 100 | We encourage you (the community) to contribute to this role. Please read the rules below. 101 | 102 | - Your work is done in your own individual branch. Make sure to Signed-off and GPG sign all commits you intend to merge. 103 | - All community Pull Requests are pulled into the devel branch 104 | - Pull Requests into devel will confirm your commits have a GPG signature, Signed-off, and a functional test before being approved 105 | - 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 106 | 107 | ## Pipeline Testing 108 | 109 | uses: 110 | 111 | - ansible-core 2.12 112 | - ansible collections - pulls in the latest version based on requirements file 113 | - runs the audit using the devel branch 114 | - This is an automated test that occurs on pull requests into devel 115 | 116 | ## Added Extras 117 | 118 | - [pre-commit](https://pre-commit.com) can be tested and can be run from within the directory 119 | 120 | ```sh 121 | pre-commit run 122 | ``` 123 | 124 | ## Known Issues 125 | 126 | During rule 1.9 this may fail with the following 127 | 128 | ```bash 129 | You must correct your GRUB install devices before proceeding: 130 | 131 | DEBIAN_FRONTEND=dialog dpkg --configure grub-pc 132 | dpkg --configure -a 133 | ``` 134 | 135 | Check your current settings 136 | 137 | ```bash 138 | debconf-show grub-pc | grep install_devices: 139 | ``` 140 | 141 | If this returns the following with no value 142 | 143 | ```bash 144 | * grub-pc/install_devices: 145 | ``` 146 | 147 | You need to set the device onto with grub-pc will configured 148 | 149 | **USE AT YOUR OWN RISK** 150 | 151 | Example only (be aware of disk): 152 | 153 | Run the following with sudo: 154 | 155 | ```bash 156 | disk=$(find /dev -type l -lname '*/sda' -path '*/by-id/*') 157 | debconf-set-selections << EOF 158 | grub-pc grub-pc/install_devices multiselect $disk 159 | EOF 160 | apt update 161 | apt install grub-pc -y 162 | ``` 163 | 164 | ## Credits and Thanks 165 | 166 | Massive thanks to the fantastic community and all its members. 167 | This includes a huge thanks and credit to the original authors and maintainers. 168 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Update_Initramfs 4 | ansible.builtin.shell: update-initramfs -u 5 | notify: change_requires_reboot 6 | 7 | - name: Remount tmp 8 | ansible.posix.mount: 9 | path: /tmp 10 | state: remounted 11 | 12 | - name: Remount var 13 | ansible.posix.mount: 14 | path: /var 15 | state: remounted 16 | 17 | - name: Remount var_tmp 18 | ansible.posix.mount: 19 | path: /var/tmp 20 | state: remounted 21 | 22 | - name: Remount var_log 23 | ansible.posix.mount: 24 | path: /var/log 25 | state: remounted 26 | 27 | - name: Remount var_log_audit 28 | ansible.posix.mount: 29 | path: /var/log/audit 30 | state: remounted 31 | 32 | - name: Remount home 33 | ansible.posix.mount: 34 | path: /home 35 | state: remounted 36 | 37 | - name: Remount dev_shm 38 | ansible.posix.mount: 39 | path: /dev/shm 40 | src: /dev/shm 41 | state: remounted 42 | 43 | - name: Grub update 44 | ansible.builtin.shell: update-grub 45 | failed_when: false 46 | notify: change_requires_reboot 47 | 48 | - name: Restart timeservice 49 | ansible.builtin.systemd: 50 | name: "{{ debian11cis_time_sync_tool }}" 51 | state: restarted 52 | 53 | - name: Reload systemctl 54 | ansible.builtin.systemd: 55 | daemon_reload: true 56 | 57 | - name: Update dconf 58 | ansible.builtin.shell: dconf update 59 | failed_when: false 60 | 61 | - name: Restart postfix 62 | ansible.builtin.service: 63 | name: postfix 64 | state: restarted 65 | 66 | - name: Restart syslog service 67 | ansible.builtin.systemd: 68 | name: "{{ debian11cis_syslog_service }}" 69 | state: restarted 70 | 71 | - name: Restart journald 72 | ansible.builtin.systemd: 73 | name: systemd-journald 74 | state: restarted 75 | 76 | - name: Restart exim4 77 | ansible.builtin.systemd: 78 | name: exim4 79 | state: restarted 80 | 81 | - name: Flush ipv4 route table 82 | ansible.posix.sysctl: 83 | name: net.ipv4.route.flush 84 | value: '1' 85 | sysctl_set: true 86 | when: ansible_facts.virtualization_type != "docker" 87 | 88 | - name: Flush ipv6 route table 89 | ansible.posix.sysctl: 90 | name: net.ipv6.route.flush 91 | value: '1' 92 | sysctl_set: true 93 | when: 94 | - ansible_facts.virtualization_type != "docker" 95 | - debian11cis_ipv6_required 96 | 97 | - name: Reload ufw 98 | community.general.ufw: 99 | state: reloaded 100 | 101 | - name: Iptables persistent 102 | ansible.builtin.shell: bash -c "iptables-save > /etc/iptables/rules.v4" 103 | changed_when: discovered_ip4tables_save.rc == 0 104 | failed_when: discovered_ip4tables_save.rc > 0 105 | register: discovered_ip4tables_save 106 | 107 | - name: Ip6tables persistent 108 | ansible.builtin.shell: bash -c "ip6tables-save > /etc/iptables/rules.v6" 109 | changed_when: discovered_ip6tables_save.rc == 0 110 | failed_when: discovered_ip6tables_save.rc > 0 111 | register: discovered_ip6tables_save 112 | 113 | - name: Auditd rules reload 114 | ansible.builtin.shell: augenrules --load 115 | when: 116 | - discovered_auditd_diff_check is not defined 117 | 118 | - name: Audit_immutable_fact 119 | ansible.builtin.debug: 120 | msg: "Reboot required for auditd to apply new rules as immutable set" 121 | notify: change_requires_reboot 122 | when: 123 | - discovered_audit_rules_updated.changed 124 | - auditd_immutable_check is defined 125 | 126 | - name: Restart auditd 127 | ansible.builtin.shell: service auditd restart 128 | when: 129 | - discovered_audit_rules_updated is defined 130 | tags: 131 | - skip_ansible_lint 132 | 133 | - name: restart sshd 134 | ansible.builtin.systemd: 135 | name: sshd 136 | state: restarted 137 | 138 | - name: reload gdm 139 | ansible.builtin.shell: dpkg-reconfigure gdm3 140 | 141 | - name: change_requires_reboot 142 | ansible.builtin.set_fact: 143 | change_requires_reboot: true 144 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | galaxy_info: 4 | author: "Mark Bolwell, George Nalen" 5 | description: "Apply the Debian 11 CIS benchmarks" 6 | company: "MindPoint Group" 7 | license: MIT 8 | namespace: mindpointgroup 9 | role_name: debian11_cis 10 | min_ansible_version: 2.15.1 11 | platforms: 12 | - name: Debian 13 | versions: 14 | - bullseye 15 | galaxy_tags: 16 | - system 17 | - security 18 | - cis 19 | - hardening 20 | - benchmark 21 | - compliance 22 | - complianceascode 23 | - debian 24 | collections: 25 | - community.general 26 | - community.crypto 27 | - ansible.posix 28 | dependencies: [] 29 | -------------------------------------------------------------------------------- /site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: all 4 | become: true 5 | 6 | roles: 7 | 8 | - role: "{{ playbook_dir }}" 9 | -------------------------------------------------------------------------------- /tasks/LE_audit_setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Pre Audit Setup | Set audit package name 4 | block: 5 | - name: Pre Audit Setup | Set audit package name | 64bit 6 | when: ansible_facts.machine == "x86_64" 7 | ansible.builtin.set_fact: 8 | audit_pkg_arch_name: AMD64 9 | 10 | - name: Pre Audit Setup | Set audit package name | ARM64 11 | when: (ansible_facts.machine == "arm64" or ansible_facts.machine == "aarch64") 12 | ansible.builtin.set_fact: 13 | audit_pkg_arch_name: ARM64 14 | 15 | - name: Pre Audit Setup | Download audit binary 16 | when: get_audit_binary_method == 'download' 17 | ansible.builtin.get_url: 18 | url: "{{ audit_bin_url }}{{ audit_pkg_arch_name }}" 19 | dest: "{{ audit_bin }}" 20 | owner: root 21 | group: root 22 | checksum: "{{ audit_bin_version[audit_pkg_arch_name + '_checksum'] }}" 23 | mode: '0555' 24 | 25 | - name: Pre Audit Setup | Copy audit binary 26 | when: get_audit_binary_method == 'copy' 27 | ansible.builtin.copy: 28 | src: "{{ audit_bin_copy_location }}" 29 | dest: "{{ audit_bin }}" 30 | mode: '0555' 31 | owner: root 32 | group: root 33 | -------------------------------------------------------------------------------- /tasks/audit_only.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Audit_only | Show Audit Summary 4 | when: audit_only 5 | ansible.builtin.debug: 6 | msg: "{{ audit_results.split('\n') }}" 7 | 8 | - name: Audit_only | Stop Playbook Audit Only selected 9 | when: audit_only 10 | ansible.builtin.meta: end_play 11 | -------------------------------------------------------------------------------- /tasks/auditd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "POST | AUDITD | Apply auditd template for section 4.1.3.x" 4 | when: update_audit_template 5 | ansible.builtin.template: 6 | src: audit/99_auditd.rules.j2 7 | dest: /etc/audit/rules.d/99_auditd.rules 8 | owner: root 9 | group: root 10 | mode: '0640' 11 | register: discovered_audit_rules_updated 12 | notify: 13 | - Auditd rules reload 14 | - Audit_immutable_fact 15 | - Restart auditd 16 | 17 | - name: POST | Set up auditd user logging exceptions 18 | when: 19 | - deb11cis_allow_auditd_uid_user_exclusions 20 | - deb11cis_auditd_uid_exclude | length > 0 21 | ansible.builtin.template: 22 | src: audit/98_auditd_exception.rules.j2 23 | dest: /etc/audit/rules.d/98_auditd_exceptions.rules 24 | owner: root 25 | group: root 26 | mode: 0600 27 | notify: Restart auditd 28 | -------------------------------------------------------------------------------- /tasks/fetch_audit_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Stage to copy audit output to a centralised location 4 | 5 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller" 6 | when: audit_output_collection_method == "fetch" 7 | ansible.builtin.fetch: 8 | src: "{{ item }}" 9 | dest: "{{ audit_output_destination }}" 10 | flat: true 11 | failed_when: false 12 | register: discovered_audit_fetch_state 13 | loop: 14 | - "{{ pre_audit_outfile }}" 15 | - "{{ post_audit_outfile }}" 16 | become: false 17 | 18 | # Added this option for continuity but could be changed by adjusting the variable audit_conf_dest 19 | # Allowing backup to one location 20 | - name: "FETCH_AUDIT_FILES | Copy files to location available to managed node" 21 | when: audit_output_collection_method == "copy" 22 | ansible.builtin.copy: 23 | src: "{{ item }}" 24 | dest: "{{ audit_output_destination }}" 25 | mode: 'u-x,go-wx' 26 | flat: true 27 | failed_when: false 28 | register: discovered_audit_copy_state 29 | loop: 30 | - "{{ pre_audit_outfile }}" 31 | - "{{ post_audit_outfile }}" 32 | 33 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 34 | when: 35 | - (audit_output_collection_method == "fetch" and not discovered_audit_fetch_state.changed) or 36 | (audit_output_collection_method == "copy" and not discovered_audit_copy_state.changed) 37 | block: 38 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 39 | ansible.builtin.debug: 40 | msg: "Warning!! Unable to write to localhost {{ audit_output_destination }} for audit file copy" 41 | 42 | - name: "FETCH_AUDIT_FILES | Fetch files and copy to controller | Warning if issues with fetch_audit_files" 43 | vars: 44 | warn_control_id: "FETCH_AUDIT_FILES" 45 | ansible.builtin.import_tasks: 46 | file: warning_facts.yml 47 | -------------------------------------------------------------------------------- /tasks/parse_etc_password.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "PRELIM | Parse /etc/passwd" 4 | tags: always 5 | block: 6 | - name: "PRELIM | Parse /etc/passwd | Get /etc/password contents" 7 | ansible.builtin.shell: cat /etc/passwd 8 | changed_when: false 9 | check_mode: false 10 | register: discovered_passwd_file_audit 11 | 12 | - name: "PRELIM | Parse /etc/passwd | Split passwd entries" 13 | ansible.builtin.set_fact: 14 | debian11cis_passwd: "{{ discovered_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" 15 | 16 | with_items: "{{ discovered_passwd_file_audit.stdout_lines }}" 17 | vars: 18 | ld_passwd_regex: >- 19 | ^(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*):(?P[^:]*) 20 | ld_passwd_yaml: | 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_remediation_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Post Audit | Run post_remediation {{ benchmark }} audit 4 | ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ post_audit_outfile }} -g \"{{ group_names }}\"" 5 | changed_when: true 6 | environment: 7 | AUDIT_BIN: "{{ audit_bin }}" 8 | AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" 9 | AUDIT_FILE: goss.yml 10 | 11 | - name: Post Audit | ensure audit files readable by users 12 | ansible.builtin.file: 13 | path: "{{ item }}" 14 | mode: '0644' 15 | state: file 16 | loop: 17 | - "{{ post_audit_outfile }}" 18 | - "{{ pre_audit_outfile }}" 19 | 20 | - name: Post Audit | Capture audit data if json format 21 | when: 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 | register: post_audit_summary 26 | changed_when: false 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 "{{ post_audit_outfile }}" | tac | tr '\n' ' ' 37 | register: post_audit_summary 38 | changed_when: false 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 {{ audit_conf_dir }} exists 10 | ansible.builtin.file: 11 | path: "{{ audit_conf_dir }}" 12 | state: directory 13 | mode: '0755' 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: discovered_goss_available 57 | 58 | - name: Pre Audit Setup | If audit ensure goss is available 59 | when: 60 | - not discovered_goss_available.stat.exists 61 | ansible.builtin.assert: 62 | msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" 63 | 64 | - name: Pre Audit Setup | Copy ansible default vars values to test audit 65 | when: run_audit 66 | tags: 67 | - goss_template 68 | - run_audit 69 | ansible.builtin.template: 70 | src: ansible_vars_goss.yml.j2 71 | dest: "{{ audit_vars_path }}" 72 | mode: '0600' 73 | 74 | - name: Pre Audit | Run pre_remediation {{ benchmark }} audit 75 | ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -f {{ audit_format }} -o {{ pre_audit_outfile }} -g \"{{ group_names }}\"" 76 | changed_when: true 77 | environment: 78 | AUDIT_BIN: "{{ audit_bin }}" 79 | AUDIT_CONTENT_LOCATION: "{{ audit_conf_dest | default('/opt') }}" 80 | AUDIT_FILE: goss.yml 81 | 82 | - name: Pre Audit | Capture audit data if json format 83 | when: audit_format == "json" 84 | block: 85 | - name: Pre Audit | Capture audit data if json format 86 | ansible.builtin.shell: grep -E '\"summary-line.*Count:.*Failed' "{{ pre_audit_outfile }}" | cut -d'"' -f4 87 | register: pre_audit_summary 88 | changed_when: false 89 | 90 | - name: Pre Audit | Set Fact for audit summary 91 | ansible.builtin.set_fact: 92 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 93 | 94 | - name: Pre Audit | Capture audit data if documentation format 95 | when: audit_format == "documentation" 96 | block: 97 | - name: Pre Audit | Capture audit data if documentation format 98 | ansible.builtin.shell: tail -2 "{{ pre_audit_outfile }}" | tac | tr '\n' ' ' 99 | register: pre_audit_summary 100 | changed_when: false 101 | 102 | - name: Pre Audit | Set Fact for audit summary 103 | ansible.builtin.set_fact: 104 | pre_audit_results: "{{ pre_audit_summary.stdout }}" 105 | 106 | - name: Audit_Only | Run Audit Only 107 | when: audit_only 108 | ansible.builtin.import_tasks: 109 | file: audit_only.yml 110 | -------------------------------------------------------------------------------- /tasks/prelim.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "PRELIM | Register if snap being used" 4 | when: debian11cis_rule_1_1_1_2 5 | tags: 6 | - rule_1.1.1.2 7 | - always 8 | ansible.builtin.shell: df -h | grep -wc "/snap" 9 | changed_when: false 10 | failed_when: snap_pkg_mgr.rc not in [ 0, 1 ] 11 | register: snap_pkg_mgr 12 | 13 | - name: "PRELIM | Register if squashfs is built into the kernel" 14 | when: debian11cis_rule_1_1_1_2 15 | tags: 16 | - rule_1.1.1.2 17 | - always 18 | ansible.builtin.shell: cat /lib/modules/$(uname -r)/modules.builtin | grep -c "squashfs" 19 | changed_when: false 20 | failed_when: squashfs_builtin.rc not in [ 0, 1 ] 21 | register: squashfs_builtin 22 | 23 | - name: "PRELIM | Section 1.1 | Create list of mount points" 24 | tags: always 25 | ansible.builtin.set_fact: 26 | mount_names: "{{ ansible_facts.mounts | map(attribute='mount') | list }}" 27 | 28 | - name: PRELIM | Capture tmp mount type | discover mount tmp type 29 | when: 30 | - "'/tmp' in mount_names" 31 | - debian11cis_rule_1_1_2_1 or 32 | debian11cis_rule_1_1_2_2 or 33 | debian11cis_rule_1_1_2_3 or 34 | debian11cis_rule_1_1_2_4 35 | tags: always 36 | block: 37 | - name: PRELIM | Capture tmp mount type | discover mount tmp type 38 | ansible.builtin.shell: systemctl is-enabled tmp.mount 39 | register: discover_tmp_mnt_type 40 | changed_when: false 41 | failed_when: discover_tmp_mnt_type.rc not in [ 0, 1 ] 42 | 43 | - name: PRELIM | Capture tmp mount type | Set to expected_tmp_mnt variable 44 | ansible.builtin.set_fact: 45 | tmp_mnt_type: "{{ expected_tmp_mnt }}" 46 | when: "'generated' in discover_tmp_mnt_type.stdout" 47 | 48 | - name: PRELIM | Capture tmp mount type | Set systemd service 49 | ansible.builtin.set_fact: 50 | tmp_mnt_type: tmp_systemd 51 | when: "'generated' not in discover_tmp_mnt_type.stdout" 52 | 53 | - name: "PRELIM | Run apt update" 54 | when: debian11cis_rule_1_3_1 or debian11cis_rule_1_9 55 | tags: always 56 | ansible.builtin.package: 57 | update_cache: true 58 | 59 | - name: "PRELIM | Check for autofs service" 60 | when: debian11cis_rule_1_1_9 61 | tags: 62 | - skip_ansible_lint 63 | - section1 64 | - always 65 | ansible.builtin.shell: "systemctl show autofs | grep LoadState | cut -d = -f 2" 66 | register: debian11cis_autofs_service_status 67 | changed_when: false 68 | check_mode: false 69 | 70 | - name: "PRELIM | Check for avahi-daemon service" 71 | when: debian11cis_rule_2_2_2 72 | tags: 73 | - skip_ansible_lint 74 | - always 75 | ansible.builtin.shell: "systemctl show avahi-daemon | grep LoadState | cut -d = -f 2" 76 | register: avahi_service_status 77 | changed_when: false 78 | check_mode: false 79 | 80 | - name: "PRELIM | Install Network-Manager" 81 | when: 82 | - debian11cis_rule_3_1_2 83 | - debian11cis_install_network_manager 84 | - not debian11cis_system_is_container 85 | - "'network-manager' not in ansible_facts.packages" 86 | tags: always 87 | ansible.builtin.package: 88 | name: network-manager 89 | state: present 90 | 91 | - name: "PRELIM | PATCH | Ensure auditd is installed" 92 | when: 93 | - debian11cis_rule_4_1_1_1 94 | - debian11cis_rule_4_1_4_5 or 95 | debian11cis_rule_4_1_4_6 or 96 | debian11cis_rule_4_1_4_7 97 | tags: 98 | - level2-server 99 | - level2-workstation 100 | - patch 101 | - auditd 102 | - always 103 | block: 104 | - name: "PRELIM | PATCH | Ensure auditd is installed" 105 | ansible.builtin.package: 106 | name: ['auditd', 'audispd-plugins'] 107 | state: present 108 | when: 109 | - "'auditd' not in ansible_facts.packages or 110 | 'auditd-plugins' not in ansible_facts.packages" 111 | 112 | - name: "PRELIM | 4.1.4.5 | Audit conf and rules files | list files" 113 | ansible.builtin.find: 114 | path: /etc/audit/ 115 | file_type: file 116 | recurse: true 117 | patterns: '*.conf,*.rules' 118 | register: auditd_conf_files 119 | 120 | - name: "PRELIM | Check if auditd is immutable before changes" 121 | when: "'auditd' in ansible_facts.packages" 122 | tags: always 123 | ansible.builtin.shell: auditctl -l | grep -c '-e 2' 124 | changed_when: false 125 | failed_when: auditd_immutable_check.rc not in [ 0, 1 ] 126 | register: auditd_immutable_check 127 | 128 | - name: "PRELIM | 5.3.4 | 5.3.5 | Find all sudoers files." 129 | when: debian11cis_rule_5_3_4 or debian11cis_rule_5_3_5 130 | tags: always 131 | ansible.builtin.shell: "find /etc/sudoers /etc/sudoers.d/ -type f ! -name '*~' ! -name '*.*'" 132 | changed_when: false 133 | failed_when: false 134 | check_mode: false 135 | register: debian11cis_sudoers_files 136 | 137 | - name: "PRELIM | Discover Interactive UID MIN and MIN from logins.def" 138 | when: not discover_int_uid 139 | tags: always 140 | block: 141 | - name: "PRELIM | Capture UID_MIN information from logins.def" 142 | ansible.builtin.shell: grep -w "^UID_MIN" /etc/login.defs | awk '{print $NF}' 143 | changed_when: false 144 | register: uid_min_id 145 | 146 | - name: "PRELIM | Capture UID_MAX information from logins.def" 147 | ansible.builtin.shell: grep -w "^UID_MAX" /etc/login.defs | awk '{print $NF}' 148 | changed_when: false 149 | register: uid_max_id 150 | 151 | - name: "PRELIM | Capture GID_MIN information from logins.def" 152 | ansible.builtin.shell: grep -w "^GID_MIN" /etc/login.defs | awk '{print $NF}' 153 | changed_when: false 154 | register: gid_min_id 155 | 156 | - name: "PRELIM | Set facts for interactive uid/gid" 157 | ansible.builtin.set_fact: 158 | min_int_uid: "{{ uid_min_id.stdout }}" 159 | max_int_uid: "{{ uid_max_id.stdout }}" 160 | min_int_gid: "{{ gid_min_id.stdout }}" 161 | 162 | - name: "PRELIM | Interactive User accounts" 163 | when: 164 | - debian11cis_rule_6_2_11 or 165 | debian11cis_rule_6_2_13 or 166 | debian11cis_rule_6_2_14 or 167 | debian11cis_rule_6_2_15 or 168 | debian11cis_rule_6_2_16 169 | tags: always 170 | ansible.builtin.shell: 'cat /etc/passwd | grep -Ev "nologin|/sbin|/bin" | cut -d: -f6' 171 | changed_when: false 172 | register: interactive_users_home 173 | 174 | - name: "PRELIM | Install ACL" 175 | when: 176 | - debian11cis_rule_6_2_6 177 | - "'acl' not in ansible_facts.packages" 178 | tags: always 179 | ansible.builtin.package: 180 | name: acl 181 | state: present 182 | 183 | - name: "PRELIM | Gather UID 0 accounts other than root" 184 | when: debian11cis_rule_6_2_10 185 | tags: 186 | - rule_6.2.10 187 | - level1-server 188 | - level1-workstation 189 | - users 190 | - always 191 | ansible.builtin.shell: "cat /etc/passwd | awk -F: '($3 == 0 && $1 != \"root\") {i++;print $1 } END {exit i}'" 192 | changed_when: false 193 | check_mode: false 194 | register: debian11cis_uid_zero_accounts_except_root 195 | 196 | - name: "PRELIM | List users accounts" 197 | when: 198 | - debian11cis_rule_6_2_14 or 199 | debian11cis_rule_6_2_15 or 200 | debian11cis_rule_6_2_16 201 | tags: always 202 | ansible.builtin.shell: "awk -F: '{print $1}' /etc/passwd" 203 | changed_when: false 204 | check_mode: false 205 | register: debian11cis_users 206 | 207 | - name: "Optional | Patch | UFW firewall force to use /etc/sysctl.conf settings" 208 | when: 209 | - debian11cis_firewall_package == "ufw" 210 | - debian11cis_ufw_use_sysctl 211 | tags: always 212 | ansible.builtin.lineinfile: 213 | path: /etc/default/ufw 214 | regexp: ^IPT_SYSCTL=.* 215 | line: IPT_SYSCTL=/etc/sysctl.conf 216 | create: true 217 | mode: 'u-x,g-wx,o-rwx' 218 | owner: root 219 | group: root 220 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled" 4 | block: 5 | - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Edit modprobe config" 6 | ansible.builtin.lineinfile: 7 | dest: /etc/modprobe.d/cramfs.conf 8 | regexp: '^(#)?install cramfs(\\s|$)' 9 | line: install cramfs /bin/true 10 | create: true 11 | 12 | - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled | Disable cramfs" 13 | community.general.modprobe: 14 | name: cramfs 15 | state: absent 16 | when: ansible_connection != 'docker' 17 | 18 | - name: "1.1.1.1 | PATCH | Ensure mounting of cramfs filesystems is disabled | blacklist cramfs" 19 | ansible.builtin.lineinfile: 20 | dest: /etc/modprobe.d/blacklist.conf 21 | regexp: '^blacklist cramfs' 22 | line: blacklist cramfs 23 | create: true 24 | notify: Update_Initramfs 25 | when: debian11cis_rule_1_1_1_1 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - rule_1.1.1.1 32 | - cramfs 33 | 34 | - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled" 35 | block: 36 | - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled | Edit modprobe config" 37 | ansible.builtin.lineinfile: 38 | dest: /etc/modprobe.d/squashfs.conf 39 | regexp: '^(#)?install squashfs(\\s|$)' 40 | line: install squashfs /bin/true 41 | create: true 42 | 43 | - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled | Disable squashfs" 44 | community.general.modprobe: 45 | name: squashfs 46 | state: absent 47 | when: ansible_connection != 'docker' 48 | 49 | - name: "1.1.1.2 | PATCH | Ensure mounting of squashfs filesystems is disabled | blacklist squashfs" 50 | ansible.builtin.lineinfile: 51 | dest: /etc/modprobe.d/blacklist.conf 52 | regexp: '^blacklist squashfs' 53 | line: blacklist squashfs 54 | create: true 55 | notify: Update_Initramfs 56 | when: 57 | - debian11cis_rule_1_1_1_2 58 | - snap_pkg_mgr.stdout == '0' 59 | - squashfs_builtin.stdout == '0' 60 | tags: 61 | - level2-server 62 | - level2-workstation 63 | - automated 64 | - patch 65 | - rule_1.1.1.2 66 | - squashfs 67 | 68 | - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disabled" 69 | block: 70 | - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disabled | Edit modprobe config" 71 | ansible.builtin.lineinfile: 72 | dest: /etc/modprobe.d/udf.conf 73 | regexp: '^(#)?install udf(\\s|$)' 74 | line: install udf /bin/true 75 | create: true 76 | 77 | - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disabled | Disable udf" 78 | community.general.modprobe: 79 | name: udf 80 | state: absent 81 | when: ansible_connection != 'docker' 82 | 83 | - name: "1.1.1.3 | PATCH | Ensure mounting of udf filesystems is disabled | blacklist udf" 84 | ansible.builtin.lineinfile: 85 | dest: /etc/modprobe.d/blacklist.conf 86 | regexp: '^blacklist udf' 87 | line: blacklist udf 88 | create: true 89 | notify: Update_Initramfs 90 | when: debian11cis_rule_1_1_1_3 91 | tags: 92 | - level2-server 93 | - level2-workstation 94 | - automated 95 | - patch 96 | - rule_1.1.1.3 97 | - udf 98 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.10.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.10 | PATCH | Disable USB Storage" 4 | block: 5 | - name: "1.1.10 | PATCH | Disable USB Storage | Set modprobe config" 6 | ansible.builtin.lineinfile: 7 | path: /etc/modprobe.d/usb_storage.conf 8 | regexp: '^install usb-storage' 9 | line: 'install usb-storage /bin/true' 10 | create: true 11 | 12 | - name: "1.1.10 | PATCH | Disable USB Storage | Blacklist usb-storage" 13 | ansible.builtin.lineinfile: 14 | path: /etc/modprobe.d/blacklist.conf 15 | line: 'blacklist usb-storage' 16 | insertafter: EOF 17 | 18 | - name: "1.1.10 | PATCH | Disable USB Storage | Remove usb-storage module" 19 | community.general.modprobe: 20 | name: usb-storage 21 | state: absent 22 | when: ansible_connection != 'docker' 23 | notify: Update_Initramfs 24 | when: 25 | - debian11cis_rule_1_1_10 26 | - not debian11cis_allow_usb_storage 27 | tags: 28 | - level1-server 29 | - level2-workstation 30 | - automated 31 | - patch 32 | - rule_1.1.10 33 | - usb_storage 34 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.2.1 | AUDIT | Ensure /tmp is a separate partition" 4 | block: 5 | - name: "1.1.2.1 | AUDIT | Ensure /tmp is a separate partition | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.2.1 | WARN | Ensure /tmp is a separate partition | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.2.1' 14 | required_mount: '/tmp' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_2_1 18 | tags: 19 | - level1-server 20 | - level1-workstation 21 | - audit 22 | - mounts 23 | - rule_1.1.2.1 24 | - tmp 25 | 26 | - name: | 27 | "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition | tmp_systemd" 28 | "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition | tmp_systemd" 29 | "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition | tmp_systemd" 30 | ansible.builtin.template: 31 | src: etc/systemd/system/tmp.mount.j2 32 | dest: /etc/systemd/system/tmp.mount 33 | owner: root 34 | group: root 35 | mode: 'u-x,go-wx' 36 | notify: Remount tmp 37 | with_items: 38 | - "{{ ansible_facts.mounts }}" 39 | loop_control: 40 | label: "{{ item.device }}" 41 | when: 42 | - "'/tmp' in mount_names" 43 | - item.mount == "/tmp" 44 | - tmp_mnt_type == 'tmp_systemd' 45 | - debian11cis_rule_1_1_2_2 or 46 | debian11cis_rule_1_1_2_3 or 47 | debian11cis_rule_1_1_2_4 48 | tags: 49 | - level1-server 50 | - level1-workstation 51 | - automated 52 | - patch 53 | - rule_1.1.2.2 54 | - rule_1.1.2.3 55 | - rule_1.1.2.4 56 | - tmp 57 | 58 | - name: | 59 | "1.1.2.2 | PATCH | Ensure nodev option set on /tmp partition | fstab" 60 | "1.1.2.3 | PATCH | Ensure noexec option set on /tmp partition | fstab" 61 | "1.1.2.4 | PATCH | Ensure nosuid option set on /tmp partition | fstab" 62 | ansible.posix.mount: 63 | path: /tmp 64 | src: "{{ item.device }}" 65 | state: present 66 | fstype: "{{ item.fstype }}" 67 | opts: defaults,{% if debian11cis_rule_1_1_2_2 %}nodev,{% endif %}{% if debian11cis_rule_1_1_2_3 %}noexec,{% endif %}{% if debian11cis_rule_1_1_2_4 %}nosuid{% endif %} 68 | notify: Remount tmp 69 | with_items: 70 | - "{{ ansible_facts.mounts }}" 71 | loop_control: 72 | label: "{{ item.device }}" 73 | when: 74 | - "'/tmp' in mount_names" 75 | - tmp_mnt_type == 'fstab' 76 | - item.mount == "/tmp" 77 | - debian11cis_rule_1_1_2_2 or 78 | debian11cis_rule_1_1_2_3 or 79 | debian11cis_rule_1_1_2_4 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - automated 84 | - patch 85 | - rule_1.1.2.2 86 | - rule_1.1.2.2 87 | - rule_1.1.2.3 88 | - rule_1.1.2.4 89 | - tmp 90 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var" 4 | block: 5 | - name: "1.1.3.1 | AUDIT | Ensure separate partition exists for /var | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.3.1 | WARN | Ensure separate partition exists for /var | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.3.1' 14 | required_mount: '/var' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_3_1 18 | tags: 19 | - level2-server 20 | - level2-workstation 21 | - automated 22 | - audit 23 | - rule_1.1.3.1 24 | - var 25 | 26 | - name: | 27 | "1.1.3.2 | PATCH | Ensure nodev option set on /var partition" 28 | "1.1.3.3 | PATCH | Ensure nosuid option set on /var partition" 29 | ansible.posix.mount: 30 | path: /var 31 | src: "{{ item.device }}" 32 | state: present 33 | fstype: "{{ item.fstype }}" 34 | opts: defaults,{% if debian11cis_rule_1_1_3_2 %}nodev,{% endif %}{% if debian11cis_rule_1_1_3_3 %}nosuid{% endif %} 35 | notify: Remount var 36 | loop: "{{ ansible_facts.mounts }}" 37 | loop_control: 38 | label: "{{ item.device }}" 39 | when: 40 | - item.mount == "/var" 41 | - debian11cis_rule_1_1_3_2 or 42 | debian11cis_rule_1_1_3_3 43 | tags: 44 | - level1-server 45 | - level1-workstation 46 | - automated 47 | - patch 48 | - rule_1.1.3.2 49 | - rule_1.1.3.3 50 | - var 51 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp" 4 | block: 5 | - name: "1.1.4.1 | AUDIT | Ensure separate partition exists for /var/tmp | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.4.1 | WARN | Ensure separate partition exists for /var/tmp | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.4.1' 14 | required_mount: '/var/tmp' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_4_1 18 | tags: 19 | - level2-server 20 | - level2-workstation 21 | - automated 22 | - audit 23 | - rule_1.1.4.1 24 | - var_tmp 25 | 26 | - name: | 27 | "1.1.4.2 | PATCH | Ensure noexec option set on /var/tmp partition" 28 | "1.1.4.3 | PATCH | Ensure nosuid option set on /var/tmp partition" 29 | "1.1.4.4 | PATCH | Ensure nodev option set on /var/tmp partition" 30 | ansible.posix.mount: 31 | path: /var/tmp 32 | src: "{{ item.device }}" 33 | state: present 34 | fstype: "{{ item.fstype }}" 35 | opts: defaults,{% if debian11cis_rule_1_1_4_2 %}noexec,{% endif %}{% if debian11cis_rule_1_1_4_3 %}nosuid,{% endif %}{% if debian11cis_rule_1_1_4_4 %}nodev{% endif %} 36 | notify: Remount var_tmp 37 | with_items: "{{ ansible_facts.mounts }}" 38 | loop_control: 39 | label: "{{ item.device }}" 40 | when: 41 | - item.mount == "/var/tmp" 42 | - debian11cis_rule_1_1_4_2 or 43 | debian11cis_rule_1_1_4_3 or 44 | debian11cis_rule_1_1_4_4 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - automated 49 | - patch 50 | - rule_1.1.4.2 51 | - rule_1.1.4.3 52 | - rule_1.1.4.4 53 | - var_tmp 54 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.5.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log" 4 | block: 5 | - name: "1.1.5.1 | AUDIT | Ensure separate partition exists for /var/log | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.5.1 | WARN | Ensure separate partition exists for /var/log | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.5.1' 14 | required_mount: '/var/log' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_5_1 18 | tags: 19 | - level2-server 20 | - level2-workstation 21 | - automated 22 | - audit 23 | - rule_1.1.5.1 24 | - var_log 25 | 26 | - name: | 27 | "1.1.5.2 | PATCH | Ensure nodev option set on /var/log partition" 28 | "1.1.5.3 | PATCH | Ensure noexec option set on /var/log partition" 29 | "1.1.5.4 | PATCH | Ensure nosuid option set on /var/log partition" 30 | ansible.posix.mount: 31 | path: /var/log 32 | src: "{{ item.device }}" 33 | state: present 34 | fstype: "{{ item.fstype }}" 35 | opts: defaults,{% if debian11cis_rule_1_1_5_2 %}nodev,{% endif %}{% if debian11cis_rule_1_1_5_3 %}noexec,{% endif %}{% if debian11cis_rule_1_1_5_4 %}nosuid{% endif %} 36 | notify: Remount var_log 37 | loop: "{{ ansible_facts.mounts }}" 38 | loop_control: 39 | label: "{{ item.device }}" 40 | when: 41 | - item.mount == "/var/log" 42 | - debian11cis_rule_1_1_5_2 or 43 | debian11cis_rule_1_1_5_3 or 44 | debian11cis_rule_1_1_5_4 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - automated 49 | - patch 50 | - rule_1.1.5.2 51 | - rule_1.1.5.3 52 | - rule_1.1.5.4 53 | - var_log 54 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit" 4 | block: 5 | - name: "1.1.6.1 | AUDIT | Ensure separate partition exists for /var/log/audit | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.6.1 | WARN | Ensure separate partition exists for /var/log/audit | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.6.1' 14 | required_mount: '/var/log/audit' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_6_1 18 | tags: 19 | - level2-server 20 | - level2-workstation 21 | - automated 22 | - audit 23 | - rule_1.1.6.1 24 | - var_log_audit 25 | 26 | - name: | 27 | "1.1.6.2 | PATCH | Ensure noexec option set on /var/log/audit partition" 28 | "1.1.6.3 | PATCH | Ensure nodev option set on /var/log/audit partition" 29 | "1.1.6.4 | PATCH | Ensure nosuid option set on /var/log/audit partition" 30 | ansible.posix.mount: 31 | path: /var/log/audit 32 | src: "{{ item.device }}" 33 | state: present 34 | fstype: "{{ item.fstype }}" 35 | opts: defaults,{% if debian11cis_rule_1_1_6_2 %}noexec,{% endif %}{% if debian11cis_rule_1_1_6_3 %}nodev,{% endif %}{% if debian11cis_rule_1_1_6_4 %}nosuid{% endif %} 36 | notify: Remount var_log_audit 37 | loop: "{{ ansible_facts.mounts }}" 38 | loop_control: 39 | label: "{{ item.device }}" 40 | when: 41 | - item.mount == "/var/log/audit" 42 | - debian11cis_rule_1_1_6_2 or 43 | debian11cis_rule_1_1_6_3 or 44 | debian11cis_rule_1_1_6_4 45 | tags: 46 | - level1-server 47 | - level1-workstation 48 | - automated 49 | - patch 50 | - rule_1.1.6.2 51 | - rule_1.1.6.3 52 | - rule_1.1.6.4 53 | - var_log_audit 54 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.7.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home" 4 | block: 5 | - name: "1.1.7.1 | AUDIT | Ensure separate partition exists for /home | Absent" 6 | ansible.builtin.debug: 7 | msg: "Warning!! {{ required_mount }} doesn't exist. This is a manual task" 8 | 9 | - name: "1.1.7.1 | WARN | Ensure separate partition exists for /home | warn_count" 10 | ansible.builtin.import_tasks: 11 | file: warning_facts.yml 12 | vars: 13 | warn_control_id: '1.1.7.1' 14 | required_mount: '/home' 15 | when: 16 | - required_mount not in mount_names 17 | - debian11cis_rule_1_1_7_1 18 | tags: 19 | - level2-server 20 | - level2-workstation 21 | - automated 22 | - audit 23 | - rule_1.1.7.1 24 | - home 25 | 26 | - name: | 27 | "1.1.7.2 | PATCH | Ensure nodev option set on /home partition" 28 | "1.1.7.3 | PATCH | Ensure nosuid option set on /home partition" 29 | ansible.posix.mount: 30 | path: /home 31 | src: "{{ item.device }}" 32 | state: present 33 | fstype: "{{ item.fstype }}" 34 | opts: defaults,{% if debian11cis_rule_1_1_7_2 %}nodev,{% endif %}{% if debian11cis_rule_1_1_7_3 %}nosuid{% endif %} 35 | notify: Remount home 36 | loop: "{{ ansible_facts.mounts }}" 37 | loop_control: 38 | label: "{{ item.device }}" 39 | when: 40 | - item.mount == "/home" 41 | - debian11cis_rule_1_1_7_2 or 42 | debian11cis_rule_1_1_7_3 43 | tags: 44 | - level1-server 45 | - level1-workstation 46 | - automated 47 | - patch 48 | - rule_1.1.7.2 49 | - rule_1.1.7.3 50 | - home 51 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.8.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: | 4 | "1.1.8.1 | PATCH | Ensure nodev option set on /dev/shm partition" 5 | "1.1.8.2 | PATCH | Ensure nosuid option set on /dev/shm partition" 6 | "1.1.8.3 | PATCH | Ensure noexec option set on /dev/shm partition" 7 | ansible.posix.mount: 8 | path: /dev/shm 9 | src: /dev/shm 10 | fstype: tmpfs 11 | state: present 12 | opts: "defaults,{% if debian11cis_rule_1_1_8_1 %}nodev,{% endif %}{% if debian11cis_rule_1_1_8_2 %}nosuid,{% endif %}{% if debian11cis_rule_1_1_8_3 %}noexec{% endif %}" 13 | notify: Remount dev_shm 14 | when: 15 | - debian11cis_rule_1_1_8_1 or 16 | debian11cis_rule_1_1_8_2 or 17 | debian11cis_rule_1_1_8_3 18 | tags: 19 | - level1-server 20 | - level1-workstation 21 | - automated 22 | - patch 23 | - rule_1.1.8.1 24 | - rule_1.1.8.2 25 | - rule_1.1.8.3 26 | - dev_shm 27 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.1.9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.1.9 | PATCH | Disable Automounting" 4 | block: 5 | - name: "1.1.9 | PATCH | Disable Automounting | remove package" 6 | ansible.builtin.package: 7 | name: autofs 8 | state: absent 9 | when: debian11cis_autofs == "remove" 10 | 11 | - name: "1.1.9 | PATCH | Disable Automounting | mask service" 12 | ansible.builtin.service: 13 | name: autofs 14 | state: stopped 15 | enabled: false 16 | masked: true 17 | when: debian11cis_autofs == "mask" 18 | when: 19 | - "'autofs' in ansible_facts.packages" 20 | - debian11cis_rule_1_1_9 21 | tags: 22 | - level1-server 23 | - level2-workstation 24 | - automated 25 | - patch 26 | - rule_1.1.9 27 | - automounting 28 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.2.1 | AUDIT | Ensure package manager repositories are configured" 4 | block: 5 | - name: "1.2.1 | AUDIT | Ensure package manager repositories are configured | Get repositories" 6 | ansible.builtin.shell: apt-cache policy 7 | changed_when: false 8 | failed_when: false 9 | check_mode: false 10 | register: discovered_apt_policy 11 | 12 | - name: "1.2.1 | AUDIT | Ensure package manager repositories are configured | Message out repository configs" 13 | ansible.builtin.debug: 14 | msg: 15 | - "Warning!! Below are the apt package repositories" 16 | - "Please review to make sure they conform to your sites policies" 17 | - "{{ discovered_apt_policy.stdout_lines }}" 18 | 19 | - name: "1.2.1 | WARN | Ensure package manager repositories are configured | warn_count" 20 | ansible.builtin.import_tasks: 21 | file: warning_facts.yml 22 | vars: 23 | warn_control_id: '1.2.1' 24 | when: debian11cis_rule_1_2_1 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - manual 29 | - audit 30 | - rule_1.2.1 31 | - apt 32 | 33 | - name: "1.2.2 | AUDIT | Ensure GPG keys are configured" 34 | block: 35 | - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | Get apt gpg keys" 36 | ansible.builtin.shell: apt-key list 37 | changed_when: false 38 | failed_when: false 39 | check_mode: false 40 | register: discovered_apt_gpgkeys 41 | 42 | - name: "1.2.2 | AUDIT | Ensure GPG keys are configured | Message out apt gpg keys" 43 | ansible.builtin.debug: 44 | msg: 45 | - "Warning!! Below are the apt gpg keys configured" 46 | - "Please review to make sure they are configured" 47 | - "in accordance with site policy" 48 | - "{{ discovered_apt_gpgkeys.stdout_lines }}" 49 | 50 | - name: "1.2.2 | WARN | Ensure GPG keys are configured | warn_count" 51 | ansible.builtin.import_tasks: 52 | file: warning_facts.yml 53 | vars: 54 | warn_control_id: '1.2.2' 55 | when: debian11cis_rule_1_2_2 56 | tags: 57 | - level1-server 58 | - level1-workstation 59 | - manual 60 | - audit 61 | - rule_1.2.2 62 | - gpg 63 | - keys 64 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.3.1 | PATCH | Ensure AIDE is installed" 4 | block: 5 | - name: "1.3.1 | PATCH | Ensure AIDE is installed" 6 | ansible.builtin.package: 7 | name: ['aide', 'aide-common'] 8 | state: present 9 | update_cache: true 10 | register: discovered_aide_installed 11 | when: 12 | - "'aide' not in ansible_facts.packages or 13 | 'aide-common' not in ansible_facts.packages" 14 | 15 | - name: "1.3.1 | PATCH | Ensure AIDE is installed | Recapture packages" 16 | ansible.builtin.package_facts: 17 | manager: auto 18 | when: discovered_aide_installed.skipped is not defined 19 | 20 | - name: "1.3.1 | PATCH | Ensure AIDE is installed | Configure AIDE" 21 | ansible.builtin.shell: aideinit && mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db 22 | args: 23 | creates: /var/lib/aide/aide.db 24 | changed_when: false 25 | failed_when: false 26 | async: "{{ debian11cis_aide_init.async }}" 27 | poll: "{{ debian11cis_aide_init.poll }}" 28 | when: not ansible_check_mode 29 | when: 30 | - debian11cis_rule_1_3_1 31 | - debian11cis_config_aide 32 | tags: 33 | - level1-server 34 | - level1-workstation 35 | - automated 36 | - patch 37 | - rule_1.3.1 38 | - aide 39 | 40 | - name: "1.3.2 | PATCH | Ensure filesystem integrity is regularly checked" 41 | ansible.builtin.cron: 42 | name: Run AIDE integrity check 43 | cron_file: "{{ debian11cis_aide_cron['cron_file'] }}" 44 | user: "{{ debian11cis_aide_cron['cron_user'] }}" 45 | minute: "{{ debian11cis_aide_cron['aide_minute'] | default('0') }}" 46 | hour: "{{ debian11cis_aide_cron['aide_hour'] | default('5') }}" 47 | day: "{{ debian11cis_aide_cron['aide_day'] | default('*') }}" 48 | month: "{{ debian11cis_aide_cron['aide_month'] | default('*') }}" 49 | weekday: "{{ debian11cis_aide_cron['aide_weekday'] | default('*') }}" 50 | job: "{{ debian11cis_aide_cron['aide_job'] }}" 51 | when: 52 | - debian11cis_config_aide 53 | - debian11cis_rule_1_3_2 54 | tags: 55 | - level1-server 56 | - level1-workstation 57 | - automated 58 | - patch 59 | - rule_1.3.2 60 | - cron 61 | - aide 62 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.4.1 | PATCH | Ensure bootloader password is set" 4 | block: 5 | - name: "1.4.1 | PATCH | Ensure bootloader password is set" 6 | ansible.builtin.template: 7 | src: etc/grub.d/00_user.j2 8 | dest: "{{ debian11cis_grub_user_file }}" 9 | owner: root 10 | group: root 11 | mode: 'u+x,go-w' 12 | notify: Grub update 13 | 14 | - name: "1.4.1 | PATCH | Ensure bootloader password is set | allow unrestricted boot" 15 | ansible.builtin.lineinfile: 16 | path: "/etc/grub.d/10_linux" 17 | regexp: '(^CLASS="--class gnu-linux --class gnu --class os).*"$' 18 | line: '\g<1> --unrestricted"' 19 | backrefs: true 20 | notify: Grub update 21 | when: not debian11cis_ask_passwd_to_boot 22 | when: 23 | - debian11cis_set_boot_pass 24 | - debian11cis_rule_1_4_1 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - automated 29 | - patch 30 | - rule_1.4.1 31 | - grub 32 | 33 | - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured" 34 | block: 35 | - name: "1.4.2 | AUDIT | Ensure permissions on bootloader config are configured | Check for Grub file" 36 | ansible.builtin.stat: 37 | path: "{{ debian11cis_grub_file }}" 38 | check_mode: false 39 | register: discovered_grub_cfg_status 40 | 41 | - name: "1.4.2 | PATCH | Ensure permissions on bootloader config are configured | Set permissions" 42 | ansible.builtin.file: 43 | path: "{{ debian11cis_grub_file }}" 44 | owner: root 45 | group: root 46 | mode: 'go-wx' 47 | when: 48 | - discovered_grub_cfg_status.stat.exists 49 | when: debian11cis_rule_1_4_2 50 | tags: 51 | - level1-server 52 | - level1-workstation 53 | - automated 54 | - patch 55 | - rule_1.4.2 56 | - grub 57 | 58 | - name: "1.4.3 | PATCH | Ensure authentication required for single user mode" 59 | ansible.builtin.user: 60 | name: "{{ debian11cis_grub_user }}" 61 | password: "{{ debian11cis_grub_user_passwd }}" 62 | when: 63 | - debian11cis_rule_1_4_3 64 | - debian11cis_set_grub_user_pass 65 | tags: 66 | - level1-server 67 | - level1-workstation 68 | - automated 69 | - patch 70 | - rule_1.4.3 71 | - passwd 72 | - grub 73 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.5.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.5.1 | PATCH | Ensure address space layout randomization (ASLR) is enabled | Set active kernel parameter" 4 | ansible.posix.sysctl: 5 | name: kernel.randomize_va_space 6 | value: '2' 7 | state: present 8 | reload: true 9 | sysctl_set: true 10 | sysctl_file: "{{ debian11cis_sysctl_kernel_file }}" 11 | ignoreerrors: true 12 | when: debian11cis_rule_1_5_1 13 | tags: 14 | - level1-server 15 | - level1-workstation 16 | - automated 17 | - patch 18 | - rule_1.5.1 19 | - aslr 20 | 21 | - name: "1.5.2 | PATCH | Ensure prelink is not installed" 22 | block: 23 | - name: "1.5.2 | PATCH | Ensure prelink is not installed | Restore binaries to normal" 24 | ansible.builtin.shell: prelink -ua 25 | changed_when: false 26 | failed_when: false 27 | 28 | - name: "1.5.2 | PATCH | Ensure prelink is not installed| Remove prelink package" 29 | ansible.builtin.package: 30 | name: prelink 31 | state: absent 32 | purge: "{{ debian11cis_purge_apt }}" 33 | when: 34 | - debian11cis_rule_1_5_2 35 | - "'prelink' in ansible_facts.packages" 36 | tags: 37 | - level1-server 38 | - level1-workstation 39 | - automated 40 | - patch 41 | - rule_1.5.2 42 | - prelink 43 | 44 | - name: "1.5.3 | PATCH | Ensure Automatic Error Reporting is not enabled" 45 | block: 46 | - name: "1.5.3 | PATCH | Ensure Automatic Error Reporting is not enabled | disable" 47 | ansible.builtin.lineinfile: 48 | path: /etc/default/apport 49 | regexp: ^enabled 50 | line: enabled=0 51 | create: true 52 | owner: root 53 | group: root 54 | mode: 'u-x,go-wx' 55 | 56 | - name: "1.5.3 | PATCH | Ensure Automatic Error Reporting is not enabled | remove package" 57 | ansible.builtin.package: 58 | name: apport 59 | state: absent 60 | notify: Purge_packages 61 | when: 62 | - "'apport' in ansible_facts.packages" 63 | when: debian11cis_rule_1_5_3 64 | tags: 65 | - level1-server 66 | - level1-workstation 67 | - automated 68 | - patch 69 | - rule_1.5.3 70 | - apport 71 | 72 | - name: "1.5.4 | PATCH | Ensure core dumps are restricted" 73 | block: 74 | - name: "1.5.4 | PATCH | Ensure core dumps are restricted | kernel sysctl" 75 | ansible.posix.sysctl: 76 | name: fs.suid_dumpable 77 | value: '0' 78 | state: present 79 | reload: true 80 | sysctl_set: true 81 | sysctl_file: "{{ debian11cis_sysctl_kernel_file }}" 82 | ignoreerrors: true 83 | 84 | - name: "1.5.4 | PATCH | Ensure core dumps are restricted | security limits" 85 | ansible.builtin.lineinfile: 86 | path: /etc/security/limits.d/99_zero_core.conf 87 | regexp: '^\* hard core' 88 | line: '* hard core 0' 89 | create: true 90 | owner: root 91 | group: root 92 | mode: 'u-x,go-wx' 93 | 94 | - name: "1.5.4 | PATCH | Ensure core dumps are restricted | sysctl.conf" 95 | ansible.builtin.lineinfile: 96 | path: /etc/sysctl.conf 97 | regexp: '^fs.suid_dumpable' 98 | line: fs.suid_dumpable=0 99 | owner: root 100 | group: root 101 | mode: 'u-x,go-wx' 102 | notify: Reload systemctl 103 | 104 | - name: "1.5.4 | PATCH | Ensure core dumps are restricted | coredump.conf" 105 | ansible.builtin.lineinfile: 106 | path: /etc/systemd/coredump.conf 107 | regexp: "{{ item.regexp }}" 108 | line: "{{ item.line }}" 109 | create: true 110 | owner: root 111 | group: root 112 | mode: 'u-x,go-wx' 113 | loop: 114 | - { regexp: '^Storage', line: 'Storage=none' } 115 | - { regexp: '^ProcessSizeMax', line: 'ProcessSizeMax=0' } 116 | when: "'systemd-coredump' in ansible_facts.packages" 117 | when: debian11cis_rule_1_5_4 118 | tags: 119 | - level1-server 120 | - level1-workstation 121 | - automated 122 | - patch 123 | - rule_1.5.4 124 | - coredump 125 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.6.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.6.1.1 | PATCH | Ensure AppArmor is installed" 4 | ansible.builtin.package: 5 | name: ['apparmor', 'apparmor-utils'] 6 | state: present 7 | when: 8 | - debian11cis_rule_1_6_1_1 9 | - "'apparmor' not in ansible_facts.packages or 10 | 'apparmor-utils' not in ansible_facts.packages" 11 | tags: 12 | - level1-server 13 | - level1-workstation 14 | - automated 15 | - patch 16 | - rule_1.6.1.1 17 | - apparmor 18 | 19 | - name: "1.6.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration" 20 | block: 21 | - name: "1.6.1.2 | AUDIT | Ensure AppArmor is enabled in the bootloader configuration | Get current settings" 22 | ansible.builtin.shell: grep "GRUB_CMDLINE_LINUX=" /etc/default/grub | cut -f2 -d'"' 23 | changed_when: false 24 | failed_when: false 25 | check_mode: false 26 | register: discovered_grub_cmdline_settings 27 | 28 | - name: "1.6.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration | Set apparmor settings if none exist" 29 | ansible.builtin.lineinfile: 30 | path: /etc/default/grub 31 | regexp: ^(GRUB_CMDLINE_LINUX=")(|apparmor=\d\s)(.*\w+") 32 | line: \1apparmor=1 \3 33 | backrefs: true 34 | notify: Grub update 35 | when: discovered_grub_cmdline_settings.stdout is not search('apparmor=') 36 | 37 | - name: "1.6.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration | Set security settings if none exist" 38 | ansible.builtin.lineinfile: 39 | path: /etc/default/grub 40 | regexp: ^(GRUB_CMDLINE_LINUX=")(|security=\w+\s)(.*\w+") 41 | line: \1security=apparmor \3 42 | backrefs: true 43 | notify: Grub update 44 | when: discovered_grub_cmdline_settings.stdout is not search('security=') 45 | 46 | - name: "1.6.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration | Set apparmor settings if none exist" 47 | ansible.builtin.lineinfile: 48 | path: /etc/default/grub 49 | regexp: '^GRUB_CMDLINE_LINUX=' 50 | line: 'GRUB_CMDLINE_LINUX="apparmor=1 security=apparmor {{ discovered_grub_cmdline_settings.stdout }}"' 51 | insertafter: '^GRUB_' 52 | when: 53 | - "'apparmor' not in discovered_grub_cmdline_settings.stdout" 54 | - "'security' not in discovered_grub_cmdline_settings.stdout" 55 | notify: Grub update 56 | 57 | - name: "1.6.1.2 | PATCH | Ensure AppArmor is enabled in the bootloader configuration | Replace apparmor settings when exists" 58 | ansible.builtin.replace: 59 | path: /etc/default/grub 60 | regexp: "{{ item.regexp }}" 61 | replace: "{{ item.replace }}" 62 | with_items: 63 | - { regexp: 'apparmor=\w+', replace: 'apparmor=1' } 64 | - { regexp: 'security=\w+', replace: 'security=apparmor' } 65 | when: 66 | - "'apparmor' in discovered_grub_cmdline_settings.stdout or 67 | 'security' in discovered_grub_cmdline_settings.stdout" 68 | notify: Grub update 69 | when: debian11cis_rule_1_6_1_2 70 | tags: 71 | - level1-server 72 | - level1-workstation 73 | - automated 74 | - patch 75 | - rule_1.6.1.2 76 | - apparmor 77 | 78 | # This is handled via this block to allow for proper flagging of idempotency for the control 79 | - name: "1.6.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode" 80 | block: 81 | - name: "1.6.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Get pre apply enforce count" 82 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 83 | changed_when: false 84 | failed_when: false 85 | register: discovered_apparmor_pre_count 86 | 87 | - name: "1.6.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Apply enforcing to /etc/apparmor.d profiles" 88 | ansible.builtin.shell: aa-enforce /etc/apparmor.d/* 89 | changed_when: false 90 | failed_when: false 91 | 92 | - name: "1.6.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | Get post apply enforce count" 93 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 94 | changed_when: false 95 | failed_when: false 96 | register: discovered_apparmor_pre_count 97 | 98 | - name: "1.6.1.3 | PATCH | Ensure all AppArmor Profiles are in enforce or complain mode | This flags for idempotency" 99 | ansible.builtin.debug: 100 | msg: Changed! The profiles in /etc/apparmor.d were set to enforcing 101 | changed_when: true 102 | when: discovered_apparmor_pre_count.stdout != discovered_apparmor_pre_count.stdout 103 | when: 104 | - debian11cis_rule_1_6_1_3 105 | - not debian11cis_apparmor_disable 106 | - not debian11cis_apparmor_enforce_only 107 | tags: 108 | - level1-server 109 | - level1-workstation 110 | - automated 111 | - patch 112 | - rule_1.6.1.3 113 | - apparmor 114 | 115 | - name: "1.6.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing" 116 | block: 117 | - name: "1.6.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Get pre apply enforce count" 118 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 119 | changed_when: false 120 | failed_when: false 121 | register: discovered_apparmor_pre_count 122 | 123 | - name: "1.6.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Apply enforcing to /etc/apparmor.d profiles" 124 | ansible.builtin.shell: aa-enforce /etc/apparmor.d/* 125 | changed_when: false 126 | failed_when: false 127 | 128 | - name: "1.6.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | Get post apply enforce count" 129 | ansible.builtin.shell: apparmor_status | grep "profiles are in enforce mode" | tr -d -c 0-9 130 | changed_when: false 131 | failed_when: false 132 | register: discovered_apparmor_pre_count 133 | 134 | - name: "1.6.1.4 | PATCH | Ensure all AppArmor Profiles are enforcing | This flags for idempotency" 135 | ansible.builtin.debug: 136 | msg: Changed! The profiles in /etc/apparmor.d were set to enforcing 137 | changed_when: true 138 | when: discovered_apparmor_pre_count.stdout != discovered_apparmor_pre_count.stdout 139 | when: 140 | - debian11cis_rule_1_6_1_4 141 | - not debian11cis_apparmor_disable 142 | - debian11cis_apparmor_enforce_only 143 | tags: 144 | - level2-server 145 | - level2-workstation 146 | - automated 147 | - scored 148 | - patch 149 | - rule_1.6.1.4 150 | - apparmor 151 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.7.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.7.1 | PATCH | Ensure message of the day is configured properly" 4 | block: 5 | - name: "1.7.1 | PATCH | Ensure message of the day is configured properly | motd" 6 | ansible.builtin.template: 7 | src: etc/motd.j2 8 | dest: /etc/motd 9 | 10 | - name: "1.7.1 | PATCH | Ensure message of the day is configured properly | disable dynamic_motd" 11 | ansible.builtin.lineinfile: 12 | path: /etc/pam.d/sshd 13 | regexp: "{{ item.regexp }}" 14 | line: "{{ item.line }}" 15 | backrefs: true 16 | loop: 17 | - { regexp: '(session\s+optional\s+pam_motd.so\s+motd=/run/motd.dynamic)', line: '# \1' } 18 | - { regexp: '(session\s+optional\s+pam_motd.so noupdate)', line: '# \1' } 19 | - { regexp: '# Pam_motd.so disabled for CIS benchmark', line: '# Pam_motd.so disabled for CIS benchmark' } 20 | when: debian11cis_disable_dynamic_motd 21 | when: debian11cis_rule_1_7_1 22 | tags: 23 | - level1-server 24 | - level1-workstation 25 | - automated 26 | - patch 27 | - rule_1.7.1 28 | - motd 29 | 30 | - name: "1.7.2 | PATCH | Ensure local login warning banner is configured properly" 31 | ansible.builtin.template: 32 | src: etc/issue.j2 33 | dest: /etc/issue 34 | when: debian11cis_rule_1_7_2 35 | tags: 36 | - level1-server 37 | - level1-workstation 38 | - automated 39 | - patch 40 | - rule_1.7.2 41 | - banner 42 | 43 | - name: "1.7.3 | PATCH | Ensure remote login warning banner is configured properly" 44 | ansible.builtin.template: 45 | src: etc/issue.net.j2 46 | dest: /etc/issue.net 47 | when: debian11cis_rule_1_7_3 48 | tags: 49 | - level1-server 50 | - level1-workstation 51 | - automated 52 | - patch 53 | - rule_1.7.3 54 | - banner 55 | 56 | - name: "1.7.4 | PATCH | Ensure permissions on /etc/motd are configured" 57 | ansible.builtin.file: 58 | path: /etc/motd 59 | owner: root 60 | group: root 61 | mode: 'u-x,go-wx' 62 | when: debian11cis_rule_1_7_4 63 | tags: 64 | - level1-server 65 | - level1-workstation 66 | - automated 67 | - patch 68 | - rule_1.7.4 69 | - permissions 70 | - motd 71 | 72 | - name: "1.7.5 | PATCH | Ensure permissions on /etc/issue are configured" 73 | ansible.builtin.file: 74 | path: /etc/issue 75 | owner: root 76 | group: root 77 | mode: 'u-x,go-wx' 78 | when: debian11cis_rule_1_7_5 79 | tags: 80 | - level1-server 81 | - level1-workstation 82 | - automated 83 | - patch 84 | - rule_1.7.5 85 | - permissions 86 | - banner 87 | 88 | - name: "1.7.6 | PATCH | Ensure permissions on /etc/issue.net are configured" 89 | ansible.builtin.file: 90 | path: /etc/issue.net 91 | owner: root 92 | group: root 93 | mode: 'u-x,go-wx' 94 | when: debian11cis_rule_1_7_6 95 | tags: 96 | - level1-server 97 | - level1-workstation 98 | - automated 99 | - patch 100 | - rule_1.7.6 101 | - permissions 102 | - banner 103 | -------------------------------------------------------------------------------- /tasks/section_1/cis_1.9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "1.9 | PATCH | Ensure updates, patches, and additional security software are installed" 4 | ansible.builtin.package: 5 | name: "*" 6 | state: latest 7 | when: debian11cis_rule_1_9 8 | tags: 9 | - level1-server 10 | - level1-workstation 11 | - manual 12 | - patch 13 | - rule_1.9 14 | - patch 15 | -------------------------------------------------------------------------------- /tasks/section_1/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 1.1.1 | Disable Unused Filesystems" 4 | ansible.builtin.import_tasks: 5 | file: cis_1.1.1.x.yml 6 | 7 | - name: "SECTION | 1.1.2 | configure /tmp" 8 | ansible.builtin.import_tasks: 9 | file: cis_1.1.2.x.yml 10 | when: not system_is_container 11 | 12 | - name: "SECTION | 1.1.3 | configure /var" 13 | ansible.builtin.import_tasks: 14 | file: cis_1.1.3.x.yml 15 | when: not system_is_container 16 | 17 | - name: "SECTION | 1.1.4 | configure /var/tmp" 18 | ansible.builtin.import_tasks: 19 | file: cis_1.1.4.x.yml 20 | when: not system_is_container 21 | 22 | - name: "SECTION | 1.1.5 | configure /var/log" 23 | ansible.builtin.import_tasks: 24 | file: cis_1.1.5.x.yml 25 | when: not system_is_container 26 | 27 | - name: "SECTION | 1.1.6 | configure /var/log/audit" 28 | ansible.builtin.import_tasks: 29 | file: cis_1.1.6.x.yml 30 | when: not system_is_container 31 | 32 | - name: "SECTION | 1.1.7 | configure /home" 33 | ansible.builtin.import_tasks: 34 | file: cis_1.1.7.x.yml 35 | when: not system_is_container 36 | 37 | - name: "SECTION | 1.1.8 | configure /dev/shm" 38 | ansible.builtin.import_tasks: 39 | file: cis_1.1.8.x.yml 40 | when: not system_is_container 41 | 42 | - name: "SECTION | 1.1.9 | configure software updates" 43 | ansible.builtin.import_tasks: 44 | file: cis_1.1.9.yml 45 | when: not system_is_container 46 | 47 | - name: "SECTION | 1.1.10 | Disable USB storage" 48 | ansible.builtin.import_tasks: 49 | file: cis_1.1.10.yml 50 | when: not system_is_container 51 | 52 | - name: "SECTION | 1.2 | Configure Software Updates" 53 | ansible.builtin.import_tasks: 54 | file: cis_1.2.x.yml 55 | when: not system_is_container 56 | 57 | - name: "SECTION | 1.3. | Filesystem Integrity Checking" 58 | ansible.builtin.import_tasks: 59 | file: cis_1.3.x.yml 60 | 61 | - name: "SECTION | 1.4 | Secure Boot Settings" 62 | ansible.builtin.import_tasks: 63 | file: cis_1.4.x.yml 64 | 65 | - name: "SECTION | 1.5 | Additional Process Hardening" 66 | ansible.builtin.import_tasks: 67 | file: cis_1.5.x.yml 68 | 69 | - name: "SECTION | 1.6 | Mandatory Access Control" 70 | ansible.builtin.import_tasks: 71 | file: cis_1.6.x.yml 72 | 73 | - name: "SECTION | 1.7 | Command Line Warning Banners" 74 | ansible.builtin.import_tasks: 75 | file: cis_1.7.x.yml 76 | 77 | - name: "SECTION | 1.8 | GNOME Display Manager" 78 | ansible.builtin.import_tasks: 79 | file: cis_1.8.x.yml 80 | when: 81 | - "'gdm3' in ansible_facts.packages" 82 | - not system_is_container 83 | 84 | - name: "SECTION | 1.9 | Ensure updates, patches, and additional security software are installed" 85 | ansible.builtin.import_tasks: 86 | file: cis_1.9.yml 87 | when: not system_is_container 88 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.1.1.1 | PATCH | Ensure a single time synchronization daemon is in use" 4 | block: 5 | - name: "2.1.1.1 | PATCH | Ensure a single time synchronization daemon is in use | Pkg installed" 6 | ansible.builtin.package: 7 | name: "{{ debian11cis_time_sync_tool }}" 8 | state: present 9 | 10 | - name: "2.1.1.1 | PATCH | Ensure a single time synchronization daemon is in use | other pkgs removed" 11 | ansible.builtin.package: 12 | name: "{{ item }}" 13 | state: absent 14 | loop: 15 | - chrony 16 | - ntp 17 | when: item != debian11cis_time_sync_tool 18 | 19 | - name: "2.1.1.1 | PATCH | Ensure a single time synchronization daemon is in use | mask service" 20 | ansible.builtin.service: 21 | name: systemd-timesyncd.service 22 | state: stopped 23 | enabled: false 24 | masked: true 25 | daemon_reload: true 26 | when: 27 | - "'systemd-timesyncd' not in debian11cis_time_sync_tool" 28 | - "'systemd-timesyncd' in ansible_facts.packages" 29 | when: debian11cis_rule_2_1_1_1 30 | tags: 31 | - level1-server 32 | - level1-workstation 33 | - automated 34 | - patch 35 | - rule_2.1.1.1 36 | - chrony 37 | - ntp 38 | - systemd-timesyncd 39 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.1.2.1 | PATCH | Ensure chrony is configured with authorized timeserver" 4 | block: 5 | - name: "2.1.2.1 | PATCH | Ensure chrony is configured with authorized timeserver | sources" 6 | ansible.builtin.template: 7 | src: "{{ item }}.j2" 8 | dest: "/{{ item }}" 9 | mode: 'u-x,go-wx' 10 | owner: root 11 | group: root 12 | loop: 13 | - etc/chrony/sources.d/pool.sources 14 | - etc/chrony/sources.d/server.sources 15 | notify: Restart timeservice 16 | 17 | - name: "2.1.2.1 | PATCH | Ensure chrony is configured with authorized timeserver | load sources" 18 | ansible.builtin.lineinfile: 19 | path: /etc/chrony/chrony.conf 20 | regexp: '^sourcedir /etc/chrony/sources.d' 21 | line: sourcedir /etc/chrony/sources.d 22 | notify: Restart timeservice 23 | when: debian11cis_rule_2_1_2_1 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - patch 28 | - rule_2.1.2.1 29 | - chrony 30 | 31 | - name: "2.1.2.2 | PATCH | Ensure chrony is running as user _chrony" 32 | ansible.builtin.lineinfile: 33 | path: /etc/chrony/chrony.conf 34 | regexp: '^user _chrony' 35 | line: 'user _chrony' 36 | when: debian11cis_rule_2_1_2_2 37 | tags: 38 | - level1-server 39 | - level1-workstation 40 | - patch 41 | - rule_2.1.2.2 42 | - chrony 43 | 44 | - name: "2.1.2.3 | PATCH | Ensure chrony is enabled and running" 45 | ansible.builtin.systemd: 46 | name: chrony 47 | state: started 48 | enabled: true 49 | when: debian11cis_rule_2_1_2_3 50 | tags: 51 | - level1-server 52 | - level1-workstation 53 | - rule_2.1.2.3 54 | - chrony 55 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.1.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.1.3.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver" 4 | block: 5 | - name: "2.1.3.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver | create conf.d dir" 6 | ansible.builtin.file: 7 | path: /etc/systemd/timesyncd.conf.d 8 | owner: root 9 | group: root 10 | mode: 'u+x,go-w' 11 | state: directory 12 | 13 | - name: "2.1.3.1 | PATCH | Ensure systemd-timesyncd configured with authorized timeserver | sources" 14 | ansible.builtin.template: 15 | src: "{{ item }}.j2" 16 | dest: "/{{ item }}" 17 | mode: 'u-x,go-wx' 18 | owner: root 19 | group: root 20 | loop: 21 | - "etc/systemd/timesyncd.conf.d/50-timesyncd.conf" 22 | notify: Restart timeservice 23 | when: debian11cis_rule_2_1_3_1 24 | tags: 25 | - level1-server 26 | - level1-workstation 27 | - patch 28 | - rule_2.1.3.1 29 | - timesyncd 30 | 31 | - name: "2.1.3.2 | PATCH | Ensure systemd-timesyncd is enabled and running" 32 | ansible.builtin.systemd: 33 | name: systemd-timesyncd 34 | state: started 35 | enabled: true 36 | masked: false 37 | when: debian11cis_rule_2_1_3_2 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - rule_2.1.3.2 42 | - timesyncd 43 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.1.4.1 | PATCH | Ensure ntp access control is configured " 4 | ansible.builtin.lineinfile: 5 | path: /etc/ntp.conf 6 | regexp: '^(restrict) (|{{ item }}) .*$' 7 | line: 'restrict {{ item }} default kod nomodify notrap nopeer noquery' 8 | loop: 9 | - '-4' 10 | - '-6' 11 | notify: Restart timeservice 12 | when: debian11cis_rule_2_1_4_1 13 | tags: 14 | - level1-server 15 | - level1-workstation 16 | - patch 17 | - rule_2.1.4.1 18 | - ntp 19 | 20 | - name: "2.1.4.2 | PATCH | Ensure ntp is configured with authorized timeserver" 21 | block: 22 | - name: "2.1.4.2 | PATCH | Ensure ntp is configured with authorized timeserver | pool" 23 | ansible.builtin.lineinfile: 24 | path: /etc/ntp.conf 25 | regexp: '^pool.*' 26 | line: 'pool {{ item.name }} {{ item.options }}' 27 | notify: Restart timeservice 28 | loop: "{{ debian11cis_time_pool }}" 29 | loop_control: 30 | label: "{{ item.name }}" 31 | 32 | - name: "2.1.4.2 | PATCH | Ensure ntp is configured with authorized timeserver | servers" 33 | ansible.builtin.lineinfile: 34 | path: /etc/ntp.conf 35 | insertafter: '^server' 36 | line: 'server {{ item.name }} {{ item.options }}' 37 | loop: "{{ debian11cis_time_servers }}" 38 | loop_control: 39 | label: "{{ item.name }}" 40 | notify: Restart timeservice 41 | when: debian11cis_rule_2_1_4_2 42 | tags: 43 | - level1-server 44 | - level1-workstation 45 | - patch 46 | - rule_2.1.4.2 47 | - ntp 48 | 49 | - name: "2.1.4.3 | PATCH | Ensure ntp is running as user ntp" 50 | ansible.builtin.lineinfile: 51 | path: /etc/init.d/ntp 52 | regexp: '^RUNASUSER.*' 53 | line: 'RUNASUSER=ntp' 54 | notify: Restart timeservice 55 | when: debian11cis_rule_2_1_4_3 56 | tags: 57 | - level1-server 58 | - level1-workstation 59 | - patch 60 | - rule_2.1.4.3 61 | - ntp 62 | 63 | - name: "2.1.4.4 | PATCH | Ensure ntp is enabled and running" 64 | ansible.builtin.systemd: 65 | name: ntp 66 | state: started 67 | enabled: true 68 | masked: false 69 | when: debian11cis_rule_2_1_4_4 70 | tags: 71 | - level1-server 72 | - level1-workstation 73 | - rule_2.1.4.4 74 | - ntp 75 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.3.1 | PATCH | Ensure NIS Client is not installed" 4 | ansible.builtin.package: 5 | name: nis 6 | state: absent 7 | purge: "{{ debian11cis_purge_apt }}" 8 | when: 9 | - debian11cis_rule_2_3_1 10 | - not debian11cis_nis_required 11 | tags: 12 | - level1-server 13 | - level1-workstation 14 | - rule_2.3.1 15 | - nis 16 | 17 | - name: "2.3.2 | PATCH | Ensure rsh client is not installed" 18 | ansible.builtin.package: 19 | name: rsh-client 20 | state: absent 21 | purge: "{{ debian11cis_purge_apt }}" 22 | when: 23 | - debian11cis_rule_2_3_2 24 | - not debian11cis_rsh_required 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - automated 29 | - patch 30 | - rule_2.3.2 31 | - rsh 32 | 33 | - name: "2.3.3 | PATCH | Ensure talk client is not installed" 34 | ansible.builtin.package: 35 | name: talk 36 | state: absent 37 | purge: "{{ debian11cis_purge_apt }}" 38 | when: 39 | - debian11cis_rule_2_3_3 40 | - not debian11cis_talk_required 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - automated 45 | - patch 46 | - rule_2.3.3 47 | - talk 48 | 49 | - name: "2.3.4 | PATCH | Ensure telnet client is not installed" 50 | ansible.builtin.package: 51 | name: telnet 52 | state: absent 53 | purge: "{{ debian11cis_purge_apt }}" 54 | when: 55 | - debian11cis_rule_2_3_4 56 | - not debian11cis_telnet_required 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - automated 61 | - patch 62 | - rule_2.3.4 63 | - telnet 64 | 65 | - name: "2.3.5 | PATCH | Ensure LDAP client is not installed" 66 | ansible.builtin.package: 67 | name: ldap-utils 68 | state: absent 69 | purge: "{{ debian11cis_purge_apt }}" 70 | when: 71 | - debian11cis_rule_2_3_5 72 | - not debian11cis_ldap_clients_required 73 | tags: 74 | - level1-server 75 | - level1-workstation 76 | - automated 77 | - patch 78 | - rule_2.3.5 79 | - ldap 80 | 81 | - name: "2.3.6 | PATCH | Ensure RPC is not installed" 82 | ansible.builtin.package: 83 | name: rpcbind 84 | state: absent 85 | purge: "{{ debian11cis_purge_apt }}" 86 | when: 87 | - debian11cis_rule_2_3_6 88 | - not debian11cis_rpc_required 89 | - not debian11cis_nfs_server # dependancy breaks 90 | - not debian11cis_nfs_client # dependancy breaks 91 | tags: 92 | - level1-server 93 | - level1-workstation 94 | - automated 95 | - patch 96 | - rule_2.3.6 97 | - rpbc 98 | -------------------------------------------------------------------------------- /tasks/section_2/cis_2.4.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked" 4 | block: 5 | - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Check for services" 6 | ansible.builtin.shell: lsof -i -P -n | grep -v "(ESTABLISHED)" 7 | changed_when: false 8 | failed_when: false 9 | check_mode: false 10 | register: discovered_listening_services 11 | 12 | - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Message out running services" 13 | ansible.builtin.debug: 14 | msg: 15 | - "Warning!! Below are the running services. Please review and remove as well as mask un-needed services" 16 | - "{{ discovered_listening_services.stdout_lines }}" 17 | when: discovered_listening_services.stdout | length > 0 18 | 19 | - name: "2.4 | AUDIT | Ensure nonessential services are removed or masked | Set warning count" 20 | ansible.builtin.import_tasks: 21 | file: warning_facts.yml 22 | when: discovered_listening_services.stdout | length > 0 23 | vars: 24 | warn_control_id: '2.4' 25 | when: debian11cis_rule_2_4 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - manual 30 | - audit 31 | - rule_2.4 32 | - services 33 | -------------------------------------------------------------------------------- /tasks/section_2/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 2.1.1.x | time service " 4 | ansible.builtin.import_tasks: 5 | file: cis_2.1.1.x.yml 6 | 7 | - name: "SECTION | 2.1.2.x | chrony time service" 8 | ansible.builtin.import_tasks: 9 | file: cis_2.1.2.x.yml 10 | when: 11 | - debian11cis_time_sync_tool == "chrony" 12 | 13 | - name: "SECTION | 2.1.3.x | systemd-timesyncd time service" 14 | ansible.builtin.import_tasks: 15 | file: cis_2.1.3.x.yml 16 | when: 17 | - debian11cis_time_sync_tool == "systemd-timesyncd" 18 | 19 | - name: "SECTION | 2.1.4.x | ntp time service " 20 | ansible.builtin.import_tasks: 21 | file: cis_2.1.4.x.yml 22 | when: 23 | - debian11cis_time_sync_tool == "ntp" 24 | 25 | - name: "SECTION | 2.1.x | Services" 26 | ansible.builtin.import_tasks: 27 | file: cis_2.2.x.yml 28 | 29 | - name: "SECTION | 2.2.x | Service Clients" 30 | ansible.builtin.import_tasks: 31 | file: cis_2.3.x.yml 32 | 33 | - name: "SECTION | 2.3.x | Ensure nonessential services are removed or masked" 34 | ansible.builtin.import_tasks: 35 | file: cis_2.4.yml 36 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.1.1 | PATCH | Ensure system is checked to determine if IPv6 is enabled" 4 | block: 5 | - name: "3.1.1 | PATCH | Ensure system is checked to determine if IPv6 is enabled | Replace ipv6.disable if it exists" 6 | ansible.builtin.replace: 7 | path: /etc/default/grub 8 | regexp: '^(GRUB_CMDLINE_LINUX=.*)\bipv6\.disable=\d\b(.*$)' 9 | replace: '\1ipv6.disable=1\2' 10 | when: debian11cis_ipv6_disable == 'grub' 11 | register: discovered_ipv6disable_replaced 12 | notify: Grub update 13 | 14 | - name: "3.1.1 | PATCH | Ensure system is checked to determine if IPv6 is enabled | Check grub cmdline linux" 15 | ansible.builtin.shell: grep "GRUB_CMDLINE_LINUX=" /etc/default/grub | cut -f2 -d'"' 16 | changed_when: false 17 | failed_when: false 18 | check_mode: false 19 | register: discovered_grub_cmdline_settings 20 | when: debian11cis_ipv6_disable == 'grub' 21 | 22 | - name: "3.1.1 | PATCH | Ensure system is checked to determine if IPv6 is enabled | Insert ipv6.disable if it doesn't exist" 23 | ansible.builtin.lineinfile: 24 | path: /etc/default/grub 25 | regexp: '^(GRUB_CMDLINE_LINUX=".*)"$' 26 | line: '\1 ipv6.disable=1"' 27 | backrefs: true 28 | when: 29 | - debian11cis_ipv6_disable == 'grub' 30 | - discovered_ipv6disable_replaced is not changed 31 | - "'ipv6.disable' not in discovered_grub_cmdline_settings.stdout" 32 | notify: Grub update 33 | 34 | - name: "3.1.1 | PATCH | Ensure system is checked to determine if IPv6 is enabled | Remove net.ipv6.conf.all.disable_ipv6" 35 | ansible.builtin.template: 36 | src: "{{ item }}.j2" 37 | dest: "/{{ item }}" 38 | owner: root 39 | group: root 40 | mode: 'u-x,g-wx,o-rwx' 41 | notify: Flush ipv6 route table 42 | loop: 43 | - etc/sysctl.d/60-disable_ipv6.conf 44 | when: debian11cis_ipv6_disable == 'sysctl' 45 | when: 46 | - debian11cis_rule_3_1_1 47 | - not debian11cis_ipv6_required 48 | tags: 49 | - level1-server 50 | - level1-workstation 51 | - patch 52 | - rule_3.1.1 53 | - ipv6 54 | 55 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled" 56 | block: 57 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Check for network-manager tool" 58 | ansible.builtin.shell: nmcli radio wifi 59 | changed_when: false 60 | failed_when: false 61 | check_mode: false 62 | register: discovered_wifi_status 63 | when: "'network-manager' in ansible_facts.packages" 64 | 65 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Disable wireless if network-manager installed" 66 | ansible.builtin.shell: nmcli radio all off 67 | changed_when: discovered_nmcli_radio_off.rc == 0 68 | register: discovered_nmcli_radio_off 69 | when: 70 | - "'network-manager' in ansible_facts.packages" 71 | - "'enabled' in discovered_wifi_status.stdout" 72 | 73 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Warn about wireless if network-manager not installed" 74 | ansible.builtin.debug: 75 | msg: "Warning!! You need to disable wireless interfaces manually since network-manager is not installed" 76 | when: "'network-manager' not in ansible_facts.packages" 77 | 78 | - name: "3.1.2 | PATCH | Ensure wireless interfaces are disabled | Set warning count" 79 | ansible.builtin.import_tasks: 80 | file: warning_facts.yml 81 | when: "'network-manager' not in ansible_facts.packages" 82 | vars: 83 | warn_control_id: '3.1.2' 84 | when: debian11cis_rule_3_1_2 85 | tags: 86 | - level1-server 87 | - level2-workstation 88 | - automated 89 | - patch 90 | - rule_3.1.2 91 | - wireless 92 | 93 | - name: "3.1.3 | PATCH | Ensure DCCP is disabled" 94 | ansible.builtin.lineinfile: 95 | path: /etc/modprobe.d/dccp.conf 96 | regexp: '^(#)?install dccp(\\s|$)' 97 | line: "{{ item }}" 98 | create: true 99 | loop: 100 | - install dccp /bin/true 101 | - blacklist dccp 102 | when: debian11cis_rule_3_1_3 103 | tags: 104 | - level2-server 105 | - level2-workstation 106 | - automated 107 | - patch 108 | - rule_3.1.3 109 | - dccp 110 | 111 | - name: "3.1.4 | PATCH | Ensure SCTP is disabled" 112 | ansible.builtin.lineinfile: 113 | path: /etc/modprobe.d/sctp.conf 114 | regexp: '^(#)?install sctp(\\s|$)' 115 | line: "{{ item }}" 116 | create: true 117 | loop: 118 | - install sctp /bin/true 119 | - blacklist sctp 120 | when: debian11cis_rule_3_1_4 121 | tags: 122 | - level2-server 123 | - level2-workstation 124 | - automated 125 | - patch 126 | - rule_3.1.4 127 | - sctp 128 | 129 | - name: "3.1.5 | PATCH | Ensure RDS is disabled" 130 | ansible.builtin.lineinfile: 131 | path: /etc/modprobe.d/rds.conf 132 | regexp: '^(#)?install rds(\\s|$)' 133 | line: "{{ item }}" 134 | create: true 135 | loop: 136 | - install rds /bin/true 137 | - blacklist rds 138 | when: debian11cis_rule_3_1_5 139 | tags: 140 | - level2-server 141 | - level2-workstation 142 | - automated 143 | - patch 144 | - rule_3.1.5 145 | - rds 146 | 147 | - name: "3.1.6 | PATCH | Ensure TIPC is disabled" 148 | ansible.builtin.lineinfile: 149 | path: /etc/modprobe.d/tipc.conf 150 | regexp: '^(#)?install tipc(\\s|$)' 151 | line: "{{ item }}" 152 | create: true 153 | loop: 154 | - install tipc /bin/true 155 | - blacklist tipc 156 | when: debian11cis_rule_3_1_6 157 | tags: 158 | - level2-server 159 | - level2-workstation 160 | - automated 161 | - patch 162 | - rule_3.1.6 163 | - tipc 164 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.2.1 | PATCH | Ensure packet redirect sending is disabled" 4 | ansible.posix.sysctl: 5 | name: "{{ item }}" 6 | value: '0' 7 | sysctl_set: true 8 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 9 | state: present 10 | reload: true 11 | ignoreerrors: true 12 | with_items: 13 | - net.ipv4.conf.all.send_redirects 14 | - net.ipv4.conf.default.send_redirects 15 | notify: Flush ipv4 route table 16 | when: 17 | - debian11cis_rule_3_2_1 18 | - not debian11cis_is_router 19 | tags: 20 | - level1-server 21 | - level1-workstation 22 | - automated 23 | - patch 24 | - rule_3.2.1 25 | - packet_redirect 26 | - sysctl 27 | 28 | - name: "3.2.2 | PATCH | Ensure IP forwarding is disabled" 29 | block: 30 | - name: "3.2.2 | PATCH | Ensure IP forwarding is disabled | IPv4 settings" 31 | ansible.posix.sysctl: 32 | name: net.ipv4.ip_forward 33 | value: '0' 34 | sysctl_set: true 35 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 36 | state: present 37 | reload: true 38 | ignoreerrors: true 39 | notify: 40 | - Flush ipv4 route table 41 | 42 | - name: "3.2.2 | PATCH | Ensure IP forwarding is disabled | IPv6 settings" 43 | ansible.posix.sysctl: 44 | name: net.ipv6.conf.all.forwarding 45 | value: '0' 46 | sysctl_set: true 47 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 48 | state: present 49 | reload: true 50 | ignoreerrors: true 51 | when: debian11cis_ipv6_disable == 'sysctl' 52 | notify: 53 | - Flush ipv6 route table 54 | when: 55 | - debian11cis_rule_3_2_2 56 | - not debian11cis_is_router 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - automated 61 | - patch 62 | - rule_3.2.2 63 | - ip_forwarding 64 | - sysctl 65 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted" 4 | block: 5 | - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv4 settings" 6 | ansible.posix.sysctl: 7 | name: "{{ item }}" 8 | value: '0' 9 | sysctl_set: true 10 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 11 | state: present 12 | reload: true 13 | ignoreerrors: true 14 | with_items: 15 | - net.ipv4.conf.all.accept_source_route 16 | - net.ipv4.conf.default.accept_source_route 17 | notify: Flush ipv4 route table 18 | 19 | - name: "3.3.1 | PATCH | Ensure source routed packets are not accepted | IPv6 settings" 20 | ansible.posix.sysctl: 21 | name: "{{ item }}" 22 | value: '0' 23 | sysctl_set: true 24 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 25 | state: present 26 | reload: true 27 | ignoreerrors: true 28 | when: 29 | - debian11cis_ipv6_required 30 | - debian11cis_ipv6_disable == 'sysctl' 31 | with_items: 32 | - net.ipv6.conf.all.accept_source_route 33 | - net.ipv6.conf.default.accept_source_route 34 | notify: Flush ipv6 route table 35 | when: 36 | - debian11cis_rule_3_3_1 37 | - not debian11cis_is_router 38 | tags: 39 | - level1-server 40 | - level1-workstation 41 | - automated 42 | - patch 43 | - rule_3.3.1 44 | - routed_packets 45 | - sysctl 46 | 47 | - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted" 48 | block: 49 | - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv4 settings" 50 | ansible.posix.sysctl: 51 | name: "{{ item }}" 52 | value: '0' 53 | sysctl_set: true 54 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 55 | state: present 56 | reload: true 57 | ignoreerrors: true 58 | with_items: 59 | - net.ipv4.conf.all.accept_redirects 60 | - net.ipv4.conf.default.accept_redirects 61 | notify: Flush ipv4 route table 62 | 63 | - name: "3.3.2 | PATCH | Ensure ICMP redirects are not accepted | IPv6 settings" 64 | ansible.posix.sysctl: 65 | name: "{{ item }}" 66 | value: '0' 67 | sysctl_set: true 68 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 69 | state: present 70 | reload: true 71 | ignoreerrors: true 72 | when: 73 | - debian11cis_ipv6_required 74 | - debian11cis_ipv6_disable == 'sysctl' 75 | with_items: 76 | - net.ipv6.conf.all.accept_redirects 77 | - net.ipv6.conf.default.accept_redirects 78 | notify: Flush ipv6 route table 79 | when: debian11cis_rule_3_3_2 80 | tags: 81 | - level1-server 82 | - level1-workstation 83 | - automated 84 | - patch 85 | - rule_3.3.2 86 | - icmp 87 | - sysctl 88 | 89 | - name: "3.3.3 | PATCH | Ensure secure ICMP redirects are not accepted" 90 | ansible.posix.sysctl: 91 | name: "{{ item }}" 92 | value: '0' 93 | sysctl_set: true 94 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 95 | state: present 96 | reload: true 97 | ignoreerrors: true 98 | with_items: 99 | - net.ipv4.conf.all.secure_redirects 100 | - net.ipv4.conf.default.secure_redirects 101 | notify: Flush ipv4 route table 102 | when: debian11cis_rule_3_3_3 103 | tags: 104 | - level1-server 105 | - level1-workstation 106 | - automated 107 | - patch 108 | - rule_3.3.3 109 | - icmp 110 | - sysctl 111 | 112 | - name: "3.3.4 | PATCH | Ensure suspicious packets are logged" 113 | ansible.posix.sysctl: 114 | name: "{{ item }}" 115 | value: '1' 116 | sysctl_set: true 117 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 118 | state: present 119 | reload: true 120 | ignoreerrors: true 121 | with_items: 122 | - net.ipv4.conf.all.log_martians 123 | - net.ipv4.conf.default.log_martians 124 | notify: Flush ipv4 route table 125 | when: debian11cis_rule_3_3_4 126 | tags: 127 | - level1-server 128 | - level1-workstation 129 | - automated 130 | - patch 131 | - rule_3.3.4 132 | - suspicious_packets 133 | - sysctl 134 | 135 | - name: "3.3.5 | PATCH | Ensure broadcast ICMP requests are ignored" 136 | ansible.posix.sysctl: 137 | name: net.ipv4.icmp_echo_ignore_broadcasts 138 | value: '1' 139 | sysctl_set: true 140 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 141 | state: present 142 | reload: true 143 | ignoreerrors: true 144 | notify: Flush ipv4 route table 145 | when: debian11cis_rule_3_3_5 146 | tags: 147 | - level1-server 148 | - level1-workstation 149 | - automated 150 | - patch 151 | - rule_3.3.5 152 | - icmp 153 | - sysctl 154 | 155 | - name: "3.3.6 | PATCH | Ensure bogus ICMP responses are ignored" 156 | ansible.posix.sysctl: 157 | name: net.ipv4.icmp_ignore_bogus_error_responses 158 | value: '1' 159 | sysctl_set: true 160 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 161 | state: present 162 | reload: true 163 | ignoreerrors: true 164 | notify: Flush ipv4 route table 165 | when: debian11cis_rule_3_3_6 166 | tags: 167 | - level1-server 168 | - level1-workstation 169 | - automated 170 | - patch 171 | - rule_3.3.6 172 | - icmp 173 | - sysctl 174 | 175 | - name: "3.3.7 | PATCH | Ensure Reverse Path Filtering is enabled" 176 | ansible.posix.sysctl: 177 | name: "{{ item }}" 178 | value: '1' 179 | sysctl_set: true 180 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 181 | state: present 182 | reload: true 183 | ignoreerrors: true 184 | with_items: 185 | - net.ipv4.conf.all.rp_filter 186 | - net.ipv4.conf.default.rp_filter 187 | notify: Flush ipv4 route table 188 | when: debian11cis_rule_3_3_7 189 | tags: 190 | - level1-server 191 | - level1-workstation 192 | - automated 193 | - patch 194 | - rule_3.3.7 195 | - reverse_path_filtering 196 | - sysctl 197 | 198 | - name: "3.3.8 | PATCH | Ensure TCP SYN Cookies is enabled" 199 | ansible.posix.sysctl: 200 | name: net.ipv4.tcp_syncookies 201 | value: '1' 202 | sysctl_set: true 203 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 204 | state: present 205 | reload: true 206 | ignoreerrors: true 207 | notify: Flush ipv4 route table 208 | when: debian11cis_rule_3_3_8 209 | tags: 210 | - level1-server 211 | - level1-workstation 212 | - automated 213 | - patch 214 | - rule_3.3.8 215 | - tcp_syn_cookies 216 | - sysctl 217 | 218 | - name: "3.3.9 | PATCH | Ensure IPv6 router advertisements are not accepted" 219 | ansible.posix.sysctl: 220 | name: "{{ item }}" 221 | value: '0' 222 | sysctl_set: true 223 | sysctl_file: "{{ debian11cis_sysctl_network_conf }}" 224 | state: present 225 | reload: true 226 | ignoreerrors: true 227 | with_items: 228 | - net.ipv6.conf.all.accept_ra 229 | - net.ipv6.conf.default.accept_ra 230 | notify: Flush ipv6 route table 231 | when: 232 | - debian11cis_ipv6_required 233 | - debian11cis_rule_3_3_9 234 | tags: 235 | - level1-server 236 | - level1-workstation 237 | - automated 238 | - patch 239 | - rule_3.3.9 240 | - ipv6 241 | - router_advertisements 242 | - sysctl 243 | -------------------------------------------------------------------------------- /tasks/section_3/cis_3.5.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "3.5.1.1 | PATCH | Ensure ufw is installed" 4 | ansible.builtin.package: 5 | name: ufw 6 | state: present 7 | when: 8 | - debian11cis_rule_3_5_1_1 9 | - "'ufw' not in ansible_facts.packages" 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - automated 14 | - patch 15 | - rule_3.5.1.1 16 | - apt 17 | - ufw 18 | 19 | - name: "3.5.1.2 | PATCH | Ensure iptables-persistent is not installed with ufw" 20 | ansible.builtin.package: 21 | name: iptables-persistent 22 | state: absent 23 | when: 24 | - debian11cis_rule_3_5_1_2 25 | - "'iptables-persistent' in ansible_facts.packages" 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - rule_3.5.1.2 32 | - ufw 33 | 34 | # Adding the allow OpenSSH rule while enabling ufw to allow ansible to run after enabling 35 | - name: "3.5.1.3 | PATCH | Ensure ufw service is enabled" 36 | when: debian11cis_rule_3_5_1_3 37 | tags: 38 | - level1-server 39 | - level1-workstation 40 | - automated 41 | - patch 42 | - rule_3.5.1.3 43 | - ufw 44 | block: 45 | - name: "3.5.1.3 | PATCH | Ensure ufw service is enabled | ssh port enabled" 46 | community.general.ufw: 47 | rule: allow 48 | name: OpenSSH 49 | 50 | - name: "3.5.1.3 | PATCH | Ensure ufw service is enabled | service" 51 | ansible.builtin.systemd: 52 | name: ufw 53 | enabled: true 54 | state: started 55 | masked: false 56 | 57 | - name: "3.5.1.4 | PATCH | Ensure loopback traffic is configured" 58 | block: 59 | - name: "3.5.1.4 | PATCH | Ensure loopback traffic is configured | Set allow in ufw rules" 60 | community.general.ufw: 61 | rule: allow 62 | direction: in 63 | interface: lo 64 | notify: Reload ufw 65 | 66 | - name: "3.5.1.4 | PATCH | Ensure loopback traffic is configured | Set allow out ufw rules" 67 | community.general.ufw: 68 | rule: allow 69 | direction: out 70 | interface: lo 71 | notify: Reload ufw 72 | 73 | - name: "3.5.1.4 | PATCH | Ensure loopback traffic is configured | Set deny ufw rules IPv4" 74 | community.general.ufw: 75 | rule: deny 76 | direction: in 77 | from_ip: 127.0.0.0/8 78 | notify: Reload ufw 79 | 80 | - name: "3.5.1.4 | PATCH | Ensure loopback traffic is configured | Set deny ufw rules IPv6" 81 | community.general.ufw: 82 | rule: deny 83 | direction: in 84 | from_ip: '::1' 85 | when: debian11cis_ipv6_required 86 | notify: Reload ufw 87 | when: 88 | - debian11cis_rule_3_5_1_4 89 | tags: 90 | - level1-server 91 | - level1-workstation 92 | - automated 93 | - patch 94 | - rule_3.5.1.4 95 | - ufw 96 | 97 | - name: "3.5.1.5 | PATCH | Ensure ufw outbound connections are configured" 98 | block: 99 | - name: "3.5.1.5 | PATCH | Ensure ufw outbound connections are configured | Custom ports" 100 | community.general.ufw: 101 | rule: allow 102 | direction: out 103 | to_port: '{{ item }}' 104 | with_items: 105 | - "{{ debian11cis_ufw_allow_out_ports }}" 106 | notify: Reload ufw 107 | when: debian11cis_ufw_allow_out_ports != "all" 108 | 109 | - name: "3.5.1.5 | PATCH | Ensure ufw outbound connections are configured | Allow all" 110 | community.general.ufw: 111 | rule: allow 112 | direction: out 113 | notify: Reload ufw 114 | when: "'all' in debian11cis_ufw_allow_out_ports" 115 | when: 116 | - debian11cis_rule_3_5_1_5 117 | tags: 118 | - level1-server 119 | - level1-workstation 120 | - manual 121 | - patch 122 | - rule_3.5.1.5 123 | - ufw 124 | 125 | - name: "3.5.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports" 126 | block: 127 | - name: "3.5.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Get list of open ports" 128 | ansible.builtin.shell: ss -4tuln 129 | changed_when: false 130 | failed_when: false 131 | check_mode: false 132 | register: discovered_ufw_open_listen_ports 133 | 134 | - name: "3.5.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Get list of firewall rules" 135 | ansible.builtin.shell: ufw status 136 | changed_when: false 137 | failed_when: false 138 | check_mode: false 139 | register: discovered_ufw_status 140 | 141 | - name: "3.5.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Message out settings" 142 | ansible.builtin.debug: 143 | msg: 144 | - "Warning!! Below are the listening ports and firewall rules" 145 | - "Please create firewall rule for any open ports if not already done" 146 | - "*****---Open Listen Ports---*****" 147 | - "{{ discovered_ufw_open_listen_ports.stdout_lines }}" 148 | - "*****---Firewall Rules---*****" 149 | - "{{ discovered_ufw_status.stdout_lines }}" 150 | 151 | - name: "3.5.1.6 | AUDIT | Ensure ufw firewall rules exist for all open ports | Set warning count" 152 | ansible.builtin.import_tasks: 153 | file: warning_facts.yml 154 | vars: 155 | warn_control_id: '3.5.1.6' 156 | when: 157 | - debian11cis_rule_3_5_1_6 158 | tags: 159 | - level1-server 160 | - level1-workstation 161 | - manual 162 | - audit 163 | - rule_3.5.1.6 164 | - ufw 165 | 166 | - name: "3.5.1.7 | PATCH | Ensure ufw default deny firewall policy" 167 | community.general.ufw: 168 | default: deny 169 | direction: "{{ item }}" 170 | notify: Reload ufw 171 | with_items: 172 | - incoming 173 | - outgoing 174 | - routed 175 | when: 176 | - debian11cis_rule_3_5_1_7 177 | tags: 178 | - level1-server 179 | - level1-workstation 180 | - automated 181 | - patch 182 | - rule_3.5.1.7 183 | - ufw 184 | -------------------------------------------------------------------------------- /tasks/section_3/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 3.1 | Disable unused network protocols and devices" 4 | ansible.builtin.import_tasks: 5 | file: cis_3.1.x.yml 6 | 7 | - name: "SECTION | 3.2 | Network Parameters Host Only" 8 | ansible.builtin.import_tasks: 9 | file: cis_3.2.x.yml 10 | 11 | - name: "SECTION | 3.3 | Network Parameters Host and Router" 12 | ansible.builtin.import_tasks: 13 | file: cis_3.3.x.yml 14 | 15 | - name: "SECTION | 3.5.1 | UFW Firewall Configuration" 16 | ansible.builtin.import_tasks: 17 | file: cis_3.5.1.x.yml 18 | when: debian11cis_firewall_package == "ufw" 19 | 20 | - name: "SECTION | 3.5.2 | nftables Firewall Configuration" 21 | ansible.builtin.import_tasks: 22 | file: cis_3.5.2.x.yml 23 | when: debian11cis_firewall_package == "nftables" 24 | 25 | - name: "SECTION | 3.5.3 | iptables Firewall Configuration" 26 | ansible.builtin.import_tasks: 27 | file: cis_3.5.3.x.yml 28 | when: debian11cis_firewall_package == "iptables" 29 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.1.1 | PATCH | Ensure auditd is installed" 4 | ansible.builtin.package: 5 | name: ['auditd', 'audispd-plugins'] 6 | state: present 7 | when: 8 | - debian11cis_rule_4_1_1_1 9 | - "'auditd' not in ansible_facts.packages or 10 | 'auditd-plugins' not in ansible_facts.packages" 11 | tags: 12 | - level2-server 13 | - level2-workstation 14 | - automated 15 | - patch 16 | - rule_4.1.1.1 17 | - auditd 18 | 19 | - name: "4.1.1.2 | PATCH | Ensure auditd service is enabled and active" 20 | ansible.builtin.service: 21 | name: auditd 22 | state: started 23 | enabled: true 24 | when: 25 | - debian11cis_rule_4_1_1_2 26 | tags: 27 | - level2-server 28 | - level2-workstation 29 | - automated 30 | - patch 31 | - rule_4.1.1.2 32 | - auditd 33 | 34 | - name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled" 35 | block: 36 | - name: "4.1.1.3 | AUDIT | Ensure auditing for processes that start prior to auditd is enabled | Get GRUB_CMDLINE_LINUX" 37 | ansible.builtin.shell: grep "GRUB_CMDLINE_LINUX=" /etc/default/grub | cut -f2 -d'"' 38 | changed_when: false 39 | failed_when: false 40 | check_mode: false 41 | register: discovered_grub_cmdline_settings 42 | 43 | - name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Add setting if doesn't exist" 44 | ansible.builtin.lineinfile: 45 | path: /etc/default/grub 46 | regexp: '^GRUB_CMDLINE_LINUX=' 47 | line: 'GRUB_CMDLINE_LINUX="{{ discovered_grub_cmdline_settings.stdout }} audit=1"' 48 | when: "'audit=' not in discovered_grub_cmdline_settings.stdout" 49 | notify: Grub update 50 | 51 | - name: "4.1.1.3 | PATCH | Ensure auditing for processes that start prior to auditd is enabled | Update setting if exists" 52 | ansible.builtin.replace: 53 | dest: /etc/default/grub 54 | regexp: 'audit=([0-9]+)' 55 | replace: 'audit=1' 56 | after: '^GRUB_CMDLINE_LINUX="' 57 | before: '"' 58 | notify: Grub update 59 | when: "'audit=' in discovered_grub_cmdline_settings.stdout" 60 | when: 61 | - debian11cis_rule_4_1_1_3 62 | tags: 63 | - level2-server 64 | - level2-workstation 65 | - automated 66 | - patch 67 | - rule_4.1.1.3 68 | - auditd 69 | 70 | - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient" 71 | block: 72 | - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient | Get current GRUB_CMDLINE_LINUX" 73 | ansible.builtin.shell: grep "GRUB_CMDLINE_LINUX=" /etc/default/grub | cut -f2 -d'"' 74 | changed_when: false 75 | failed_when: false 76 | check_mode: false 77 | register: discovered_grub_cmdline_settings 78 | 79 | - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient | Add setting if doesn't exist" 80 | ansible.builtin.lineinfile: 81 | path: /etc/default/grub 82 | regexp: '^GRUB_CMDLINE_LINUX=' 83 | line: 'GRUB_CMDLINE_LINUX="{{ discovered_grub_cmdline_settings.stdout }} audit_backlog_limit={{ debian11cis_audit_back_log_limit }}"' 84 | notify: Grub update 85 | when: "'audit_backlog_limit=' not in discovered_grub_cmdline_settings.stdout" 86 | 87 | - name: "4.1.1.4 | PATCH | Ensure audit_backlog_limit is sufficient | Update setting if exists" 88 | ansible.builtin.replace: 89 | dest: /etc/default/grub 90 | regexp: 'audit_backlog_limit=([0-9]+)' 91 | replace: 'audit_backlog_limit={{ debian11cis_audit_back_log_limit }}' 92 | after: '^GRUB_CMDLINE_LINUX="' 93 | before: '"' 94 | notify: Grub update 95 | when: 96 | - debian11cis_rule_4_1_1_4 97 | tags: 98 | - level2-server 99 | - level2-workstation 100 | - automated 101 | - patch 102 | - rule_4.1.1.4 103 | - auditd 104 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.2.1 | PATCH | Ensure audit log storage size is configured" 4 | ansible.builtin.lineinfile: 5 | dest: /etc/audit/auditd.conf 6 | regexp: "^max_log_file( |=)" 7 | line: "max_log_file = {{ debian11cis_max_log_file_size }}" 8 | state: present 9 | notify: Restart auditd 10 | when: 11 | - debian11cis_rule_4_1_2_1 12 | tags: 13 | - level2-server 14 | - level2-workstation 15 | - automated 16 | - patch 17 | - rule_4.1.2.1 18 | - auditd 19 | 20 | - name: "4.1.2.2 | PATCH | Ensure audit logs are not automatically deleted" 21 | ansible.builtin.lineinfile: 22 | path: /etc/audit/auditd.conf 23 | regexp: '^max_log_file_action' 24 | line: "max_log_file_action = {{ debian11cis_auditd['max_log_file_action'] }}" 25 | notify: Restart auditd 26 | when: 27 | - debian11cis_rule_4_1_2_2 28 | tags: 29 | - level2-server 30 | - level2-workstation 31 | - automated 32 | - patch 33 | - rule_4.1.2.2 34 | - auditd 35 | 36 | - name: "4.1.2.3 | PATCH | Ensure system is disabled when audit logs are full" 37 | ansible.builtin.lineinfile: 38 | path: /etc/audit/auditd.conf 39 | regexp: "{{ item.regexp }}" 40 | line: "{{ item.line }}" 41 | notify: Restart auditd 42 | with_items: 43 | - { regexp: '^space_left_action', line: "space_left_action = {{ debian11cis_auditd['space_left_action'] }}" } 44 | - { regexp: '^action_mail_acct', line: "action_mail_acct = {{ debian11cis_auditd['action_mail_acct'] }}" } 45 | - { regexp: '^admin_space_left_action', line: "admin_space_left_action = {{ debian11cis_auditd['admin_space_left_action'] }}" } 46 | when: 47 | - debian11cis_rule_4_1_2_3 48 | tags: 49 | - level2-server 50 | - level2-workstation 51 | - automated 52 | - patch 53 | - rule_4.1.2.3 54 | - auditd 55 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.1.3.1 | PATCH | Ensure changes to system administration scope (sudoers) is collected" 4 | ansible.builtin.set_fact: 5 | update_audit_template: true 6 | when: 7 | - debian11cis_rule_4_1_3_1 8 | tags: 9 | - level2-server 10 | - level2-workstation 11 | - automated 12 | - patch 13 | - rule_4.1.3.1 14 | - auditd 15 | 16 | - name: "4.1.3.2 | PATCH | Ensure actions as another user are always logged" 17 | ansible.builtin.set_fact: 18 | update_audit_template: true 19 | notify: restart auditd 20 | when: 21 | - debian11cis_rule_4_1_3_2 22 | tags: 23 | - level2-server 24 | - level2-workstation 25 | - automated 26 | - patch 27 | - rule_4.1.3.2 28 | - auditd 29 | 30 | - name: "4.1.3.3 | PATCH | Ensure events that modify the sudo log file are collected" 31 | ansible.builtin.set_fact: 32 | update_audit_template: true 33 | notify: restart auditd 34 | when: 35 | - debian11cis_rule_4_1_3_3 36 | tags: 37 | - level2-server 38 | - level2-workstation 39 | - automated 40 | - patch 41 | - rule_4.1.3.3 42 | - auditd 43 | 44 | - name: "4.1.3.4 | PATCH | Ensure events that modify date and time information are collected" 45 | ansible.builtin.set_fact: 46 | update_audit_template: true 47 | when: 48 | - debian11cis_rule_4_1_3_4 49 | tags: 50 | - level2-server 51 | - level2-workstation 52 | - automated 53 | - patch 54 | - rule_4.1.3.4 55 | - auditd 56 | 57 | - name: "4.1.3.5 | PATCH | Ensure events that modify the system's network environment are collected" 58 | ansible.builtin.set_fact: 59 | update_audit_template: true 60 | when: 61 | - debian11cis_rule_4_1_3_5 62 | tags: 63 | - level2-server 64 | - level2-workstation 65 | - automated 66 | - patch 67 | - rule_4.1.3.5 68 | - auditd 69 | 70 | - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected" 71 | block: 72 | - name: "4.1.3.6 | AUDIT | Ensure use of privileged commands is collected | Get list of privileged programs" 73 | ansible.builtin.shell: for i in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do find $i -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null; done | grep -vw '/snap' 74 | register: discovered_privilege_process 75 | changed_when: false 76 | check_mode: false 77 | 78 | - name: "4.1.3.6 | PATCH | Ensure use of privileged commands is collected | Set privileged rules" 79 | ansible.builtin.set_fact: 80 | update_audit_template: true 81 | when: 82 | - debian11cis_rule_4_1_3_6 83 | tags: 84 | - level2-server 85 | - level2-workstation 86 | - automated 87 | - patch 88 | - rule_4.1.3.6 89 | - auditd 90 | 91 | - name: "4.1.3.7 | PATCH | Ensure unsuccessful unauthorized file access attempts are collected" 92 | ansible.builtin.set_fact: 93 | update_audit_template: true 94 | when: 95 | - debian11cis_rule_4_1_3_7 96 | tags: 97 | - level2-server 98 | - level2-workstation 99 | - automated 100 | - patch 101 | - rule_4.1.3.7 102 | - auditd 103 | 104 | - name: "4.1.3.8 | PATCH | Ensure events that modify user/group information are collected" 105 | ansible.builtin.set_fact: 106 | update_audit_template: true 107 | when: 108 | - debian11cis_rule_4_1_3_8 109 | tags: 110 | - level2-server 111 | - level2-workstation 112 | - automated 113 | - patch 114 | - rule_4.1.3.8 115 | - auditd 116 | 117 | - name: "4.1.3.9 | PATCH | Ensure discretionary access control permission modification events are collected" 118 | ansible.builtin.set_fact: 119 | update_audit_template: true 120 | when: 121 | - debian11cis_rule_4_1_3_9 122 | tags: 123 | - level2-server 124 | - level2-workstation 125 | - automated 126 | - patch 127 | - rule_4.1.3.9 128 | - auditd 129 | 130 | - name: "4.1.3.10 | PATCH | Ensure successful file system mounts are collected" 131 | ansible.builtin.set_fact: 132 | update_audit_template: true 133 | when: 134 | debian11cis_rule_4_1_3_10 135 | tags: 136 | - level2-server 137 | - level2-workstation 138 | - automated 139 | - patch 140 | - rule_4.1.3.10 141 | - auditd 142 | 143 | - name: "4.1.3.11 | PATCH | Ensure session initiation information is collected" 144 | ansible.builtin.set_fact: 145 | update_audit_template: true 146 | when: 147 | - debian11cis_rule_4_1_3_11 148 | tags: 149 | - level2-server 150 | - level2-workstation 151 | - automated 152 | - patch 153 | - rule_4.1.3.11 154 | - auditd 155 | 156 | - name: "4.1.3.12 | PATCH | Ensure login and logout events are collected" 157 | ansible.builtin.set_fact: 158 | update_audit_template: true 159 | when: 160 | - debian11cis_rule_4_1_3_12 161 | tags: 162 | - level2-server 163 | - level2-workstation 164 | - automated 165 | - patch 166 | - rule_4.1.3.12 167 | - auditd 168 | 169 | - name: "4.1.3.13 | PATCH | Ensure file deletion events by users are collected" 170 | ansible.builtin.set_fact: 171 | update_audit_template: true 172 | when: 173 | - debian11cis_rule_4_1_3_13 174 | tags: 175 | - level2-server 176 | - level2-workstation 177 | - automated 178 | - patch 179 | - rule_4.1.3.13 180 | - auditd 181 | 182 | - name: "4.1.3.14 | PATCH | Ensure events that modify the system's Mandatory Access Controls are collected" 183 | ansible.builtin.set_fact: 184 | update_audit_template: true 185 | when: 186 | - debian11cis_rule_4_1_3_14 187 | tags: 188 | - level2-server 189 | - level2-workstation 190 | - automated 191 | - patch 192 | - rule_4.1.3.14 193 | - auditd 194 | 195 | - name: "4.1.3.15 | PATCH | Ensure successful and unsuccessful attempts to use the chcon command are recorded" 196 | ansible.builtin.set_fact: 197 | update_audit_template: true 198 | when: 199 | - debian11cis_rule_4_1_3_15 200 | tags: 201 | - level2-server 202 | - level2-workstation 203 | - automated 204 | - patch 205 | - rule_4.1.3.15 206 | - auditd 207 | 208 | - name: "4.1.3.16 | PATCH | Ensure successful and unsuccessful attempts to use the setfacl command are recorded" 209 | ansible.builtin.set_fact: 210 | update_audit_template: true 211 | when: 212 | - debian11cis_rule_4_1_3_16 213 | tags: 214 | - level2-server 215 | - level2-workstation 216 | - automated 217 | - patch 218 | - rule_4.1.3.16 219 | - auditd 220 | 221 | - name: "4.1.3.17 | PATCH | Ensure successful and unsuccessful attempts to use the chacl command are recorded" 222 | ansible.builtin.set_fact: 223 | update_audit_template: true 224 | when: 225 | - debian11cis_rule_4_1_3_17 226 | tags: 227 | - level2-server 228 | - level2-workstation 229 | - automated 230 | - patch 231 | - rule_4.1.3.17 232 | - auditd 233 | 234 | - name: "4.1.3.18 | PATCH | Ensure successful and unsuccessful attempts to use the usermod command are recorded" 235 | ansible.builtin.set_fact: 236 | update_audit_template: true 237 | when: 238 | - debian11cis_rule_4_1_3_18 239 | tags: 240 | - level2-server 241 | - level2-workstation 242 | - automated 243 | - patch 244 | - rule_4.1.3.18 245 | - auditd 246 | 247 | - name: "4.1.3.19 | PATCH | Ensure kernel module loading and unloading is collected" 248 | ansible.builtin.set_fact: 249 | update_audit_template: true 250 | when: 251 | - debian11cis_rule_4_1_3_19 252 | tags: 253 | - level2-server 254 | - level2-workstation 255 | - automated 256 | - patch 257 | - rule_4.1.3.19 258 | - auditd 259 | 260 | - name: "4.1.3.20 | PATCH | Ensure the audit configuration is immutable" 261 | ansible.builtin.set_fact: 262 | update_audit_template: true 263 | when: 264 | - debian11cis_rule_4_1_3_20 265 | tags: 266 | - level2-server 267 | - level2-workstation 268 | - automated 269 | - scored 270 | - patch 271 | - rule_4.1.3.20 272 | - auditd 273 | 274 | - name: "4.1.3.21 | PATCH | Ensure the running and on disk configuration is the same" 275 | ansible.builtin.shell: augenrules --check 276 | changed_when: false 277 | register: discovered_auditd_diff_check 278 | when: 279 | - debian11cis_rule_4_1_3_21 280 | tags: 281 | - level2-server 282 | - level2-workstation 283 | - automated 284 | - scored 285 | - patch 286 | - rule_4.1.3.21 287 | - auditd 288 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.1.4.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: | 4 | "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" 5 | "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" 6 | "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" 7 | 8 | block: 9 | - name: "4.1.4.1 | AUDIT | Ensure audit log files are mode 0640 or less permissive | discover file" 10 | ansible.builtin.shell: grep ^log_file /etc/audit/auditd.conf | awk '{ print $NF }' 11 | changed_when: false 12 | register: audit_discovered_logfile 13 | 14 | - name: "4.1.4.1 | AUDIT | Ensure audit log files are mode 0640 or less permissive | stat file" 15 | ansible.builtin.stat: 16 | path: "{{ audit_discovered_logfile.stdout }}" 17 | changed_when: false 18 | register: discovered_audit_logfile 19 | 20 | - name: | 21 | "4.1.4.1 | PATCH | Ensure audit log files are mode 0640 or less permissive" 22 | "4.1.4.2 | PATCH | Ensure only authorized users own audit log files" 23 | "4.1.4.3 | PATCH | Ensure only authorized groups are assigned ownership of audit log files" 24 | ansible.builtin.file: 25 | path: "{{ audit_discovered_logfile.stdout }}" 26 | mode: 'u-x,g-wx,o-rwx' 27 | owner: root 28 | group: root 29 | when: 30 | - debian11cis_rule_4_1_4_1 or 31 | debian11cis_rule_4_1_4_2 or 32 | debian11cis_rule_4_1_4_3 33 | tags: 34 | - level2-server 35 | - level2-workstation 36 | - patch 37 | - auditd 38 | - rule_4.1.4.1 39 | - rule_4.1.4.2 40 | - rule_4.1.4.3 41 | 42 | - name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive" 43 | block: 44 | - name: "4.1.4.4 | AUDIT | Ensure the audit log directory is 0750 or more restrictive | get current permissions" 45 | ansible.builtin.stat: 46 | path: "{{ audit_discovered_logfile.stdout | dirname }}" 47 | register: discovered_auditlog_directory 48 | 49 | - name: "4.1.4.4 | PATCH | Ensure the audit log directory is 0750 or more restrictive | set" 50 | ansible.builtin.file: 51 | path: "{{ audit_discovered_logfile.stdout | dirname }}" 52 | state: directory 53 | mode: 'u-x,g-w,o-rwx' 54 | when: not discovered_auditlog_directory.stat.mode is match('07(0|5)0') 55 | when: 56 | - debian11cis_rule_4_1_4_4 57 | tags: 58 | - level2-server 59 | - level2-workstation 60 | - patch 61 | - auditd 62 | - rule_4.1.4.4 63 | 64 | - name: "4.1.4.5 | PATCH | Ensure audit configuration files are 640 or more restrictive" 65 | ansible.builtin.file: 66 | path: "{{ item.path }}" 67 | mode: 'u-x,g-wx,o-rwx' 68 | loop: "{{ auditd_conf_files.files }}" 69 | loop_control: 70 | label: "{{ item.path }}" 71 | when: 72 | - item.mode > '0640' 73 | - debian11cis_rule_4_1_4_5 74 | tags: 75 | - level2-server 76 | - level2-workstation 77 | - patch 78 | - auditd 79 | - rule_4.1.4.5 80 | 81 | - name: "4.1.4.6 | PATCH | Ensure audit configuration files are owned by root" 82 | ansible.builtin.file: 83 | path: "{{ item.path }}" 84 | owner: root 85 | loop: "{{ auditd_conf_files.files }}" 86 | loop_control: 87 | label: "{{ item.path }}" 88 | when: 89 | - debian11cis_rule_4_1_4_6 90 | tags: 91 | - level2-server 92 | - level2-workstation 93 | - patch 94 | - auditd 95 | - rule_4.1.4.6 96 | 97 | - name: "4.1.4.7 | PATCH | Ensure audit configuration files belong to group root" 98 | ansible.builtin.file: 99 | path: "{{ item.path }}" 100 | group: root 101 | loop: "{{ auditd_conf_files.files }}" 102 | loop_control: 103 | label: "{{ item.path }}" 104 | when: 105 | - debian11cis_rule_4_1_4_7 106 | tags: 107 | - level2-server 108 | - level2-workstation 109 | - patch 110 | - auditd 111 | - rule_4.1.4.7 112 | 113 | - name: "4.1.4.8 | PATCH | Ensure audit tools are 755 or more restrictive" 114 | ansible.builtin.file: 115 | path: "{{ item }}" 116 | mode: 'u-x,g-w,o-rwx' 117 | loop: "{{ audit_bins }}" 118 | when: 119 | - debian11cis_rule_4_1_4_8 120 | tags: 121 | - level2-server 122 | - level2-workstation 123 | - patch 124 | - auditd 125 | - rule_4.1.4.8 126 | 127 | - name: "4.1.4.9 | PATCH | Ensure audit tools are owned by root" 128 | ansible.builtin.file: 129 | path: "{{ item }}" 130 | owner: root 131 | group: root 132 | loop: "{{ audit_bins }}" 133 | when: 134 | - debian11cis_rule_4_1_4_9 135 | tags: 136 | - level2-server 137 | - level2-workstation 138 | - patch 139 | - auditd 140 | - rule_4.1.4.9 141 | 142 | - name: "4.1.4.10 | PATCH | Ensure audit tools belong to group root" 143 | ansible.builtin.file: 144 | path: "{{ item }}" 145 | group: root 146 | loop: "{{ audit_bins }}" 147 | when: 148 | - debian11cis_rule_4_1_4_10 149 | tags: 150 | - level2-server 151 | - level2-workstation 152 | - patch 153 | - auditd 154 | - rule_4.1.4.10 155 | 156 | - name: "4.1.4.11 | PATCH | Ensure cryptographic mechanisms are used to protect the integrity of audit tools" 157 | ansible.builtin.lineinfile: 158 | path: /etc/aide/aide.conf 159 | regexp: "{{ item }}" 160 | line: "{{ item }}" 161 | loop: 162 | - '# Audit tools' 163 | - /sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 164 | - /sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 165 | - /sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 166 | - /sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 167 | - /sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 168 | - /sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 169 | when: 170 | - debian11cis_rule_4_1_4_11 and 171 | debian11cis_config_aide 172 | tags: 173 | - level2-server 174 | - level2-workstation 175 | - patch 176 | - auditd 177 | - rule_4.1.4.11 178 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.2.1.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.2.1.1.1 | PATCH | Ensure systemd-journal-remote is installed" 4 | ansible.builtin.package: 5 | name: systemd-journal-remote 6 | state: present 7 | when: 8 | - debian11cis_rule_4_2_1_1_1 9 | - not debian11cis_system_is_log_server 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - manual 14 | - patch 15 | - journald 16 | - rule_4.2.1.1.1 17 | 18 | - name: "4.2.1.1.2 | PATCH | Ensure systemd-journal-remote is configured" 19 | ansible.builtin.lineinfile: 20 | path: /etc/systemd/journal-upload.conf 21 | regexp: "{{ item.regexp }}" 22 | line: "{{ item.line }}" 23 | notify: Restart journald 24 | loop: 25 | - { regexp: 'URL=', line: 'URL={{ debian11cis_remote_log_server }}'} 26 | - { regexp: 'ServerKeyFile=', line: 'ServerKeyFile={{ debian11cis_journal_upload_serverkeyfile }}'} 27 | - { regexp: 'ServerCertificateFile=', line: 'ServerCertificateFile={{ debian11cis_journal_servercertificatefile }}'} 28 | - { regexp: 'TrustedCertificateFile=', line: 'TrustedCertificateFile={{ debian11cis_journal_trustedcertificatefile }}'} 29 | when: 30 | - debian11cis_rule_4_2_1_1_2 31 | - not debian11cis_system_is_log_server 32 | tags: 33 | - level1-server 34 | - level1-workstation 35 | - manual 36 | - patch 37 | - journald 38 | - rule_4.2.1.1.2 39 | 40 | - name: "4.2.1.1.3 | PATCH | Ensure systemd-journal-remote is enabled" 41 | ansible.builtin.systemd: 42 | name: systemd-journal-upload 43 | state: started 44 | enabled: true 45 | when: 46 | - not debian11cis_system_is_log_server 47 | - debian11cis_rule_4_2_1_1_3 48 | tags: 49 | - level1-server 50 | - level1-workstation 51 | - manual 52 | - patch 53 | - journald 54 | - rule_4.2.1.1.3 55 | 56 | - name: "4.2.1.1.4 | PATCH | Ensure journald is not configured to recieve logs from a remote client" 57 | ansible.builtin.systemd: 58 | name: systemd-journal-remote.socket 59 | state: stopped 60 | enabled: false 61 | masked: true 62 | when: 63 | - not debian11cis_system_is_log_server 64 | - debian11cis_rule_4_2_1_1_4 65 | tags: 66 | - level1-server 67 | - level1-workstation 68 | - patch 69 | - journald 70 | - rule_4.2.1.1.4 71 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.2.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.2.1.2 | PATCH | Ensure journald service is enabled" 4 | block: 5 | - name: "4.2.1.2 | AUDIT | Ensure journald service is enabled | Capture status" 6 | ansible.builtin.shell: systemctl is-enabled systemd-journald.service 7 | changed_when: false 8 | failed_when: false 9 | register: discovered_journald_status 10 | 11 | - name: "4.2.1.2 | AUDIT | Ensure journald service is enabled | Alert on bad status" 12 | ansible.builtin.debug: 13 | msg: 14 | - "Warning!! The status of systemd-journald should be static and it is not. Please investigate" 15 | when: "'static' not in discovered_journald_status.stdout" 16 | 17 | - name: "4.2.1.2 | AUDIT | Ensure journald service is enabled | Warn Count" 18 | ansible.builtin.import_tasks: 19 | file: warning_facts.yml 20 | when: "'static' not in discovered_journald_status.stdout" 21 | vars: 22 | warn_control_id: '4.2.1.2' 23 | when: 24 | - debian11cis_rule_4_2_1_2 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - audit 29 | - journald 30 | - rule_4.2.1.2 31 | 32 | - name: "4.2.1.3 | PATCH | Ensure journald is configured to compress large log files" 33 | ansible.builtin.lineinfile: 34 | path: /etc/systemd/journald.conf 35 | regexp: '^(#|)Compress=' 36 | line: Compress=yes 37 | notify: Restart journald 38 | when: 39 | - debian11cis_rule_4_2_1_3 40 | tags: 41 | - level1-server 42 | - level1-workstation 43 | - patch 44 | - journald 45 | - rule_4.2.1.3 46 | 47 | - name: "4.2.1.4 | PATCH | Ensure journald is configured to write logfiles to persistent disk" 48 | ansible.builtin.lineinfile: 49 | path: /etc/systemd/journald.conf 50 | regexp: '^(#|)Storage=' 51 | line: Storage=persistent 52 | notify: Restart journald 53 | when: 54 | - debian11cis_rule_4_2_1_4 55 | tags: 56 | - level1-server 57 | - level1-workstation 58 | - patch 59 | - journald 60 | - rule_4.2.1.4 61 | 62 | - name: "4.2.1.5 | PATCH | Ensure journald is not configured to send logs to rsyslog" 63 | ansible.builtin.lineinfile: 64 | path: /etc/systemd/journald.conf 65 | regexp: '^ForwardToSyslog=' 66 | line: '#ForwardToSyslog=yes' 67 | notify: Restart journald 68 | when: 69 | - debian11cis_rule_4_2_1_5 70 | tags: 71 | - level1-server 72 | - level1-workstation 73 | - manual 74 | - patch 75 | - journald 76 | - rule_4.2.1.5 77 | 78 | - name: "4.2.1.6 | PATCH | Ensure journald log rotation is configured per site policy" 79 | ansible.builtin.lineinfile: 80 | path: /etc/systemd/journald.conf 81 | regexp: "{{ item.regexp }}" 82 | line: "{{ item.line }}" 83 | notify: Restart journald 84 | loop: 85 | - { regexp: '^(#|\s+)SystemMaxUse=', line: "{{ debian11cis_journald_systemmaxuse }}" } 86 | - { regexp: '^(#|\s+)SystemKeepFree=', line: "{{ debian11cis_journald_systemkeepfree }}" } 87 | - { regexp: '^(#|\s+)RuntimeMaxUse=', line: "{{ debian11cis_journald_runtimemaxuse }}" } 88 | - { regexp: '^(#|\s+)RuntimeKeepFree=', line: "{{ debian11cis_journald_runtimekeepfree }}" } 89 | - { regexp: '^(#|\s+)MaxFileSec=', line: "{{ debian11cis_journald_maxfilesec }}" } 90 | when: 91 | - debian11cis_rule_4_2_1_6 92 | tags: 93 | - level1-server 94 | - level1-workstation 95 | - manual 96 | - patch 97 | - journald 98 | - rule_4.2.1.6 99 | 100 | - name: "4.2.1.7 | AUDIT | Ensure journald default file permissions configured" 101 | block: 102 | - name: "4.2.1.7 | AUDIT | Ensure journald default file permissions configured | Check for override file" 103 | ansible.builtin.stat: 104 | path: /etc/tmpfiles.d/systemd.conf 105 | register: discovered_journald_override_status 106 | 107 | - name: "4.2.1.7 | AUDIT | Ensure journald default file permissions configured | Set live file" 108 | ansible.builtin.set_fact: 109 | systemd_conf_file: /etc/tmpfiles.d/systemd.conf 110 | when: discovered_journald_override_status.stat.exists 111 | 112 | - name: "4.2.1.7 | PATCH | Ensure journald default file permissions configured | Set permission" 113 | ansible.builtin.lineinfile: 114 | path: "{{ systemd_conf_file | default('/usr/lib/tmpfiles.d/systemd.conf') }}" 115 | regexp: '^z \/var\/log\/journal\/%m\/system.journal (!?06(0|4)0) root' 116 | line: 'z /var/log/journal/%m/system.journal 0640 root systemd-journal - -' 117 | when: 118 | - debian11cis_rule_4_2_1_7 119 | tags: 120 | - level1-server 121 | - level1-workstation 122 | - manual 123 | - patch 124 | - journald 125 | - rule_4.2.1.7 126 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.2.2.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.2.2.1 | PATCH | Ensure rsyslog is installed" 4 | ansible.builtin.package: 5 | name: rsyslog 6 | state: present 7 | when: 8 | - debian11cis_rule_4_2_2_1 9 | - "'rsyslog' not in ansible_facts.packages" 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - automated 14 | - patch 15 | - rule_4.2.2.1 16 | - rsyslog 17 | - apt 18 | 19 | - name: "4.2.2.2 | PATCH | Ensure rsyslog Service is enabled" 20 | ansible.builtin.systemd: 21 | name: rsyslog 22 | enabled: true 23 | when: 24 | - debian11cis_rule_4_2_2_2 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - automated 29 | - patch 30 | - rule_4.2.2.2 31 | - rsyslog 32 | 33 | - name: "4.2.2.3 | PATCH | Ensure journald is configured to send logs to rsyslog" 34 | ansible.builtin.lineinfile: 35 | path: /etc/systemd/journald.conf 36 | regexp: ^ForwardToSyslog= 37 | line: ForwardToSyslog=yes 38 | notify: Restart syslog service 39 | when: 40 | - debian11cis_rule_4_2_2_3 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - manual 45 | - patch 46 | - journald 47 | - rule_4.2.2.3 48 | 49 | - name: "4.2.2.4 | PATCH | Ensure rsyslog default file permissions configured" 50 | ansible.builtin.lineinfile: 51 | path: /etc/rsyslog.conf 52 | regexp: '^\$FileCreateMode|^#\$FileCreateMode' 53 | line: '$FileCreateMode 0640' 54 | notify: Restart syslog service 55 | when: 56 | - debian11cis_rule_4_2_2_4 57 | tags: 58 | - level1-server 59 | - level1-workstation 60 | - automated 61 | - patch 62 | - rule_4.2.2.4 63 | - rsyslog 64 | 65 | - name: "4.2.2.5 | PATCH | Ensure logging is configured" 66 | block: 67 | - name: "4.2.2.5 | AUDIT | Ensure logging is configured | Find configuration file" 68 | ansible.builtin.shell: grep -r "*.emerg" /etc/* | cut -f1 -d":" 69 | changed_when: false 70 | failed_when: false 71 | check_mode: false 72 | register: discovered_debian11cis_rsyslog_config_path 73 | 74 | - name: "4.2.2.5 | AUDIT | Ensure logging is configured | Gather rsyslog current config" 75 | ansible.builtin.shell: "cat {{ discovered_debian11cis_rsyslog_config_path.stdout }}" 76 | changed_when: false 77 | failed_when: false 78 | check_mode: false 79 | register: discovered_debian11cis_rsyslog_config 80 | 81 | - name: "4.2.2.5 | AUDIT | Ensure logging is configured | Message out config" 82 | ansible.builtin.debug: 83 | msg: 84 | - "Warning!! Below is the current logging configurations for rsyslog, please review" 85 | - "{{ discovered_debian11cis_rsyslog_config.stdout_lines }}" 86 | when: not debian11cis_rsyslog_ansible_managed 87 | 88 | - name: "4.2.2.5 | PATCH | Ensure logging is configured | Set warning count" 89 | ansible.builtin.import_tasks: 90 | file: warning_facts.yml 91 | when: not debian11cis_rsyslog_ansible_managed 92 | 93 | - name: "4.2.2.5 | PATCH | Ensure logging is configured | Automated rsyslog configuration" 94 | ansible.builtin.lineinfile: 95 | path: "{{ discovered_debian11cis_rsyslog_config_path.stdout }}" 96 | regexp: "{{ item.regexp }}" 97 | line: "{{ item.line }}" 98 | insertafter: "{{ item.insertafter }}" 99 | with_items: 100 | - { regexp: '^\*.emerg', line: '*.emerg :omusrmsg:*', insertafter: '^# Emergencies are sent to everybody logged in' } 101 | - { regexp: '^auth,authpriv.\*', line: 'auth,authpriv.* /var/log/secure', insertafter: '^# First some standard log files. Log by facility' } 102 | - { regexp: '^mail.\*|^#mail.\*', line: 'mail.* -/var/log/mail', insertafter: '^# First some standard log files' } 103 | - { regexp: '^cron.\*|^cron.\*', line: 'cron.* -/var/log/cron', insertafter: '^# First some standard log files' } 104 | - { regexp: '^mail.info|^#mail.info', line: 'mail.info -/var/log/mail.info', insertafter: '^# Logging for the mail system' } 105 | - { regexp: '^mail.warn|^#mail.warn', line: 'mail.warning -/var/log/mail.warn', insertafter: '^# Logging for the mail system.' } 106 | - { regexp: '^mail.err|^#mail.err', line: 'mail.err /var/log/mail.err', insertafter: '^# Logging for the mail system.' } 107 | - { regexp: '^\*.=warning;\*.=err|^#\*.=warning;\*.=err', line: '*.=warning;*.=err -/var/log/warn', insertafter: '^# First some standard log files' } 108 | - { regexp: '^\*.crit|^#\*.crit', line: '*.crit /var/log/warn', insertafter: '^# First some standard log files' } 109 | - { regexp: '^\*.\*;mail.none;news.none|^#\*.\*;mail.none;news.none', line: '*.*;mail.none;news.none -/var/log/messages', insertafter: '^# First some standard log files' } 110 | - { regexp: '^local0,local1.\*|^#local0,local1.\*', line: 'local0,local1.* -/var/log/localmessages', insertafter: '^# First some standard log files' } 111 | - { regexp: '^local2,local3.\*|^#local2,local3.\*', line: 'local2,local3.* -/var/log/localmessages', insertafter: '^# First some standard log files' } 112 | - { regexp: '^local4,local5.\*|^#local4,local5.\*', line: 'local4,local5.* -/var/log/localmessages', insertafter: '^# First some standard log files' } 113 | - { regexp: '^local6,local7.\*|^#local6,local7.\*', line: 'local6,local7.* -/var/log/localmessages', insertafter: '^# First some standard log files' } 114 | loop_control: 115 | label: "{{ item.line }}" 116 | notify: Restart syslog service 117 | when: debian11cis_rsyslog_ansible_managed 118 | vars: 119 | warn_control_id: '4.2.2.5' 120 | when: 121 | - debian11cis_rule_4_2_2_5 122 | tags: 123 | - level1-server 124 | - level1-workstation 125 | - manual 126 | - patch 127 | - rule_4.2.2.5 128 | - rsyslog 129 | 130 | - name: "4.2.2.6 | PATCH | Ensure rsyslog is configured to send logs to a remote log host" 131 | ansible.builtin.blockinfile: 132 | path: /etc/rsyslog.conf 133 | block: | 134 | ##Enable sending of logs over TCP add the following line: 135 | *.* @@{{ debian11cis_remote_log_server }} 136 | insertafter: EOF 137 | when: 138 | - debian11cis_rule_4_2_2_6 139 | - not debian11cis_system_is_log_server 140 | tags: 141 | - level1-server 142 | - level1-workstation 143 | - automated 144 | - patch 145 | - rule_4.2.2.6 146 | - rsyslog 147 | 148 | - name: "4.2.2.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote client" 149 | block: 150 | - name: "4.2.2.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote client | When not a log host" 151 | ansible.builtin.replace: 152 | path: /etc/rsyslog.conf 153 | regexp: '({{ item }})' 154 | replace: '#\1' 155 | with_items: 156 | - '^(\$ModLoad|module(load="imtcp"))' 157 | - '^(\$(InputTCP|InputRELP|UDP)ServerRun|input(type="imtcp" port=".*"))' 158 | notify: Restart syslog service 159 | when: not debian11cis_system_is_log_server 160 | 161 | - name: "4.2.2.7 | PATCH | Ensure rsyslog is not configured to receive logs from a remote client | When a log server" 162 | ansible.builtin.lineinfile: 163 | path: /etc/rsyslog.conf 164 | regexp: "{{ item.regexp }}" 165 | line: "{{ item.line }}" 166 | with_items: 167 | - { regexp: '^\$ModLoad|^#\$ModLoad', line: '$ModLoad imtcp' } 168 | - { regexp: '^\$InputTCPServerRun|^#\$InputTCPServerRun', line: '$InputTCPServerRun 514' } 169 | notify: Restart syslog service 170 | when: debian11cis_system_is_log_server 171 | when: 172 | - debian11cis_rule_4_2_2_7 173 | tags: 174 | - level1-server 175 | - level1-workstation 176 | - manual 177 | - patch 178 | - rule_4.2.2.7 179 | - rsyslog 180 | -------------------------------------------------------------------------------- /tasks/section_4/cis_4.2.3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured" 4 | block: 5 | - name: "4.2.3 | AUDIT | Ensure permissions on all logfiles are configured | find files" 6 | ansible.builtin.find: 7 | paths: "/var/log" 8 | file_type: file 9 | recurse: true 10 | hidden: true 11 | register: discovered_logfiles 12 | 13 | - name: "4.2.3 | PATCH | Ensure permissions on all logfiles are configured | change permissions" 14 | ansible.builtin.file: 15 | path: "{{ item.path }}" 16 | mode: 'u-x,g-wx,o-rwx' 17 | loop: "{{ discovered_logfiles.files }}" 18 | loop_control: 19 | label: "{{ item.path }}" 20 | when: 21 | - item.path != "/var/log/btmp" 22 | - item.path != "/var/log/utmp" 23 | - item.path != "/var/log/wtmp" 24 | when: 25 | - debian11cis_rule_4_2_3 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - patch 30 | - logfiles 31 | - rule_4.2.3 32 | -------------------------------------------------------------------------------- /tasks/section_4/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 4.1.1 | Ensure auditing is enabled" 4 | ansible.builtin.import_tasks: 5 | file: cis_4.1.1.x.yml 6 | 7 | - name: "SECTION | 4.1.2 | Configure Data Retention" 8 | ansible.builtin.import_tasks: 9 | file: cis_4.1.2.x.yml 10 | 11 | - name: "SECTION | 4.1.3 | Configure auditd rules" 12 | ansible.builtin.import_tasks: 13 | file: cis_4.1.3.x.yml 14 | 15 | - name: "SECTION | 4.1.4 | Configure auditd file access" 16 | ansible.builtin.import_tasks: 17 | file: cis_4.1.4.x.yml 18 | 19 | - name: "SECTION | 4.2.1.1.x | Configure journald" 20 | ansible.builtin.import_tasks: 21 | file: cis_4.2.1.1.x.yml 22 | when: debian11cis_syslog_service == 'journald' 23 | 24 | - name: "SECTION | 4.2.1.x | Configure journald" 25 | ansible.builtin.import_tasks: 26 | file: cis_4.2.1.x.yml 27 | when: debian11cis_syslog_service == 'journald' 28 | 29 | - name: "SECTION | 4.2.2.x | Configure rsyslog" 30 | ansible.builtin.import_tasks: 31 | file: cis_4.2.2.x.yml 32 | when: debian11cis_syslog_service == 'rsyslog' 33 | 34 | - name: "SECTION | 4.2.3 | Ensure permissions on all logfiles are configured" 35 | ansible.builtin.import_tasks: 36 | file: cis_4.2.3.yml 37 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.1.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.1.1 | PATCH | Ensure cron daemon is enabled and running" 4 | ansible.builtin.systemd: 5 | name: cron 6 | state: started 7 | enabled: true 8 | when: 9 | - debian11cis_rule_5_1_1 10 | tags: 11 | - level1-server 12 | - level1-workstation 13 | - automated 14 | - patch 15 | - rule_5.1.1 16 | - cron 17 | 18 | - name: "5.1.2 | PATCH | Ensure permissions on /etc/crontab are configured" 19 | ansible.builtin.file: 20 | path: /etc/crontab 21 | owner: root 22 | group: root 23 | mode: 'u-x,go-rwx' 24 | when: 25 | - debian11cis_rule_5_1_2 26 | tags: 27 | - level1-server 28 | - level1-workstation 29 | - automated 30 | - patch 31 | - rule_5.1.2 32 | - cron 33 | 34 | - name: "5.1.3 | PATCH | Ensure permissions on /etc/cron.hourly are configured" 35 | ansible.builtin.file: 36 | path: /etc/cron.hourly 37 | owner: root 38 | group: root 39 | mode: 'u+x,go-rwx' 40 | when: 41 | - debian11cis_rule_5_1_3 42 | tags: 43 | - level1-server 44 | - level1-workstation 45 | - automated 46 | - patch 47 | - rule_5.1.3 48 | - cron 49 | 50 | - name: "5.1.4 | PATCH | Ensure permissions on /etc/cron.daily are configured" 51 | ansible.builtin.file: 52 | path: /etc/cron.daily 53 | owner: root 54 | group: root 55 | mode: 'u+x,go-rwx' 56 | when: 57 | - debian11cis_rule_5_1_4 58 | tags: 59 | - level1-server 60 | - level1-workstation 61 | - automated 62 | - patch 63 | - rule_5.1.4 64 | - cron 65 | 66 | - name: "5.1.5 | PATCH | Ensure permissions on /etc/cron.weekly are configured" 67 | ansible.builtin.file: 68 | path: /etc/cron.weekly 69 | owner: root 70 | group: root 71 | mode: 'u+x,go-rwx' 72 | when: 73 | - debian11cis_rule_5_1_5 74 | tags: 75 | - level1-server 76 | - level1-workstation 77 | - automated 78 | - patch 79 | - rule_5.1.5 80 | - cron 81 | 82 | - name: "5.1.6 | PATCH | Ensure permissions on /etc/cron.monthly are configured" 83 | ansible.builtin.file: 84 | path: /etc/cron.monthly 85 | owner: root 86 | group: root 87 | mode: 'u+x,go-rwx' 88 | when: 89 | - debian11cis_rule_5_1_6 90 | tags: 91 | - level1-server 92 | - level1-workstation 93 | - automated 94 | - patch 95 | - rule_5.1.6 96 | - cron 97 | 98 | - name: "5.1.7 | PATCH | Ensure permissions on /etc/cron.d are configured" 99 | ansible.builtin.file: 100 | path: /etc/cron.d 101 | owner: root 102 | group: root 103 | mode: 'u+x,go-rwx' 104 | when: 105 | - debian11cis_rule_5_1_7 106 | tags: 107 | - level1-server 108 | - level1-workstation 109 | - automated 110 | - patch 111 | - rule_5.1.7 112 | - cron 113 | 114 | - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users" 115 | block: 116 | - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Remove cron.deny" 117 | ansible.builtin.file: 118 | path: /etc/cron.deny 119 | state: absent 120 | 121 | - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Check for cron.allow" 122 | ansible.builtin.stat: 123 | path: /etc/cron.allow 124 | register: discovered_cron_allow_status 125 | 126 | - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Create cron.allow if doesn't exist" 127 | ansible.builtin.file: 128 | path: /etc/cron.allow 129 | owner: root 130 | group: root 131 | mode: 'u-x,g-wx,o-rwx' 132 | state: touch 133 | when: not discovered_cron_allow_status.stat.exists 134 | 135 | - name: "5.1.8 | PATCH | Ensure cron is restricted to authorized users | Update cron.allow if exists" 136 | ansible.builtin.file: 137 | path: /etc/cron.allow 138 | owner: root 139 | group: root 140 | mode: 'u-x,g-wx,o-rwx' 141 | when: discovered_cron_allow_status.stat.exists 142 | when: 143 | - debian11cis_rule_5_1_8 144 | tags: 145 | - level1-server 146 | - level1-workstation 147 | - automated 148 | - patch 149 | - rule_5.1.8 150 | - cron 151 | 152 | - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users" 153 | block: 154 | - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Remove at.deny" 155 | ansible.builtin.file: 156 | path: /etc/at.deny 157 | state: absent 158 | 159 | - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Check for at.allow" 160 | ansible.builtin.stat: 161 | path: /etc/at.allow 162 | register: discovered_at_allow_status 163 | 164 | - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | Create at.allow if doesn't exist" 165 | ansible.builtin.file: 166 | path: /etc/at.allow 167 | owner: root 168 | group: root 169 | mode: 'u-x,g-wx,o-rwx' 170 | state: touch 171 | when: not discovered_at_allow_status.stat.exists 172 | 173 | - name: "5.1.9 | PATCH | Ensure at is restricted to authorized users | update at.allow if exists" 174 | ansible.builtin.file: 175 | path: /etc/at.allow 176 | owner: root 177 | group: root 178 | mode: 'u-x,g-wx,o-rwx' 179 | when: discovered_at_allow_status.stat.exists 180 | when: 181 | - debian11cis_rule_5_1_9 182 | tags: 183 | - level1-server 184 | - level1-workstation 185 | - automated 186 | - patch 187 | - rule_5.1.9 188 | - cron 189 | -------------------------------------------------------------------------------- /tasks/section_5/cis_5.3.x.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "5.3.1 | PATCH | Ensure sudo is installed" 4 | ansible.builtin.package: 5 | name: "{{ debian11cis_sudo_package }}" 6 | state: present 7 | when: 8 | - debian11cis_rule_5_3_1 9 | tags: 10 | - level1-server 11 | - level1-workstation 12 | - automated 13 | - patch 14 | - rule_5.3.1 15 | - sudo 16 | 17 | - name: "5.3.2 | PATCH | Ensure sudo commands use pty" 18 | ansible.builtin.lineinfile: 19 | path: /etc/sudoers 20 | regexp: '^Defaults \s+use_' 21 | line: 'Defaults use_pty' 22 | insertafter: '^\s*Defaults' 23 | when: 24 | - debian11cis_rule_5_3_2 25 | tags: 26 | - level1-server 27 | - level1-workstation 28 | - automated 29 | - patch 30 | - rule_5.3.2 31 | - sudo 32 | 33 | - name: "5.3.3 | PATCH | Ensure sudo log file exists" 34 | ansible.builtin.lineinfile: 35 | path: /etc/sudoers 36 | regexp: '^Defaults\s+logfile' 37 | line: 'Defaults logfile="{{ debian11cis_sudo_logfile }}"' 38 | insertafter: '^\s*Defaults' 39 | when: 40 | - debian11cis_rule_5_3_3 41 | tags: 42 | - level1-server 43 | - level1-workstation 44 | - automated 45 | - patch 46 | - rule_5.3.3 47 | - sudo 48 | 49 | - name: "5.3.4 | PATCH | Ensure users must provide password for escalation" 50 | ansible.builtin.replace: 51 | path: "{{ item }}" 52 | regexp: '^([^#|{% if system_is_ec2 %}ec2-user{% endif %}].*)NOPASSWD(.*)' 53 | replace: '\1PASSWD\2' 54 | validate: '/usr/sbin/visudo -cf %s' 55 | loop: "{{ debian11cis_sudoers_files.stdout_lines }}" 56 | when: 57 | - debian11cis_rule_5_3_4 58 | tags: 59 | - level2-server 60 | - level2-workstation 61 | - patch 62 | - sudo 63 | - rule_5.3.4 64 | 65 | - name: "5.3.5 | PATCH | Ensure re-authentication for privilege escalation is not disabled globally" 66 | ansible.builtin.replace: 67 | path: "{{ item }}" 68 | regexp: '^([^#].*)!authenticate(.*)' 69 | replace: '\1authenticate\2' 70 | validate: '/usr/sbin/visudo -cf %s' 71 | loop: "{{ debian11cis_sudoers_files.stdout_lines }}" 72 | when: 73 | - debian11cis_rule_5_3_5 74 | tags: 75 | - level1-server 76 | - level1-workstation 77 | - patch 78 | - sudo 79 | - rule_5.3.5 80 | 81 | - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly" 82 | block: 83 | - name: "5.3.6 | AUDIT | Ensure sudo authentication timeout is configured correctly | Get files with timeout set" 84 | ansible.builtin.shell: grep -is 'timestamp_timeout' /etc/sudoers /etc/sudoers.d/* | cut -d":" -f1 | uniq | sort 85 | changed_when: false 86 | failed_when: false 87 | register: discovered_sudo_timeout_files 88 | 89 | - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" 90 | ansible.builtin.lineinfile: 91 | path: /etc/sudoers 92 | regexp: '^\s*Defaults/s+timestamp_timeout=' 93 | line: "Defaults timestamp_timeout={{ debian11cis_sudo_timestamp_timeout }}" 94 | insertafter: '^\s*Defaults' 95 | validate: '/usr/sbin/visudo -cf %s' 96 | when: discovered_sudo_timeout_files.stdout | length == 0 97 | 98 | - name: "5.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" 99 | ansible.builtin.replace: 100 | path: "{{ item }}" 101 | regexp: 'timestamp_timeout=(\d+)' 102 | replace: "timestamp_timeout={{ debian11cis_sudo_timestamp_timeout }}" 103 | validate: '/usr/sbin/visudo -cf %s' 104 | loop: "{{ discovered_sudo_timeout_files.stdout_lines }}" 105 | when: discovered_sudo_timeout_files.stdout | length > 0 106 | when: 107 | - debian11cis_rule_5_3_6 108 | tags: 109 | - level1-server 110 | - level1-workstation 111 | - patch 112 | - sudo 113 | - rule_5.3.6 114 | 115 | - name: "5.3.7 | PATCH | Ensure access to the su command is restricted" 116 | block: 117 | 118 | - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | Ensure sugroup exists" 119 | ansible.builtin.group: 120 | name: "{{ debian11cis_sugroup }}" 121 | state: present 122 | 123 | - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | remove users from group" 124 | ansible.builtin.lineinfile: 125 | path: /etc/group 126 | regexp: '^{{ debian11cis_sugroup }}(:.:.*:).*$' 127 | line: '{{ debian11cis_sugroup }}\g<1>' 128 | backrefs: true 129 | 130 | - name: "5.3.7 | PATCH | Ensure access to the su command is restricted | Setting pam_wheel to use_uid" 131 | ansible.builtin.lineinfile: 132 | path: /etc/pam.d/su 133 | regexp: '^(#)?auth\s+required\s+pam_wheel\.so' 134 | line: 'auth required pam_wheel.so use_uid group={{ debian11cis_sugroup }}' 135 | when: 136 | - debian11cis_rule_5_3_7 137 | tags: 138 | - level1-server 139 | - level1-workstation 140 | - patch 141 | - sudo 142 | - rule_5.3.7 143 | -------------------------------------------------------------------------------- /tasks/section_5/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 5.1 | Configure time-based job schedulers" 4 | ansible.builtin.import_tasks: 5 | file: cis_5.1.x.yml 6 | 7 | - name: "SECTION | 5.2 | Configure sudo" 8 | ansible.builtin.import_tasks: 9 | file: cis_5.2.x.yml 10 | when: not system_is_container 11 | 12 | - name: "SECTION | 5.3 | Configure SSH Server" 13 | ansible.builtin.import_tasks: 14 | file: cis_5.3.x.yml 15 | when: not system_is_container 16 | 17 | - name: "SECTION | 5.4.x | User PAM" 18 | ansible.builtin.import_tasks: 19 | file: cis_5.4.x.yml 20 | when: not system_is_container 21 | 22 | - name: "SECTION | 5.5.x | User Accounts and Environment" 23 | ansible.builtin.import_tasks: 24 | file: cis_5.5.x.yml 25 | -------------------------------------------------------------------------------- /tasks/section_6/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "SECTION | 6.1 | System File Permissions" 4 | ansible.builtin.import_tasks: 5 | file: cis_6.1.x.yml 6 | 7 | - name: "SECTION | 6.2 | User and Group Settings" 8 | ansible.builtin.import_tasks: 9 | file: cis_6.2.x.yml 10 | -------------------------------------------------------------------------------- /tasks/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." 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 | ## This file is managed by Ansible, YOUR CHANGES WILL BE LOST! 2 | 3 | # This file contains users whose actions are not logged by auditd 4 | {% if allow_auditd_uid_user_exclusions %} 5 | {% for user in deb11cis_auditd_uid_exclude %} 6 | -a never,user -F uid!={{ user }} -F auid!={{ user }} 7 | {% endfor %} 8 | {% endif %} 9 | -------------------------------------------------------------------------------- /templates/audit/99_auditd.rules.j2: -------------------------------------------------------------------------------- 1 | 2 | ## Ansible controlled file 3 | # Added as part of ansible-lockdown CIS baseline 4 | # provided by Mindpoint Group - A Tyto Athene Company YOUR CHANGED WILL BE LOST! 5 | 6 | # This template will set all of the auditd configurations via a handler in the role in one task instead of individually 7 | 8 | {% if debian11cis_rule_4_1_3_1 %} 9 | -w /etc/sudoers -p wa -k scope 10 | -w /etc/sudoers.d/ -p wa -k scope 11 | {% endif %} 12 | {% if debian11cis_rule_4_1_3_2 %} 13 | -a always,exit -F arch=b64 -C euid!=uid -F euid=0 -F auid>=1000 -F auid!=4294967295 -S execve -k actions 14 | -a always,exit -F arch=b32 -C euid!=uid -F euid=0 -F auid>=1000 -F auid!=4294967295 -S execve -k actions 15 | {% endif %} 16 | {% if debian11cis_rule_4_1_3_3 %} 17 | -w {{ debian11cis_sudo_logfile }} -p wa -k sudo_log_file 18 | {% endif %} 19 | {% if debian11cis_rule_4_1_3_4 %} 20 | -a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change 21 | -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time-change 22 | -w /etc/localtime -p wa -k time-change 23 | {% endif %} 24 | {% if debian11cis_rule_4_1_3_5 %} 25 | -a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale 26 | -a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale 27 | -w /etc/issue -p wa -k system-locale 28 | -w /etc/issue.net -p wa -k system-locale 29 | -w /etc/hosts -p wa -k system-locale 30 | -w /etc/networks -p wa -k system-locale 31 | -w /etc/network/ -p wa -k system-locale 32 | {% endif %} 33 | {% if debian11cis_rule_4_1_3_6 %} 34 | {% if discovered_privilege_process is defined %} 35 | {% for proc in discovered_privilege_process.stdout_lines -%} 36 | -a always,exit -F path={{ proc }} -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged 37 | {% endfor %} 38 | {% endif %} 39 | {% endif %} 40 | {% if debian11cis_rule_4_1_3_7 %} 41 | -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access 42 | -a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access 43 | -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access 44 | -a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access 45 | {% endif %} 46 | {% if debian11cis_rule_4_1_3_8 %} 47 | -w /etc/group -p wa -k identity 48 | -w /etc/passwd -p wa -k identity 49 | -w /etc/gshadow -p wa -k identity 50 | -w /etc/shadow -p wa -k identity 51 | -w /etc/security/opasswd -p wa -k identity 52 | {% endif %} 53 | {% if debian11cis_rule_4_1_3_9 %} 54 | -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod 55 | -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=4294967295 -k perm_mod 56 | -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod 57 | -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod 58 | -a always,exit -F arch=b32 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=4294967295 -k perm_mod 59 | -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod 60 | {% endif %} 61 | {% if debian11cis_rule_4_1_3_10 %} 62 | -a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts 63 | -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts 64 | {% endif %} 65 | {% if debian11cis_rule_4_1_3_11 %} 66 | -w /var/run/utmp -p wa -k session 67 | -w /var/log/wtmp -p wa -k session 68 | -w /var/log/btmp -p wa -k session 69 | {% endif %} 70 | {% if debian11cis_rule_4_1_3_12 %} 71 | -w /var/log/faillock -p wa -k logins 72 | -w /var/log/lastlog -p wa -k logins 73 | {% endif %} 74 | {% if debian11cis_rule_4_1_3_13 %} 75 | -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k delete 76 | -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k delete 77 | {% endif %} 78 | {% if debian11cis_rule_4_1_3_14 %} 79 | -w /etc/apparmor/ -p wa -k MAC-policy 80 | -w /etc/apparmor.d/ -p wa -k MAC-policy 81 | {% endif %} 82 | {% if debian11cis_rule_4_1_3_15 %} 83 | -a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=1000 -F auid!=unset -k perm_chng 84 | {% endif %} 85 | {% if debian11cis_rule_4_1_3_16 %} 86 | -a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=1000 -F auid!=unset -k perm_chng 87 | {% endif %} 88 | {% if debian11cis_rule_4_1_3_17 %} 89 | -a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=1000 -F auid!=unset -k priv_cmd 90 | {% endif %} 91 | {% if debian11cis_rule_4_1_3_18 %} 92 | -a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=1000 -F auid!=unset -k usermod 93 | {% endif %} 94 | {% if debian11cis_rule_4_1_3_19 %} 95 | -a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=1000 -F auid!=unset -k kernel_modules 96 | -a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -F auid>=1000 -F auid!=unset -k kernel_modules 97 | -a always,exit -F arch=b32 -S init_module,finit_module,delete_module,create_module,query_module -F auid>=1000 -F auid!=unset -k kernel_modules 98 | {% endif %} 99 | {% if debian11cis_rule_4_1_3_20 %} 100 | -e 2 101 | {% endif %} 102 | -------------------------------------------------------------------------------- /templates/chrony.conf.j2: -------------------------------------------------------------------------------- 1 | # Welcome to the chrony configuration file. See chrony.conf(5) for more 2 | # information about usuable directives. 3 | 4 | # This will use (up to): 5 | # - 4 sources from ntp.debian.com which some are ipv6 enabled 6 | # - 2 sources from 2.debian.pool.ntp.org which is ipv6 enabled as well 7 | # - 1 source from [01].debian.pool.ntp.org each (ipv4 only atm) 8 | # This means by default, up to 6 dual-stack and up to 2 additional IPv4-only 9 | # sources will be used. 10 | # At the same time it retains some protection against one of the entries being 11 | # down (compare to just using one of the lines). See (LP: #1754358) for the 12 | # discussion. 13 | # 14 | # About using servers from the NTP Pool Project in general see (LP: #104525). 15 | # Approved by debian Technical Board on 2011-02-08. 16 | # See http://www.pool.ntp.org/join.html for more information. 17 | 18 | {% for server in debian11cis_time_synchronization_servers -%} 19 | server {{ server }} {{ debian11cis_chrony_server_options }} 20 | {% endfor %} 21 | 22 | # This directive specify the location of the file containing ID/key pairs for 23 | # NTP authentication. 24 | keyfile /etc/chrony/chrony.keys 25 | 26 | # Set runtime command key. Note that if you change the key (not the 27 | # password) to anything other than 1 you will need to edit 28 | # /etc/ppp/ip-up.d/chrony, /etc/ppp/ip-down.d/chrony, /etc/init.d/chrony 29 | # and /etc/cron.weekly/chrony as these scripts use it to get the password. 30 | 31 | #commandkey 1 32 | 33 | # This directive specify the file into which chronyd will store the rate 34 | # information. 35 | driftfile /var/lib/chrony/chrony.drift 36 | 37 | # Uncomment the following line to turn logging on. 38 | #log tracking measurements statistics 39 | 40 | # Log files location. 41 | logdir /var/log/chrony 42 | 43 | # Stop bad estimates upsetting machine clock. 44 | maxupdateskew 100.0 45 | 46 | # This directive enables kernel synchronisation (every 11 minutes) of the 47 | # real-time clock. Note that it can’t be used along with the 'rtcfile' directive. 48 | rtcsync 49 | 50 | # Dump measurements when daemon exits. 51 | dumponexit 52 | 53 | # Specify directory for dumping measurements. 54 | 55 | dumpdir /var/lib/chrony 56 | 57 | # Let computer be a server when it is unsynchronised. 58 | 59 | local stratum 10 60 | 61 | # Allow computers on the unrouted nets to use the server. 62 | 63 | #allow 10/8 64 | #allow 192.168/16 65 | #allow 172.16/12 66 | 67 | # This directive forces `chronyd' to send a message to syslog if it 68 | # makes a system clock adjustment larger than a threshold value in seconds. 69 | 70 | logchange 0.5 71 | 72 | # This directive defines an email address to which mail should be sent 73 | # if chronyd applies a correction exceeding a particular threshold to the 74 | # system clock. 75 | 76 | # mailonchange root@localhost 0.5 77 | 78 | # This directive tells chrony to regulate the real-time clock and tells it 79 | # Where to store related data. It may not work on some newer motherboards 80 | # that use the HPET real-time clock. It requires enhanced real-time 81 | # support in the kernel. I've commented it out because with certain 82 | # combinations of motherboard and kernel it is reported to cause lockups. 83 | 84 | # rtcfile /var/lib/chrony/chrony.rtc 85 | 86 | # If the last line of this file reads 'rtconutc' chrony will assume that 87 | # the CMOS clock is on UTC (GMT). If it reads '# rtconutc' or is absent 88 | # chrony will assume local time. The line (if any) was written by the 89 | # chrony postinst based on what it found in /etc/default/rcS. You may 90 | # change it if necessary. 91 | rtconutc 92 | 93 | user {{ debian11cis_chrony_user }} 94 | -------------------------------------------------------------------------------- /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 = {{ debian11cis_level_1 }} 11 | level_2_hardening_enabled = {{ debian11cis_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/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 debian11cis_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 debian11cis_time_servers %} 6 | server {{ server.name }} {{ server.options }} 7 | {% endfor %} 8 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-automount_lock.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of CIS 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 CIS 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 CIS 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 CIS 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 CIS 3 | # provided by Mindpoint Group - A Tyto Athene Company 4 | 5 | 6 | # Specify the dconf path 7 | [org/gnome/desktop/session] 8 | 9 | # Number of seconds of inactivity before the screen goes blank 10 | # Set to 0 seconds if you want to deactivate the screensaver. 11 | idle-delay=uint32 {{ debian11cis_screensaver_idle_delay }} 12 | 13 | # Specify the dconf path 14 | [org/gnome/desktop/screensaver] 15 | 16 | # Number of seconds after the screen is blank before locking the screen 17 | lock-delay=uint32 {{ debian11cis_screensaver_lock_delay }} 18 | -------------------------------------------------------------------------------- /templates/etc/dconf/db/00-screensaver_lock.j2: -------------------------------------------------------------------------------- 1 | ## Ansible controlled file 2 | # Added as part of CIS 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/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 < 32 | # might also be helpful. 33 | # 34 | # Note that "restrict" applies to both servers and clients, so a configuration 35 | # that might be intended to block requests from certain clients could also end 36 | # up blocking replies from your own upstream servers. 37 | 38 | # By default, exchange time with everybody, but don't allow configuration. 39 | # The two lines below are required to meet CIS requirements 40 | restrict -4 default kod nomodify notrap nopeer noquery 41 | restrict -6 default kod nomodify notrap nopeer noquery 42 | 43 | # Local users may interrogate the ntp server more closely. 44 | restrict 127.0.0.1 45 | restrict ::1 46 | 47 | # Needed for adding pool entries 48 | restrict source notrap nomodify noquery 49 | 50 | # Clients from this (example!) subnet have unlimited access, but only if 51 | # cryptographically authenticated. 52 | #restrict 192.168.123.0 mask 255.255.255.0 notrust 53 | 54 | 55 | # If you want to provide time to your local subnet, change the next line. 56 | # (Again, the address is an example only.) 57 | #broadcast 192.168.123.255 58 | 59 | # If you want to listen to time broadcasts on your local subnet, de-comment the 60 | # next lines. Please do this only if you trust everybody on the network! 61 | #disable auth 62 | #broadcastclient 63 | 64 | #Changes recquired to use pps synchonisation as explained in documentation: 65 | #http://www.ntp.org/ntpfaq/NTP-s-config-adv.htm#AEN3918 66 | 67 | #server 127.127.8.1 mode 135 prefer # Meinberg GPS167 with PPS 68 | #fudge 127.127.8.1 time1 0.0042 # relative to PPS for my hardware 69 | 70 | #server 127.127.22.1 # ATOM(PPS) 71 | #fudge 127.127.22.1 flag3 1 # enable PPS API 72 | -------------------------------------------------------------------------------- /vars/audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | #### Audit Configuration Settings #### 4 | 5 | # This variable specifies the timeout (in ms) for audit commands that 6 | # take a very long time: if a command takes too long to complete, 7 | # it will be forcefully terminated after the specified duration. 8 | audit_cmd_timeout: 120000 9 | 10 | # if get_audit_binary_method == download change accordingly 11 | audit_bin_url: "https://github.com/goss-org/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-" 12 | 13 | ### Goss Audit Benchmark file ### 14 | ## managed by the control audit_content 15 | # git 16 | audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" 17 | audit_git_version: "benchmark-{{ benchmark_version }}" 18 | 19 | ## Goss configuration information 20 | # Where the goss configs and outputs are stored 21 | audit_out_dir: '/opt' 22 | # Where the goss audit configuration will be stored 23 | audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit" 24 | 25 | # If changed these can affect other products 26 | pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" 27 | post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" 28 | 29 | ## The following should not need changing 30 | 31 | ### Audit binary settings ### 32 | audit_bin_version: 33 | release: v0.4.8 34 | AMD64_checksum: 'sha256:85d00b7bba5f175bec95de7dfe1f71f8f25204914aad4c6f03c8457868eb6e2f' 35 | ARM64_checksum: 'sha256:bca8c898bfd35b94c51455ece6193c95e2cd7b2b183ac2047b2d76291e73e47d' 36 | audit_bin_path: /usr/local/bin/ 37 | audit_bin: "{{ audit_bin_path }}goss" 38 | audit_format: json 39 | 40 | audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_facts.hostname }}.yml" 41 | audit_results: | 42 | The{% if not audit_only %} pre remediation{% endif %} audit results are: {{ pre_audit_results}} 43 | {% if not audit_only %}The post remediation audit results are: {{ post_audit_results }}{% endif %} 44 | 45 | Full breakdown can be found in {{ audit_log_dir }} 46 | -------------------------------------------------------------------------------- /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 already installed 6 | 7 | ## controls 8 | 9 | # Firewall 10 | debian11cis_firewall_package: None 11 | 12 | # Filesystems 13 | 14 | ## Related individual rules 15 | # Aide 16 | debian11cis_rule_1_4_1: false 17 | debian11cis_rule_1_4_2: false 18 | 19 | # AppArmor 20 | debian11cis_rule_1_6_1: false 21 | debian11cis_rule_1_6_2: false 22 | debian11cis_rule_1_6_3: false 23 | debian11cis_rule_1_6_4: false 24 | 25 | # time sync 26 | debian11cis_rule_2_1_1_1: false 27 | debian11cis_rule_2_2_1_2: false 28 | 29 | # Auditd 30 | debian11cis_rule_4_1_1_1: true 31 | debian11cis_rule_4_1_1_2: true 32 | debian11cis_rule_4_1_1_3: true 33 | debian11cis_rule_4_1_1_4: true 34 | debian11cis_rule_4_1_2_1: true 35 | debian11cis_rule_4_1_2_2: true 36 | debian11cis_rule_4_1_2_3: true 37 | # Auditd rules 38 | debian11cis_rule_4_1_3_1: true 39 | debian11cis_rule_4_1_3_2: true 40 | debian11cis_rule_4_1_3_3: true 41 | debian11cis_rule_4_1_3_4: true 42 | debian11cis_rule_4_1_3_5: true 43 | debian11cis_rule_4_1_3_6: true 44 | debian11cis_rule_4_1_3_7: true 45 | debian11cis_rule_4_1_3_8: true 46 | debian11cis_rule_4_1_3_9: true 47 | debian11cis_rule_4_1_3_10: true 48 | debian11cis_rule_4_1_3_11: true 49 | debian11cis_rule_4_1_3_12: true 50 | debian11cis_rule_4_1_3_13: true 51 | debian11cis_rule_4_1_3_14: true 52 | debian11cis_rule_4_1_3_15: true 53 | debian11cis_rule_4_1_3_16: true 54 | debian11cis_rule_4_1_3_17: true 55 | debian11cis_rule_4_1_3_18: true 56 | debian11cis_rule_4_1_3_19: true 57 | debian11cis_rule_4_1_3_20: true 58 | debian11cis_rule_4_1_3_21: true 59 | # Auditd file access 60 | debian11cis_rule_4_1_4_1: true 61 | debian11cis_rule_4_1_4_2: true 62 | debian11cis_rule_4_1_4_3: true 63 | debian11cis_rule_4_1_4_4: true 64 | debian11cis_rule_4_1_4_5: true 65 | debian11cis_rule_4_1_4_6: true 66 | debian11cis_rule_4_1_4_7: true 67 | debian11cis_rule_4_1_4_8: true 68 | debian11cis_rule_4_1_4_9: true 69 | debian11cis_rule_4_1_4_10: true 70 | debian11cis_rule_4_1_4_11: true 71 | 72 | # cron 73 | debian11cis_rule_5_1_1: false 74 | debian11cis_rule_5_1_2: false 75 | debian11cis_rule_5_1_3: false 76 | debian11cis_rule_5_1_4: false 77 | debian11cis_rule_5_1_5: false 78 | debian11cis_rule_5_1_6: false 79 | debian11cis_rule_5_1_7: false 80 | debian11cis_rule_5_1_8: false 81 | 82 | # ssh 83 | debian11cis_rule_5_2_1: false 84 | debian11cis_rule_5_2_2: false 85 | debian11cis_rule_5_2_3: false 86 | debian11cis_rule_5_2_4: false 87 | debian11cis_rule_5_2_5: false 88 | debian11cis_rule_5_2_6: false 89 | debian11cis_rule_5_2_7: false 90 | debian11cis_rule_5_2_8: false 91 | debian11cis_rule_5_2_9: false 92 | debian11cis_rule_5_2_10: false 93 | debian11cis_rule_5_2_11: false 94 | debian11cis_rule_5_2_12: false 95 | debian11cis_rule_5_2_13: false 96 | debian11cis_rule_5_2_14: false 97 | debian11cis_rule_5_2_15: false 98 | debian11cis_rule_5_2_16: false 99 | debian11cis_rule_5_2_17: false 100 | debian11cis_rule_5_2_18: false 101 | debian11cis_rule_5_2_19: false 102 | debian11cis_rule_5_2_20: false 103 | debian11cis_rule_5_2_21: false 104 | debian11cis_rule_5_2_22: false 105 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | min_ansible_version: 2.14.1 4 | 5 | # Set default value for reboot value 6 | change_requires_reboot: false 7 | # The role discovers dynamically (in tasks/main.yml) whether it 8 | # is executed on a container image and sets the variable 9 | # system_is_container the true. Otherwise, the default value 10 | # 'false' is left unchanged. 11 | system_is_container: false 12 | 13 | # Used to control warning summary 14 | warn_control_list: "" 15 | warn_count: 0 16 | 17 | audit_bins: 18 | - /sbin/auditctl 19 | - /sbin/aureport 20 | - /sbin/ausearch 21 | - /sbin/autrace 22 | - /sbin/auditd 23 | - /sbin/augenrules 24 | --------------------------------------------------------------------------------