diff --git a/.github/workflows/devel_pipeline_validation.yml b/.github/workflows/devel_pipeline_validation.yml index dba39dc0..9fbe7aa8 100644 --- a/.github/workflows/devel_pipeline_validation.yml +++ b/.github/workflows/devel_pipeline_validation.yml @@ -29,7 +29,7 @@ Congrats on opening your first pull request and thank you for taking the time to help improve Ansible-Lockdown! Please join in the conversation happening on the [Discord Server](https://www.lockdownenterprise.com/discord) as well. - # This workflow contains a single job which tests the playbook + # This workflow contains a single job that tests the playbook playbook-test: # The type of runner that the job will run on runs-on: ubuntu-latest @@ -44,13 +44,13 @@ steps: - name: Clone ${{ github.event.repository.name }} - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Pull in terraform code for linux servers - - name: Clone github IaC plan - uses: actions/checkout@v3 + - name: Clone GitHub IaC plan + uses: actions/checkout@v4 with: repository: ansible-lockdown/github_linux_IaC path: .github/workflows/github_linux_IaC @@ -74,7 +74,7 @@ pwd ls env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -82,7 +82,7 @@ id: init run: terraform init env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -90,7 +90,7 @@ id: validate run: terraform validate env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -111,9 +111,9 @@ # Aws deployments taking a while to come up insert sleep or playbook fails - name: Sleep for 60 seconds - run: sleep 60s + run: sleep ${{ vars.BUILD_SLEEPTIME }} - # Run the ansible playbook + # Run the Ansibleplaybook - name: Run_Ansible_Playbook uses: arillso/action.playbook@master with: diff --git a/.github/workflows/main_pipeline_validation.yml b/.github/workflows/main_pipeline_validation.yml index 0b149fb3..67ee9d90 100644 --- a/.github/workflows/main_pipeline_validation.yml +++ b/.github/workflows/main_pipeline_validation.yml @@ -18,7 +18,7 @@ # that can run sequentially or in parallel jobs: - # This workflow contains a single job which tests the playbook + # This workflow contains a single job that tests the playbook playbook-test: # The type of runner that the job will run on runs-on: ubuntu-latest @@ -33,13 +33,13 @@ steps: - name: Clone ${{ github.event.repository.name }} - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} # Pull in terraform code for linux servers - - name: Clone github IaC plan - uses: actions/checkout@v3 + - name: Clone GitHub IaC plan + uses: actions/checkout@v4 with: repository: ansible-lockdown/github_linux_IaC path: .github/workflows/github_linux_IaC @@ -63,7 +63,7 @@ pwd ls env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -71,7 +71,7 @@ id: init run: terraform init env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -79,7 +79,7 @@ id: validate run: terraform validate env: - # Imported from github variables this is used to load the relvent OS.tfvars file + # Imported from GitHub variables this is used to load the relevant OS.tfvars file OSVAR: ${{ vars.OSVAR }} TF_VAR_benchmark_type: ${{ vars.BENCHMARK_TYPE }} @@ -100,9 +100,9 @@ # Aws deployments taking a while to come up insert sleep or playbook fails - name: Sleep for 60 seconds - run: sleep 60s + run: sleep ${{ vars.BUILD_SLEEPTIME }} - # Run the ansible playbook + # Run the Ansibleplaybook - name: Run_Ansible_Playbook uses: arillso/action.playbook@master with: diff --git a/.github/workflows/update_galaxy.yml b/.github/workflows/update_galaxy.yml index 951a53cb..f9352800 100644 --- a/.github/workflows/update_galaxy.yml +++ b/.github/workflows/update_galaxy.yml @@ -1,11 +1,7 @@ --- -# This is a basic workflow to help you get started with Actions - name: update galaxy -# Controls when the action will run. -# Triggers the workflow on merge request events to the main branch on: push: branches: @@ -14,8 +10,10 @@ jobs: update_role: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: robertdebock/galaxy-action@master + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Action Ansible Galaxy Release ${{ github.ref_name }} + uses: ansible-actions/ansible-galaxy-action@main with: - galaxy_api_key: ${{ secrets.GALAXY_API_KEY }} - git_branch: main + galaxy_api_key: ${{ secrets.GALAXY_API_KEY }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9fa68a00..f645faf8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v4.5.0 hooks: # Safety - id: detect-aws-credentials @@ -37,14 +37,14 @@ repos: exclude: .config/.gitleaks-report.json tasks/parse_etc_password - repo: https://github.com/gitleaks/gitleaks - rev: v8.17.0 + rev: v8.18.1 hooks: - id: gitleaks args: ['--baseline-path', '.config/.gitleaks-report.json'] exclude: .config/.secrets.baseline - repo: https://github.com/ansible-community/ansible-lint - rev: v6.17.2 + rev: v6.22.1 hooks: - id: ansible-lint name: Ansible-lint @@ -63,6 +63,6 @@ repos: - ansible-core>=2.10.1 - repo: https://github.com/adrienverge/yamllint.git - rev: v1.32.0 # or higher tag + rev: v1.33.0 # or higher tag hooks: - id: yamllint diff --git a/Changelog.md b/Changelog.md index ccdbd51c..2d94cfc0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,12 @@ # Change log for Ubuntu 2004 +## v2.0.1 based upon CIS 2.0.1 + +- ability to run goss audit only audit_only variable + - audit vars mainly move dto var/audit.yml +- several control updates +- goss version update to 0.4.4 + ## V2.0 based upon CIS 2.0.1 - v2.0.1 - refer to change history from official CIS pdf. diff --git a/README.md b/README.md index 8a0cb551..46738e1f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ ![followers](https://img.shields.io/github/followers/ansible-lockdown?style=social) [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/AnsibleLockdown.svg?style=social&label=Follow%20%40AnsibleLockdown)](https://twitter.com/AnsibleLockdown) -![Ansible Galaxy Quality](https://img.shields.io/ansible/quality/54777?label=Quality&&logo=ansible) ![Discord Badge](https://img.shields.io/discord/925818806838919229?logo=discord) ![Release Branch](https://img.shields.io/badge/Release%20Branch-Main-brightgreen) diff --git a/collections/requirements.yml b/collections/requirements.yml index 23596ec0..8ebc6180 100644 --- a/collections/requirements.yml +++ b/collections/requirements.yml @@ -2,7 +2,13 @@ collections: - name: community.general + source: https://github.com/ansible-collections/community.general + type: git - name: community.crypto + source: https://github.com/ansible-collections/community.crypto + type: git - name: ansible.posix + source: https://github.com/ansible-collections/ansible.posix + type: git diff --git a/defaults/main.yml b/defaults/main.yml index 464def5a..9ad090d3 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -18,26 +18,53 @@ benchmark: UBUNTU20-CIS ## metadata for Audit benchmark benchmark_version: 'v2.0.1' -### Audit Binary is required on the remote host +########################################## +### Goss is required on the remote host ### +## Refer to vars/auditd.yml for any other settings ## + +# Allow audit to setup the requirements including installing git (if option chosen and downloading and adding goss binary to system) setup_audit: false + +# enable audits to run - this runs the audit and get the latest content +run_audit: false + +# Only run Audit do not remediate +audit_only: false +# As part of audit_only +# This will enable files to be copied back to control node +fetch_audit_files: false +# Path to copy the files to will create dir structure +audit_capture_files_dir: /some/location to copy to on control node + # How to retrieve audit binary # Options are copy or download - detailed settings at the bottom of this file # you will need to access to either github or the file already dowmloaded get_audit_binary_method: download +## if get_audit_binary_method - copy the following needs to be updated for your environment +## it is expected that it will be copied from somewhere accessible to the control node +## e.g copy from ansible control node to remote host +audit_bin_copy_location: /some/accessible/path + # how to get audit files onto host options # options are git/copy/get_url other e.g. if you wish to run from already downloaded conf audit_content: git -# enable audits to run - this runs the audit and get the latest content -run_audit: false +# archive or copy: +audit_conf_copy: "some path to copy from" + +# get_url: +audit_files_url: "some url maybe s3?" # Run heavy tests - some tests can have more impact on a system enabling these can have greater impact on a system audit_run_heavy_tests: true -# Timeout for those cmds that take longer to run where timeout set -audit_cmd_timeout: 60000 -### End Audit enablements #### +# This variable specifies the timeout (in ms) for audit commands that +# take a very long time: if a command takes too long to complete, +# it will be forcefully terminated after the specified duration. +audit_cmd_timeout: 120000 + +### End Goss enablements #### # We've defined complexity-high to mean that we cannot automatically remediate # the rule in question. In the future this might mean that the remediation @@ -415,7 +442,7 @@ ubtu20cis_dovecot_server: false ubtu20cis_smb_server: false ubtu20cis_squid_server: false ubtu20cis_snmp_server: false -ubtu20cis_rsync_server: false +ubtu20cis_rsync_server: mask # Can be set to true, mask or remove depending on requirements ubtu20cis_nis_server: false ubtu20cis_nfs_client: false # rpcbind is required by nfs-common which is required on client and server @@ -467,15 +494,17 @@ ubtu20cis_aide_cron: aide_weekday: '*' # Control 1.4.1 -# THIS VARIABLE SHOULD BE CHANGED +# THESE VARIABLES SHOULD BE CHANGED # This will fail assertion if not changed and rule 1.4.2 is enabled # insert password as per output of running grub-mkpasswd-pbkdf2 # refers to https://help.ubuntu.com/community/Grub2/Passwords +# You maybe changing the root password if grub user root - Ensure you understand the risks +ubtu20cis_set_grub_user_password: false +ubtu20cis_grub_user: root +ubtu20cis_grub_user_passwd: '$y$j9T$MBA5l/tQyWifM869nQjsi.$cTy0ConcNjIYOn6Cppo5NAky20osrkRxz4fEWA8xac6' # Set to changeme ubtu20cis_set_boot_pass: false -ubtu20cis_set_grub_password: true ubtu20cis_grub_user_file: /etc/grub.d/40_custom -ubtu20cis_grub_user: root ubtu20cis_grub_file: /boot/grub/grub.cfg # This is used to set the password in grub the full string is required. @@ -739,51 +768,3 @@ ubtu20cis_sgid_adjust: false # Control 6.2.5 Allow ansible to adjust world-writable files. False will just display world-writable files, True will remove world-writable # ubtu20cis_passwd_label: "{{ (this_item | default(item)).id }}: {{ (this_item | default(item)).dir }}" ubtu20cis_passwd_label: "{{ (this_item | default(item)).id }}: {{ (this_item | default(item)).dir }}" - -#### Audit Configuration Settings #### - -### Audit binary settings ### -audit_bin_version: - release: v0.3.23 - checksum: 'sha256:9e9f24e25f86d6adf2e669a9ffbe8c3d7b9b439f5f877500dea02ba837e10e4d' -audit_bin_path: /usr/local/bin/ -audit_bin: "{{ audit_bin_path }}goss" -audit_format: json - -# if get_audit_binary_method == download change accordingly -audit_bin_url: "https://github.com/aelsabbahy/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-amd64" - -## if get_audit_binary_method - copy the following needs to be updated for your environment -## it is expected that it will be copied from somewhere accessible to the control node -## e.g copy from ansible control node to remote host -audit_bin_copy_location: /some/accessible/path - -### Goss Audit Benchmark file ### -## managed by the control audit_content -# git -audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" -audit_git_version: "benchmark_{{ benchmark_version }}" - -# archive or copy: -audit_conf_copy: "some path to copy from" - -# get_url: -audit_files_url: "some url maybe s3?" - -## Goss configuration information -# Where the goss configs and outputs are stored -audit_out_dir: '/opt' -# Where the goss audit configuration will be stored -audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit/" - -# If changed these can affect other products -pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_pre_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" -post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_hostname }}-{{ benchmark }}_post_scan_{{ ansible_date_time.epoch }}.{{ audit_format }}" - -## The following should not need changing -audit_control_file: "{{ audit_conf_dir }}goss.yml" -audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_hostname }}.yml" -audit_results: | - The pre remediation results are: {{ pre_audit_summary }}. - The post remediation results are: {{ post_audit_summary }}. - Full breakdown can be found in {{ audit_out_dir }} diff --git a/tasks/LE_audit_setup.yml b/tasks/LE_audit_setup.yml index 4ef8469f..7ef94b4a 100644 --- a/tasks/LE_audit_setup.yml +++ b/tasks/LE_audit_setup.yml @@ -1,21 +1,33 @@ --- +- name: Pre Audit Setup | Set audit package name + block: + - name: Pre Audit Setup | Set audit package name | 64bit + ansible.builtin.set_fact: + audit_pkg_arch_name: AMD64 + when: ansible_facts.machine == "x86_64" + + - name: Pre Audit Setup | Set audit package name | ARM64 + ansible.builtin.set_fact: + audit_pkg_arch_name: ARM64 + when: ansible_facts.machine == "arm64" + - name: Pre Audit Setup | Download audit binary ansible.builtin.get_url: - url: "{{ audit_bin_url }}" + url: "{{ audit_bin_url }}{{ audit_pkg_arch_name }}" dest: "{{ audit_bin }}" owner: root group: root - checksum: "{{ audit_bin_version.checksum }}" - mode: 0555 + checksum: "{{ audit_bin_version[audit_pkg_arch_name + '_checksum'] }}" + mode: '0555' when: - get_audit_binary_method == 'download' -- name: Pre Audit Setup | copy audit binary +- name: Pre Audit Setup | Copy audit binary ansible.builtin.copy: src: "{{ audit_bin_copy_location }}" dest: "{{ audit_bin }}" - mode: 0555 + mode: '0555' owner: root group: root when: diff --git a/tasks/audit_only.yml b/tasks/audit_only.yml new file mode 100644 index 00000000..864f5bbe --- /dev/null +++ b/tasks/audit_only.yml @@ -0,0 +1,30 @@ +--- + +- name: Audit_Only | Create local Directories for hosts + ansible.builtin.file: + mode: '0755' + path: "{{ audit_capture_files_dir }}/{{ inventory_hostname }}" + recurse: true + state: directory + when: fetch_audit_files + delegate_to: localhost + become: false + +- name: Audit_only | Get audits from systems and put in group dir + ansible.builtin.fetch: + dest: "{{ audit_capture_files_dir }}/{{ inventory_hostname }}/" + flat: true + mode: '0644' + src: "{{ pre_audit_outfile }}" + when: fetch_audit_files + +- name: Audit_only | Show Audit Summary + when: + - audit_only + ansible.builtin.debug: + msg: "The Audit results are: {{ pre_audit_summary }}." + +- name: Audit_only | Stop Playbook Audit Only selected + when: + - audit_only + ansible.builtin.meta: end_play diff --git a/tasks/main.yml b/tasks/main.yml index 84f0f93c..00e9dc52 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -22,7 +22,7 @@ - '"grub.pbkdf2.sha512.1000" in ubtu20cis_bootloader_password_hash' fail_msg: "The default grub password has not been changed" when: - - ubtu20cis_set_grub_password + - ubtu20cis_set_grub_user_password - ubtu20cis_rule_1_4_2 - name: Ensure root password has been changed @@ -87,11 +87,20 @@ - prelim_tasks - run_audit -- name: Run pre remediation audit tasks - ansible.builtin.import_tasks: - file: pre_remediation_audit.yml +- name: Include audit specific variables + ansible.builtin.include_vars: audit.yml when: + - run_audit or audit_only + - setup_audit + tags: + - setup_audit - run_audit + +- name: Include pre-remediation audit tasks + ansible.builtin.import_tasks: pre_remediation_audit.yml + when: + - run_audit or audit_only + - setup_audit tags: - run_audit diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index d7a17bc6..eb01bc75 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,24 +1,28 @@ --- -- name: "Post Audit | Run post_remediation {{ benchmark }} audit" +- name: Post Audit | Run post_remediation {{ benchmark }} audit ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" changed_when: true - vars: - warn: false + environment: + AUDIT_BIN: "{{ audit_bin }}" + AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_FILE: goss.yml - name: Post Audit | ensure audit files readable by users ansible.builtin.file: path: "{{ item }}" - mode: 0644 + mode: '0644' state: file loop: - "{{ post_audit_outfile }}" - "{{ pre_audit_outfile }}" - name: Post Audit | Capture audit data if json format + when: + - audit_format == "json" block: - - name: "capture data {{ post_audit_outfile }}" - ansible.builtin.shell: "cat {{ post_audit_outfile }}" + - name: capture data {{ post_audit_outfile }} + ansible.builtin.shell: cat {{ post_audit_outfile }} register: post_audit changed_when: false @@ -26,19 +30,17 @@ ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout | from_json | json_query(summary) }}" vars: - summary: 'summary."summary-line"' - when: - - audit_format == "json" + summary: summary."summary-line" - name: Post Audit | Capture audit data if documentation format + when: + - audit_format == "documentation" block: - - name: "Post Audit | capture data {{ post_audit_outfile }}" - ansible.builtin.shell: "tail -2 {{ post_audit_outfile }}" + - name: Post Audit | capture data {{ post_audit_outfile }} + ansible.builtin.shell: tail -2 {{ post_audit_outfile }} register: post_audit changed_when: false - name: Post Audit | Capture post-audit result ansible.builtin.set_fact: post_audit_summary: "{{ post_audit.stdout_lines }}" - when: - - audit_format == "documentation" diff --git a/tasks/pre_remediation_audit.yml b/tasks/pre_remediation_audit.yml index d0882f33..258171a1 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -1,57 +1,58 @@ --- -- name: Audit Binary Setup | Setup the LE audit - ansible.builtin.import_tasks: - file: LE_audit_setup.yml +- name: Pre Audit Setup | Setup the LE audit when: - setup_audit tags: - setup_audit + ansible.builtin.include_tasks: LE_audit_setup.yml -- name: "Pre Audit Setup | Ensure audit_conf_dir exists" +- name: Pre Audit Setup | Ensure {{ audit_conf_dir }} exists ansible.builtin.file: path: "{{ audit_conf_dir }}" state: directory mode: '0755' - name: Pre Audit Setup | If using git for content set up + when: + - audit_content == 'git' block: - name: Pre Audit Setup | Install git ansible.builtin.package: name: git state: present - - name: Pre Audit Setup | retrieve audit content files from git + - name: Pre Audit Setup | Retrieve audit content files from git ansible.builtin.git: repo: "{{ audit_file_git }}" dest: "{{ audit_conf_dir }}" version: "{{ audit_git_version }}" - when: - - audit_content == 'git' -- name: Pre Audit Setup | copy to audit content files to server +- name: Pre Audit Setup | Copy to audit content files to server + when: + - audit_content == 'copy' ansible.builtin.copy: src: "{{ audit_local_copy }}" dest: "{{ audit_conf_dest }}" - mode: 0644 - when: - - audit_content == 'copy' + mode: preserve -- name: Pre Audit Setup | unarchive audit content files on server - ansible.builtin.unarchive: - src: "{{ audit_conf_copy }}" - dest: "{{ audit_conf_dest }}" +- name: Pre Audit Setup | Unarchive audit content files on server when: - audit_content == 'archived' + ansible.builtin.unarchive: + src: "{{ audit_conf_copy }}" + dest: "{{ audit_conf_dir }}" -- name: Pre Audit Setup | get audit content from url +- name: Pre Audit Setup | Get audit content from url + when: + - audit_content == 'get_url' ansible.builtin.get_url: url: "{{ audit_files_url }}" dest: "{{ audit_conf_dir }}" - when: - - audit_content == 'get_url' - name: Pre Audit Setup | Check Goss is available + when: + - run_audit block: - name: Pre Audit Setup | Check for goss file ansible.builtin.stat: @@ -59,33 +60,36 @@ register: goss_available - name: Pre Audit Setup | If audit ensure goss is available - ansible.builtin.assert: - msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" when: - not goss_available.stat.exists - when: - - run_audit + ansible.builtin.assert: + msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" - name: Pre Audit Setup | Copy ansible default vars values to test audit + tags: + - goss_template + - run_audit + when: + - run_audit ansible.builtin.template: src: ansible_vars_goss.yml.j2 dest: "{{ audit_vars_path }}" - mode: 0600 - when: - - run_audit - tags: - - goss_template + mode: '0600' -- name: "Pre Audit | Run pre_remediation {{ benchmark }} audit" +- name: Pre Audit | Run pre_remediation {{ benchmark }} audit ansible.builtin.shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" changed_when: true - vars: - warn: false + environment: + AUDIT_BIN: "{{ audit_bin }}" + AUDIT_CONTENT_LOCATION: "{{ audit_out_dir }}" + AUDIT_FILE: goss.yml - name: Pre Audit | Capture audit data if json format + when: + - audit_format == "json" block: - - name: "capture data {{ pre_audit_outfile }}" - ansible.builtin.shell: "cat {{ pre_audit_outfile }}" + - name: capture data {{ pre_audit_outfile }} + ansible.builtin.shell: cat {{ pre_audit_outfile }} register: pre_audit changed_when: false @@ -93,19 +97,22 @@ ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout | from_json | json_query(summary) }}" vars: - summary: 'summary."summary-line"' - when: - - audit_format == "json" + summary: summary."summary-line" - name: Pre Audit | Capture audit data if documentation format + when: + - audit_format == "documentation" block: - - name: "capture data {{ pre_audit_outfile }}" - ansible.builtin.shell: "tail -2 {{ pre_audit_outfile }}" + - name: Pre Audit | capture data {{ pre_audit_outfile }} | documentation format + ansible.builtin.shell: tail -2 {{ pre_audit_outfile }} register: pre_audit changed_when: false - - name: Pre Audit | Capture pre-audit result + - name: Pre Audit | Capture pre-audit result | documentation format ansible.builtin.set_fact: pre_audit_summary: "{{ pre_audit.stdout_lines }}" + +- name: Audit_Only | Run Audit Only when: - - audit_format == "documentation" + - audit_only + ansible.builtin.import_tasks: audit_only.yml diff --git a/tasks/section_1/cis_1.4.x.yml b/tasks/section_1/cis_1.4.x.yml index f9a41d47..a58aa57b 100644 --- a/tasks/section_1/cis_1.4.x.yml +++ b/tasks/section_1/cis_1.4.x.yml @@ -11,8 +11,8 @@ insertafter: "{{ item.after | default(omit) }}" create: true loop: - - { regexp: '^set superusers', line: 'set superusers="{{ ubtu20cis_grub_user }}"' } - - { regexp: '^password_pbkdf2 {{ ubtu20cis_grub_user }} grub.pbkdf2.*', line: 'password_pbkdf2 {{ ubtu20cis_grub_user }} {{ ubtu20cis_bootloader_password_hash }}', after: 'set superusers="' } + - { regexp: '^set superusers', line: 'set superusers="{{ ubtu20cis_grub_user }}"' } + - { regexp: '^password_pbkdf2 {{ ubtu20cis_grub_user }} grub.pbkdf2.*', line: 'password_pbkdf2 {{ ubtu20cis_grub_user }} {{ ubtu20cis_bootloader_password_hash }}', after: 'set superusers="' } notify: Grub update - name: "1.4.1 | PATCH | Ensure bootloader password is set | allow unrestricted boot" @@ -63,10 +63,10 @@ - name: "1.4.3 | PATCH | Ensure authentication required for single user mode" ansible.builtin.user: name: "{{ ubtu20cis_grub_user }}" - password: "{{ ubtu20cis_bootloader_password_hash }}" + password: "{{ ubtu20cis_grub_user_passwd }}" when: - ubtu20cis_rule_1_4_3 - - ubtu20cis_set_boot_pass + - ubtu20cis_set_grub_user_password tags: - level1-server - level1-workstation diff --git a/tasks/section_1/cis_1.5.x.yml b/tasks/section_1/cis_1.5.x.yml index 153edf12..b4bedc2d 100644 --- a/tasks/section_1/cis_1.5.x.yml +++ b/tasks/section_1/cis_1.5.x.yml @@ -70,6 +70,8 @@ name: apport state: stopped enabled: false + when: + - "'apport' in ansible_facts.packages" - name: "1.5.4 | PATCH | Ensure Automatic Error Reporting is not enabled | remove package" ansible.builtin.package: diff --git a/tasks/section_2/cis_2.1.1.x.yml b/tasks/section_2/cis_2.1.1.x.yml index 84e4bcbb..fe9cdd28 100644 --- a/tasks/section_2/cis_2.1.1.x.yml +++ b/tasks/section_2/cis_2.1.1.x.yml @@ -22,7 +22,9 @@ state: stopped enabled: false masked: true - when: ubtu20cis_time_sync_tool != "systemd-timesyncd" + when: + - ubtu20cis_time_sync_tool != "systemd-timesyncd" + - "'systemd-timesyncd' in ansible_facts.packages" when: - ubtu20cis_rule_2_1_1_1 tags: diff --git a/tasks/section_2/cis_2.2.x.yml b/tasks/section_2/cis_2.2.x.yml index 26301f5c..93ff80e5 100644 --- a/tasks/section_2/cis_2.2.x.yml +++ b/tasks/section_2/cis_2.2.x.yml @@ -328,13 +328,26 @@ - rule_2.2.16 - postfix -- name: "2.2.17 | PATCH | Ensure rsync service is not installed" - ansible.builtin.package: - name: rsync - state: absent +- name: "2.2.17 | PATCH | Ensure rsync service is either not installed or masked" + block: + - name: "2.2.17 | PATCH | Ensure rsync service is either not installed or masked | remove pkg" + ansible.builtin.package: + name: rsync + state: absent + when: + - ubtu20cis_rule_2_2_17 + - ubtu20cis_rsync_server == 'remove' + + - name: "2.2.17 | PATCH | Ensure rsync service is either not installed or masked | mask service" + ansible.builtin.service: + name: rsync.service + state: stopped + enabled: false + masked: true + when: + - ubtu20cis_rule_2_2_17 + - ubtu20cis_rsync_server == 'mask' when: - - ubtu20cis_rule_2_2_17 - - not ubtu20cis_rsync_server - "'rsync' in ansible_facts.packages" tags: - level1-server diff --git a/tasks/section_4/cis_4.3.x.yml b/tasks/section_4/cis_4.3.x.yml index dc6b1b71..a4a5c0fa 100644 --- a/tasks/section_4/cis_4.3.x.yml +++ b/tasks/section_4/cis_4.3.x.yml @@ -17,9 +17,9 @@ - name: "4.3.2 | PATCH | Ensure sudo commands use pty" ansible.builtin.lineinfile: path: /etc/sudoers - regexp: '^Defaults use_' - line: 'Defaults use_pty' - insertafter: '^Defaults' + regexp: '^\s*Defaults\s+use_pty\s*$' + line: 'Defaults use_pty' + insertafter: 'EOF' when: - ubtu20cis_rule_4_3_2 tags: @@ -33,9 +33,9 @@ - name: "4.3.3 | PATCH | Ensure sudo log file exists" ansible.builtin.lineinfile: path: /etc/sudoers - regexp: '^Defaults logfile' - line: 'Defaults logfile="{{ ubtu20cis_sudo_logfile }}"' - insertafter: '^Defaults' + regexp: '^\s*Defaults\s+logfile\s*=' + line: 'Defaults logfile="{{ ubtu20cis_sudo_logfile }}"' + insertafter: 'EOF' when: - ubtu20cis_rule_4_3_3 tags: @@ -89,7 +89,7 @@ - name: "4.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if no results" ansible.builtin.lineinfile: path: /etc/sudoers - regexp: 'Defaults timestamp_timeout=' + regexp: '^\s*Defaults\s+timestamp_timeout\s*=' line: "Defaults timestamp_timeout={{ ubtu20cis_sudo_timestamp_timeout }}" validate: '/usr/sbin/visudo -cf %s' when: ubtu20cis_4_3_6_timeout_files.stdout | length == 0 @@ -97,7 +97,7 @@ - name: "4.3.6 | PATCH | Ensure sudo authentication timeout is configured correctly | Set value if has results" ansible.builtin.replace: path: "{{ item }}" - regexp: 'timestamp_timeout=(\d+)' + regexp: 'timestamp_timeout\s*=\s*(\d+)' replace: "timestamp_timeout={{ ubtu20cis_sudo_timestamp_timeout }}" validate: '/usr/sbin/visudo -cf %s' loop: "{{ ubtu20cis_4_3_6_timeout_files.stdout_lines }}" diff --git a/tasks/section_4/cis_4.5.1.x.yml b/tasks/section_4/cis_4.5.1.x.yml index 151b0ebd..a0b74bcd 100644 --- a/tasks/section_4/cis_4.5.1.x.yml +++ b/tasks/section_4/cis_4.5.1.x.yml @@ -8,14 +8,20 @@ regexp: '^PASS_MIN_DAYS|^#PASS_MIN_DAYS' line: 'PASS_MIN_DAYS {{ ubtu20cis_pass.min_days }}' + - name: "4.5.1.1 | PATCH | Ensure minimum days between password changes is configured | Get existing users PASS_MIN_DAYS" + ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($4<{{ ubtu20cis_pass.min_days }})) {print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: ubtu20cis_4_5_1_1_min_days + - name: "4.5.1.1 | PATCH | Ensure minimum days between password changes is configured | Set existing users PASS_MIN_DAYS" ansible.builtin.shell: chage --mindays {{ ubtu20cis_pass.min_days }} {{ item }} failed_when: false - with_items: - - "{{ ubtu20cis_passwd | selectattr('uid', '>=', 1000) | map(attribute='id') | list }}" + changed_when: ubtu20cis_4_5_1_1_min_days.stdout |length > 0 + loop: "{{ ubtu20cis_4_5_1_1_min_days.stdout_lines }}" when: - ubtu20cis_disruption_high - - item != 'nobody' + - (item != 'root') or (not ubtu20cis_uses_root) when: - ubtu20cis_rule_4_5_1_1 tags: @@ -36,14 +42,20 @@ line: 'PASS_MAX_DAYS {{ ubtu20cis_pass.max_days }}' insertafter: '# Password aging controls' + - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Get existing users PASS_MAX_DAYS" + ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && ($5>{{ ubtu20cis_pass.max_days }} || $5<{{ ubtu20cis_pass.min_days }} || $5 == -1)){print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: ubtu20cis_4_5_1_2_max_days + - name: "4.5.1.2 | PATCH | Ensure password expiration is 365 days or less | Set existing users PASS_MAX_DAYS" ansible.builtin.shell: chage --maxdays {{ ubtu20cis_pass.max_days }} {{ item }} failed_when: false - with_items: - - "{{ ubtu20cis_passwd | selectattr('uid', '>=', 1000) | map(attribute='id') | list }}" + changed_when: ubtu20cis_4_5_1_2_max_days.stdout | length > 0 + loop: "{{ ubtu20cis_4_5_1_2_max_days.stdout_lines }}" when: - ubtu20cis_disruption_high - - item != 'nobody' + - (item != 'root') or (not ubtu20cis_uses_root) when: - ubtu20cis_rule_4_5_1_2 tags: @@ -63,14 +75,20 @@ regexp: '^PASS_WARN_AGE|^#PASS_WARN_AGE' line: 'PASS_WARN_AGE {{ ubtu20cis_pass.warn_age }}' + - name: "4.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Get existing users PASS_WARN_AGE" + ansible.builtin.shell: "awk -F: '(/^[^:]+:[^!*]/ && $6<{{ ubtu20cis_pass.warn_age }}){print $1}' /etc/shadow" + changed_when: false + failed_when: false + register: ubtu20cis_4_5_1_3_warn_days + - name: "4.5.1.3 | PATCH | Ensure password expiration warning days is 7 or more | Set existing users PASS_WARN_AGE" - ansible.builtin.shell: chage --warndays {{ ubtu20cis_pass.warn_age }} {{ item }} + ansible.builtin.shell: chage --maxdays {{ ubtu20cis_pass.warn_age }} {{ item }} failed_when: false - with_items: - - "{{ ubtu20cis_passwd | selectattr('uid', '>=', 1000) | map(attribute='id') | list }}" + changed_when: ubtu20cis_4_5_1_3_warn_days.stdout | length > 0 + loop: "{{ ubtu20cis_4_5_1_3_warn_days.stdout_lines }}" when: - ubtu20cis_disruption_high - - item != 'nobody' + - (item != 'root') or (not ubtu20cis_uses_root) when: - ubtu20cis_rule_4_5_1_3 tags: @@ -173,7 +191,7 @@ - name: "4.5.1.6 | PATCH | Ensure the number of changed characters in a new password is configured" ansible.builtin.lineinfile: path: /etc/security/pwquality.conf - regexp: '^(#\s+|)difok|' + regexp: '^(#\s+|)difok' line: 'difok = {{ ubtu20cis_pass.character_changed }}' create: true mode: 0640 diff --git a/tasks/section_4/cis_4.5.x.yml b/tasks/section_4/cis_4.5.x.yml index 9f851614..43a04129 100644 --- a/tasks/section_4/cis_4.5.x.yml +++ b/tasks/section_4/cis_4.5.x.yml @@ -75,14 +75,13 @@ - name: "4.5.4 | PATCH | Ensure default user umask is 027 or more restrictive" ansible.builtin.lineinfile: - path: "{{ item }}" - regexp: '(?i)(umask\s*)' - line: '\g<1>{{ ubtu20cis_bash_umask }}' - backrefs: true + path: "{{ item.path }}" + regexp: '(?i)(umask\s*\d\d\d)' + line: '{{ item.line }} {{ ubtu20cis_bash_umask }}' with_items: - - /etc/bash.bashrc - - /etc/profile - - /etc/login.defs + - { path: '/etc/bash.bashrc', line: 'umask' } + - { path: '/etc/profile', line: 'umask' } + - { path: '/etc/login.defs', line: 'UMASK' } - name: "4.5.4 | PATCH | Ensure default user umask is 027 or more restrictive" ansible.builtin.lineinfile: diff --git a/tasks/section_5/cis_5.1.2.x.yml b/tasks/section_5/cis_5.1.2.x.yml index 4db266f2..3b4674ca 100644 --- a/tasks/section_5/cis_5.1.2.x.yml +++ b/tasks/section_5/cis_5.1.2.x.yml @@ -97,7 +97,7 @@ - { regexp: '^mail.info|^#mail.info', line: 'mail.info -/var/log/mail.info', insertafter: '^# Logging for the mail system' } - { regexp: '^mail.warn|^#mail.warn', line: 'mail.warn -/var/log/mail.warn', insertafter: '^# Logging for the mail system.' } - { regexp: '^mail.err|^#mail.err', line: 'mail.err /var/log/mail.err', insertafter: '^# Logging for the mail system.' } - - { regexp: '^cron.\*|^#cron.\*', line: 'cron.\* -/var/log/news/news.notice', insertafter: '^# First some standard log files' } + - { regexp: '^cron.\*|^#cron.\*', line: 'cron.\* /var/log/cron', insertafter: '^# First some standard log files' } - { regexp: '^\*.=warning;\*.=err|^#\*.=warning;\*.=err', line: '*.=warning;*.=err -/var/log/warn', insertafter: '^# First some standard log files' } - { regexp: '^\*.crit|^#\*.crit', line: '*.crit /var/log/warn', insertafter: '^# First some standard log files' } - { regexp: '^\*.\*;mail.none;news.none|^#\*.\*;mail.none;news.none', line: '*.*;mail.none;news.none -/var/log/messages', insertafter: '^# First some standard log files' } diff --git a/tasks/section_6/cis_6.1.x.yml b/tasks/section_6/cis_6.1.x.yml index 25ca4c06..94314160 100644 --- a/tasks/section_6/cis_6.1.x.yml +++ b/tasks/section_6/cis_6.1.x.yml @@ -5,7 +5,7 @@ path: /etc/passwd owner: root group: root - mode: 0644 + mode: '0644' when: - ubtu20cis_rule_6_1_1 tags: @@ -21,7 +21,7 @@ path: /etc/passwd- owner: root group: root - mode: 0600 + mode: '0600' when: - ubtu20cis_rule_6_1_2 tags: @@ -37,7 +37,7 @@ path: /etc/group owner: root group: root - mode: 0644 + mode: '0644' when: - ubtu20cis_rule_6_1_3 tags: @@ -53,7 +53,7 @@ path: /etc/group- owner: root group: root - mode: 0644 + mode: '0644' when: - ubtu20cis_rule_6_1_4 tags: @@ -69,7 +69,7 @@ path: /etc/shadow owner: root group: shadow - mode: 0640 + mode: '0640' when: - ubtu20cis_rule_6_1_5 tags: @@ -85,7 +85,7 @@ path: /etc/shadow- owner: root group: shadow - mode: 0640 + mode: '0640' when: - ubtu20cis_rule_6_1_6 tags: @@ -101,7 +101,7 @@ path: /etc/gshadow owner: root group: shadow - mode: 0640 + mode: '0640' when: - ubtu20cis_rule_6_1_7 tags: @@ -117,7 +117,7 @@ path: /etc/gshadow- owner: root group: shadow - mode: 0640 + mode: '0640' when: - ubtu20cis_rule_6_1_8 tags: @@ -133,7 +133,7 @@ path: /etc/shells owner: root group: root - mode: 0644 + mode: '0644' when: - ubtu20cis_rule_6_1_9 tags: @@ -156,7 +156,7 @@ path: /etc/opasswd owner: root group: root - mode: 0600 + mode: '0600' state: touch when: opasswd.stat.exists @@ -170,7 +170,7 @@ path: /etc/opasswd.old owner: root group: root - mode: 0600 + mode: '0600' when: opasswd_old.stat.exists when: - ubtu20cis_rule_6_1_10 @@ -258,7 +258,7 @@ vars: warn_control_id: '6.1.12_unowned' - - name: "6.1.12 | AUDIT | Ensure no unowned or ungrouped files or directories exist | Get ungrouped fiels or directories" + - name: "6.1.12 | AUDIT | Ensure no unowned or ungrouped files or directories exist | Get ungrouped files or directories" ansible.builtin.shell: find {{ item.mount }} -xdev -nogroup -not -fstype nfs changed_when: false failed_when: false diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index 672791a7..826549d5 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -409,7 +409,6 @@ with_items: "{{ ubtu20cis_6_2_12_audit.stdout_lines }}" when: - ubtu20cis_6_2_12_audit.stdout | length > 0 - - ubtu20cis_dotperm_ansibleManaged - name: "6.2.12 | PATCH | Ensure local interactive user dot files access is configured | Ensure no users have .netrc files" ansible.builtin.file: diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 index 69463623..9ed5b421 100644 --- a/templates/ansible_vars_goss.yml.j2 +++ b/templates/ansible_vars_goss.yml.j2 @@ -1,19 +1,20 @@ -audit_run: ansible # This is forced to wrapper by running the run_audit wrapper script (placeholder only if run via ansible) +--- + ## metadata for Audit benchmark benchmark_version: '2.0.1' # Some audit tests may need to scan every filesystem or have an impact on a system # these may need be scheduled to minimise impact also ability to set a timeout if taking too long -run_heavy_tests: {{ audit_run_heavy_tests }} + timeout_ms: {{ audit_cmd_timeout }} -ubtu20cis_section1: true -ubtu20cis_section2: true -ubtu20cis_section3: true -ubtu20cis_section4: true -ubtu20cis_section5: true -ubtu20cis_section6: true +ubtu20cis_section1: {{ ubtu20cis_section1_patch }} +ubtu20cis_section2: {{ ubtu20cis_section2_patch }} +ubtu20cis_section3: {{ ubtu20cis_section3_patch }} +ubtu20cis_section4: {{ ubtu20cis_section4_patch }} +ubtu20cis_section5: {{ ubtu20cis_section5_patch }} +ubtu20cis_section6: {{ ubtu20cis_section6_patch }} ubtu20cis_level_1: true ubtu20cis_level_2: true @@ -21,7 +22,7 @@ ubtu20cis_level_2: true ubtu20cis_apparmor_disable: true # to enable rules that may have IO impact on a system e.g. full filesystem scans or CPU heavy -run_heavy_tests: true +run_heavy_tests: {{ audit_run_heavy_tests }} # True is BIOS based system else set to false ubtu20_legacy_boot: true @@ -412,7 +413,7 @@ ubtu20cis_ldap_server: {{ ubtu20cis_ldap_server }} ubtu20cis_dns_server: {{ ubtu20cis_dns_server }} ubtu20cis_vsftpd_server: {{ ubtu20cis_vsftpd_server }} ubtu20cis_httpd_server: {{ ubtu20cis_httpd_server }} -ubtu20cis_is_mail_server: false +ubtu20cis_is_mail_server: {{ ubtu20cis_is_mail_server }} ubtu20cis_dovecot_server: {{ ubtu20cis_dovecot_server }} ubtu20cis_samba_server: {{ ubtu20cis_smb_server }} ubtu20cis_squid_server: {{ ubtu20cis_squid_server }} @@ -454,7 +455,7 @@ ubtu20cis_rpc_required: {{ ubtu20cis_rpc_required }} ubtu20cis_ipv6_required: {{ ubtu20cis_ipv6_required }} # System network parameters (host only OR host and router) -ubtu20cis_is_router: false +ubtu20cis_is_router: {{ ubtu20cis_is_router }} ubtu20cis_firewall: {{ ubtu20cis_firewall_package }} diff --git a/templates/audit/ubtu20cis_5_2_3_12_logins.rules.j2 b/templates/audit/ubtu20cis_5_2_3_12_logins.rules.j2 index c26bc611..83615822 100644 --- a/templates/audit/ubtu20cis_5_2_3_12_logins.rules.j2 +++ b/templates/audit/ubtu20cis_5_2_3_12_logins.rules.j2 @@ -1,2 +1,2 @@ --w /var/log/faillock -p wa -k logins +-w /var/run/faillock -p wa -k logins -w /var/log/lastlog -p wa -k logins diff --git a/vars/audit.yml b/vars/audit.yml new file mode 100644 index 00000000..e8d2b3f0 --- /dev/null +++ b/vars/audit.yml @@ -0,0 +1,41 @@ +--- + +#### Audit Configuration Settings #### + +# Timeout for those cmds that take longer to run where timeout set +audit_cmd_timeout: 120000 + +# if get_audit_binary_method == download change accordingly +audit_bin_url: "https://github.com/goss-org/goss/releases/download/{{ audit_bin_version.release }}/goss-linux-" + +### Goss Audit Benchmark file ### +## managed by the control audit_content +# git +audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" +audit_git_version: "benchmark_{{ benchmark_version }}" + +## Goss configuration information +# Where the goss configs and outputs are stored +audit_out_dir: '/opt' +# Where the goss audit configuration will be stored +audit_conf_dir: "{{ audit_out_dir }}/{{ benchmark }}-Audit" + +# If changed these can affect other products +pre_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_pre_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" +post_audit_outfile: "{{ audit_out_dir }}/{{ ansible_facts.hostname }}-{{ benchmark }}-{{ benchmark_version }}_post_scan_{{ ansible_facts.date_time.epoch }}.{{ audit_format }}" + +## The following should not need changing + +### Audit binary settings ### +audit_bin_version: + release: v0.4.4 + AMD64_checksum: 'sha256:1c4f54b22fde9d4d5687939abc2606b0660a5d14a98afcd09b04b793d69acdc5' +audit_bin_path: /usr/local/bin/ +audit_bin: "{{ audit_bin_path }}goss" +audit_format: json + +audit_vars_path: "{{ audit_conf_dir }}/vars/{{ ansible_facts.hostname }}.yml" +audit_results: | + The pre remediation results are: {{ pre_audit_summary }}. + The post remediation results are: {{ post_audit_summary }}. + Full breakdown can be found in {{ audit_out_dir }}