diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..b2daffb9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# adding github settings to show correct language +*.sh linguist-detectable=true +*.yml linguist-detectable=true +*.ps1 linguist-detectable=true +*.j2 linguist-detectable=true +*.md linguist-documentation diff --git a/Changelog.md b/Changelog.md index c472014e..de3186a5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,24 @@ # Changes to rhel8CIS +## 1.3.3 + +- update to audit script + - variable for audit OS agnostic + - removal of included library module (not required) + +- Issues included + - #135 - running levels - upadted tags + - #138 - auditd immutable + - #139 - 5.2.13 valus updated + - #140 + - #141 - check mode update + - #142 + - #143 - labels added + - #144 + - #146 - undefined variable added + - #147 - removed warn statement + - #149 - shell timeout + ## 1.3.2 - issues with crypto policies on ec2 - added skip for rules if system_is_ec2 variable diff --git a/defaults/main.yml b/defaults/main.yml index 0de2c930..1ee8d95e 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -487,8 +487,8 @@ rhel8cis_system_is_log_server: false ## Section5 vars rhel8cis_sshd: - clientalivecountmax: 3 - clientaliveinterval: 300 + clientalivecountmax: 0 + clientaliveinterval: 900 ciphers: "aes256-ctr,aes192-ctr,aes128-ctr" macs: "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com" logingracetime: 60 @@ -592,19 +592,19 @@ goss_url: "https://github.com/aelsabbahy/goss/releases/download/{{ goss_version. copy_goss_from_path: /some/accessible/path ### Goss Audit Benchmark file ### -## managed by the control rhel8cis_audit_content +## managed by the control audit_content # git -rhel8cis_audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" -rhel8cis_audit_git_version: main +audit_file_git: "https://github.com/ansible-lockdown/{{ benchmark }}-Audit.git" +audit_git_version: main # copy: -rhel8cis_audit_local_copy: "some path to copy from" +audit_local_copy: "some path to copy from" # get_url: -rhel8cis_audit_files_url: "some url maybe s3?" +audit_files_url: "some url maybe s3?" # Where the goss audit configuration will be stored -rhel8cis_audit_files: "/var/tmp/{{ benchmark }}-Audit/" +audit_files: "/var/tmp/{{ benchmark }}-Audit/" ## Goss configuration information # Where the goss configs and outputs are stored diff --git a/library/goss.py b/library/goss.py deleted file mode 100644 index 76cf3ea5..00000000 --- a/library/goss.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 -# FROM: https://github.com/indusbox/goss-ansible - -import os - -from ansible.module_utils.basic import AnsibleModule - - -DOCUMENTATION = ''' ---- -module: goss -author: Mathieu Corbin -short_description: Launch goss (https://github.com/aelsabbahy/goss) tests -description: - - Launch goss tests. - This module always returns `changed = false` for idempotence. -options: - path: - required: true - description: - - Test file to validate. - The test file must be on the remote machine. - goss_path: - required: false - description: - - Path location for the goss executable. - Default is "goss" (ie.`no absolute path, goss executable must be available in $PATH). - vars_path: - required: false - description: - - Path location for a variables YAML/JSON file to use as templating inputs. - format: - required: false - description: - - Output goss format. - Goss format list : goss v --format => [documentation json junit nagios nagios_verbose rspecish tap silent]. - Default is "rspecish". - format_options: - required: false - description: - - Extra options passed to the formatter, valid options: [perfdata pretty verbose] - Goss format options: goss -v --format json --format_options pretty - default: null - output_file: - required: false - description: - - Save the result of the goss command in a file whose path is output_file -examples: - - name: run goss against the gossfile /path/to/file.yml - goss: - path: "/path/to/file.yml" - - name: run goss against the gossfile /path/to/file.yml with nagios output - goss: - path: "/path/to/file.yml" - format: "nagios" - - name: run /usr/local/bin/goss against the gossfile /path/to/file.yml - goss: - path: "/path/to/file.yml" - goss_path: "/usr/local/bin/goss" - - name: run /usr/local/bin/goss with a variables file - goss: - vars_path: "/path/to/file.yml" - - name: run goss against multiple gossfiles and write the result in JSON format to /my/output/ for each file - goss: - path: "{{ item }}" - format: json - output_file : /my/output/{{ item }} - with_items: "{{ goss_files }}" -''' - - -def check(module, test_file_path, output_format, format_options, goss_path, vars_path): - """ - Launch goss validate command on the file - """ - cmd = f'{ goss_path } --gossfile { test_file_path }' - # goss parent command flags - if vars_path is not None: - cmd += f' --vars { vars_path }' - - # validate sub-command flags - cmd += ' validate' - if output_format is not None: - cmd += f' --format { output_format }' - if format_options is not None: - cmd += f' --format { output_format } --format-options { format_options }' - - - return module.run_command(cmd) - - -def write_result(output_file_path, out): - """ - Write goss result to output_file_path - """ - if output_file_path is not None: - with open(output_file_path, 'w') as output_file: - output_file.write(out) - - -def run_module(): - module = AnsibleModule( - argument_spec=dict( - path=dict(required=True, type='str'), - format=dict(required=False, type='str'), - output_file=dict(required=False, type='str'), - format_options=dict(required=False, type='str'), - vars_path=dict(required=False, type='str'), - goss_path=dict(required=False, default='goss', type='str'), - ), - supports_check_mode=False - ) - - test_file_path = module.params['path'] - output_format = module.params['format'] # goss output format - format_options = module.params['format_options'] # goss format options - output_file_path = module.params['output_file'] - goss_path = module.params['goss_path'] - vars_path = module.params['vars_path'] - - test_file_path = os.path.expanduser(test_file_path) - - if not os.access(test_file_path, os.R_OK): - module.fail_json(msg=f'Test file { test_file_path } not readable') - - if os.path.isdir(test_file_path): - module.fail_json(msg=f'Test file { test_file_path } must be a file but is a path') - - if format_options is not None: - format_options = (format_options) - options = ('pretty', 'perfdata', 'verbose') - if format_options not in options: - module.fail_json(msg=f' format_options { format_options } - must be one of perfdata, pretty or verbose') - - rc, out, err = check(module, test_file_path, output_format, format_options, goss_path, vars_path) - - - if output_file_path is not None: - output_file_path = os.path.expanduser(output_file_path) - - if output_file_path.endswith(os.sep): - module.fail_json(msg=f'output_file { output_file_path } must be a file') - - output_dirname = os.path.dirname(output_file_path) - - if not os.path.exists(output_dirname): - module.fail_json(msg=f'directory { output_dirname } does not exists') - - if not os.access(os.path.dirname(output_file_path), os.W_OK): - module.fail_json(msg=f'Destination { output_dirname } not writable') - - write_result(output_file_path, out) - - if rc is not None and rc != 0: - error_msg = 'err : { err } ; out : { out }' - module.fail_json(msg=error_msg) - - module.exit_json(stdout=out, changed=False) - - -if __name__ == '__main__': - run_module() \ No newline at end of file diff --git a/tasks/parse_etc_password.yml b/tasks/parse_etc_password.yml index 6836752b..e66123f6 100644 --- a/tasks/parse_etc_password.yml +++ b/tasks/parse_etc_password.yml @@ -1,14 +1,14 @@ --- -- name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 6.2.20 | Parse /etc/passwd" +- name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" block: - - name: "PRELIM | {{ rhel8cis_passwd_tasks }} | Parse /etc/passwd" + - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Parse /etc/passwd" command: cat /etc/passwd changed_when: no check_mode: no register: rhel8cis_passwd_file_audit - - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 6.2.20 | Split passwd entries" + - name: "PRELIM | 5.5.2 | 6.2.7 | 6.2.8 | 6.2.20 | Split passwd entries" set_fact: rhel8cis_passwd: "{{ rhel8cis_passwd_file_audit.stdout_lines | map('regex_replace', ld_passwd_regex, ld_passwd_yaml) | map('from_yaml') | list }}" with_items: "{{ rhel8cis_passwd_file_audit.stdout_lines }}" diff --git a/tasks/post_remediation_audit.yml b/tasks/post_remediation_audit.yml index 6bfa7719..17ef3f87 100644 --- a/tasks/post_remediation_audit.yml +++ b/tasks/post_remediation_audit.yml @@ -1,17 +1,11 @@ --- -- name: "Run post_remediation {{ benchmark }} audit" - goss: - goss_path: "{{ audit_bin }}" - path: "{{ goss_file }}" - vars_path: "{{ audit_vars_path }}" - format: "{{ audit_format }}" - output_file: "{{ post_audit_outfile }}" - failed_when: false - environment: - GOSS_FMT_OPTIONS: Pretty +- name: "Post Audit | Run post_remediation {{ benchmark }} audit" + shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ post_audit_outfile }} -g {{ group_names }}" + vars: + warn: false -- name: ensure audit files readable by users +- name: Post Audit | ensure audit files readable by users file: path: "{{ item }}" mode: 0644 @@ -20,7 +14,7 @@ - "{{ post_audit_outfile }}" - "{{ pre_audit_outfile }}" -- name: Capture audit data if json format +- name: Post Audit | Capture audit data if json format block: - name: "capture data {{ post_audit_outfile }}" command: "cat {{ post_audit_outfile }}" @@ -35,14 +29,14 @@ when: - audit_format == "json" -- name: Capture audit data if documentation format +- name: Post Audit | Capture audit data if documentation format block: - - name: "capture data {{ post_audit_outfile }}" + - name: "Post Audit | capture data {{ post_audit_outfile }}" command: "tail -2 {{ post_audit_outfile }}" register: post_audit changed_when: false - - name: Capture post-audit result + - name: Post Audit | Capture post-audit result set_fact: post_audit_summary: "{{ post_audit.stdout_lines }}" when: diff --git a/tasks/pre_remediation_audit.yml b/tasks/pre_remediation_audit.yml index 201b996d..78728628 100644 --- a/tasks/pre_remediation_audit.yml +++ b/tasks/pre_remediation_audit.yml @@ -1,27 +1,27 @@ --- -- name: Setup the audit +- name: Pre Audit | Setup the audit include_tasks: LE_audit_setup.yml when: - setup_audit tags: - setup_audit -- name: "Ensure {{ audit_conf_dir }} exists" +- name: "Pre Audit | Ensure {{ audit_conf_dir }} exists" file: path: "{{ audit_conf_dir }}" state: directory mode: '0755' -- name: If using git for content set up +- name: Pre Audit | If using git for content set up block: - - name: Install git (rh8 python3) + - name: Pre Audit | Install git (rh8 python3) package: name: git state: present when: ansible_distribution_major_version == 8 - - name: Install git (rh7 python2) + - name: Pre Audit | Install git (rh7 python2) package: name: git state: present @@ -29,37 +29,37 @@ ansible_python_interpreter: "{{ python2_bin }}" when: ansible_distribution_major_version == 7 - - name: retrieve audit content files from git + - name: Pre Audit | retrieve audit content files from git git: - repo: "{{ rhel8cis_audit_file_git }}" + repo: "{{ audit_file_git }}" dest: "{{ audit_conf_dir }}" - version: "{{ rhel8cis_audit_git_version }}" + version: "{{ audit_git_version }}" when: - audit_content == 'git' -- name: copy to audit content files to server +- name: Pre Audit | copy to audit content files to server copy: - src: "{{ rhel8cis_audit_local_copy }}" + src: "{{ audit_local_copy }}" dest: "{{ audit_conf_dir }}" mode: 0644 when: - audit_content == 'copy' -- name: get audit content from url +- name: Pre Audit | get audit content from url get_url: - url: "{{ rhel8cis_audit_files_url }}" + url: "{{ audit_files_url }}" dest: "{{ audit_conf_dir }}" when: - audit_content == 'get_url' -- name: Check Goss is available +- name: Pre Audit | Check Goss is available block: - - name: Check for goss file + - name: Pre Audit | Check for goss file stat: path: "{{ audit_bin }}" register: goss_available - - name: If audit ensure goss is available + - name: Pre Audit | If audit ensure goss is available assert: msg: "Audit has been selected: unable to find goss binary at {{ audit_bin }}" when: @@ -67,14 +67,14 @@ when: - run_audit -- name: "Check whether machine is UEFI-based" +- name: "Pre Audit | Check whether machine is UEFI-based" stat: path: /sys/firmware/efi register: rhel8_efi_boot tags: - goss_template -- name: Copy ansible default vars values to test audit +- name: Pre Audit | Copy ansible default vars values to test audit template: src: ansible_vars_goss.yml.j2 dest: "{{ audit_vars_path }}" @@ -84,25 +84,19 @@ tags: - goss_template -- name: "Run pre_remediation {{ benchmark }} audit" - goss: - goss_path: "{{ audit_bin }}" - path: "{{ goss_file }}" - vars_path: "{{ audit_vars_path }}" - format: "{{ audit_format }}" - output_file: "{{ pre_audit_outfile }}" - failed_when: false - environment: - GOSS_FMT_OPTIONS: Pretty - -- name: Capture audit data if json format +- name: "Pre Audit | Run pre_remediation {{ benchmark }} audit" + shell: "{{ audit_conf_dir }}/run_audit.sh -v {{ audit_vars_path }} -o {{ pre_audit_outfile }} -g {{ group_names }}" + vars: + warn: false + +- name: Pre Audit | Capture audit data if json format block: - - name: "capture data {{ pre_audit_outfile }}" + - name: "Pre Audit | capture data {{ pre_audit_outfile }}" command: "cat {{ pre_audit_outfile }}" register: pre_audit changed_when: false - - name: Capture pre-audit result + - name: Pre Audit | Capture pre-audit result set_fact: pre_audit_summary: "{{ pre_audit.stdout | from_json |json_query(summary) }}" vars: @@ -110,14 +104,14 @@ when: - audit_format == "json" -- name: Capture audit data if documentation format +- name: Pre Audit | Capture audit data if documentation format block: - - name: "capture data {{ pre_audit_outfile }}" + - name: "Pre Audit | capture data {{ pre_audit_outfile }}" command: "tail -2 {{ pre_audit_outfile }}" register: pre_audit changed_when: false - - name: Capture pre-audit result + - name: Pre Audit | Capture pre-audit result set_fact: pre_audit_summary: "{{ pre_audit.stdout_lines }}" when: diff --git a/tasks/section_1/cis_1.1.x.yml b/tasks/section_1/cis_1.1.x.yml index adeb29b3..c7cdcedb 100644 --- a/tasks/section_1/cis_1.1.x.yml +++ b/tasks/section_1/cis_1.1.x.yml @@ -254,6 +254,7 @@ shell: mount -l | grep -E '\s/dev/shm\s' changed_when: false failed_when: false + check_mode: no register: rhel8cis_1_1_15_dev_shm_status - name: | diff --git a/tasks/section_3/cis_3.2.x.yml b/tasks/section_3/cis_3.2.x.yml index 4eb33b32..ad2f7964 100644 --- a/tasks/section_3/cis_3.2.x.yml +++ b/tasks/section_3/cis_3.2.x.yml @@ -200,8 +200,8 @@ - rhel8cis_ipv6_required - rhel8cis_rule_3_2_9 tags: - - level1-server - - level1-workstation + - level2-server + - level2-workstation - sysctl - patch - rule_3.2.9 diff --git a/tasks/section_4/cis_4.1.x.yml b/tasks/section_4/cis_4.1.x.yml index dea1d5f9..136ace10 100644 --- a/tasks/section_4/cis_4.1.x.yml +++ b/tasks/section_4/cis_4.1.x.yml @@ -250,7 +250,7 @@ - name: "4.1.17 | L2 | PATCH | Ensure the audit configuration is immutable" template: src: audit/rhel8cis_rule_4_1_17.rules.j2 - dest: /etc/audit/rules.d/rhel8cis_rule_4_1_17.rules + dest: /etc/audit/rules.d/99-finalize.rules owner: root group: root mode: 0600 diff --git a/tasks/section_4/cis_4.2.1.x.yml b/tasks/section_4/cis_4.2.1.x.yml index 836d47d0..a7a961c6 100644 --- a/tasks/section_4/cis_4.2.1.x.yml +++ b/tasks/section_4/cis_4.2.1.x.yml @@ -47,6 +47,7 @@ become: yes changed_when: false failed_when: no + check_mode: no register: rhel_08_4_2_1_4_audit - name: "4.2.1.4 | L1 | AUDIT | Ensure logging is configured | rsyslog current config message out" diff --git a/tasks/section_5/cis_5.2.x.yml b/tasks/section_5/cis_5.2.x.yml index 96b8d1fc..f4349943 100644 --- a/tasks/section_5/cis_5.2.x.yml +++ b/tasks/section_5/cis_5.2.x.yml @@ -196,7 +196,7 @@ - patch - rule_5.2.10 -- name: "5.2.11 | PATCH | Ensure SSH PermitEmptyPasswords is disabled" +- name: "5.2.11 | L1 | PATCH | Ensure SSH PermitEmptyPasswords is disabled" lineinfile: state: present dest: /etc/ssh/sshd_config diff --git a/tasks/section_5/cis_5.5.x.yml b/tasks/section_5/cis_5.5.x.yml index b9e78c45..75f744f1 100644 --- a/tasks/section_5/cis_5.5.x.yml +++ b/tasks/section_5/cis_5.5.x.yml @@ -49,16 +49,16 @@ block: | # Set session timeout - CIS ID RHEL-08-5.4.5 TMOUT={{ rhel8cis_shell_session_timeout.timeout }} - readonly TMOUT export TMOUT + readonly TMOUT with_items: - { dest: "{{ rhel8cis_shell_session_timeout.file }}", state: present } - { dest: /etc/profile, state: "{{ (rhel8cis_shell_session_timeout.file == '/etc/profile') | ternary('present', 'absent') }}" } when: - rhel8cis_rule_5_5_3 tags: - - level2-server - - level2-workstation + - level1-server + - level1-workstation - patch - rule_5.5.3 @@ -69,7 +69,8 @@ when: - rhel8cis_rule_5_5_4 tags: - - level1 + - level1-server + - level1-workstation - patch - rule_5.5.4 diff --git a/tasks/section_6/cis_6.2.x.yml b/tasks/section_6/cis_6.2.x.yml index 4895f385..42f22dcd 100644 --- a/tasks/section_6/cis_6.2.x.yml +++ b/tasks/section_6/cis_6.2.x.yml @@ -181,8 +181,6 @@ path: "{{ item.dir }}" owner: "{{ item.id }}" state: directory - args: - warn: no with_items: "{{ rhel8cis_passwd }}" loop_control: label: "{{ rhel8cis_passwd_label }}" @@ -468,7 +466,7 @@ - name: "6.2.20 | L1 | AUDIT | Ensure all users' home directories exist" command: find -H {{ item.0 | quote }} -not -type l -perm /027 check_mode: false - changed_when: rhel_08_6_2_20_patch_audit.stdout "| length > 0" + changed_when: rhel_08_6_2_20_patch_audit.stdout | length > 0 register: rhel_08_6_2_20_patch_audit when: - ansible_check_mode diff --git a/templates/ansible_vars_goss.yml.j2 b/templates/ansible_vars_goss.yml.j2 index c3c3efb9..652b968b 100644 --- a/templates/ansible_vars_goss.yml.j2 +++ b/templates/ansible_vars_goss.yml.j2 @@ -1,10 +1,8 @@ ## metadata for Audit benchmark -rhel8cis_benchmark: -- "type: CIS" -- "version: '1.0.1'" -- "os: RHEL 8" -- "epoch: {{ ansible_date_time.epoch }}" -- "hostname: {{ ansible_hostname }}" +benchmark_version: '1.0.1' + +# Set if genuine RHEL (subscription manager check) not for derivatives e.g. CentOS +is_redhat_os: {% if ansible_distribution == "RedHat" %}true{% else %}false{% endif %} rhel8cis_os_distribution: {{ ansible_distribution | lower }}