754 lines
26 KiB
YAML
754 lines
26 KiB
YAML
|
|
---
|
|||
|
|
- name: Verify
|
|||
|
|
hosts: all
|
|||
|
|
any_errors_fatal: true
|
|||
|
|
|
|||
|
|
tasks:
|
|||
|
|
- name: Reboot host
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.reboot:
|
|||
|
|
|
|||
|
|
- name: Wait for the host and reconnect
|
|||
|
|
ansible.builtin.wait_for:
|
|||
|
|
port: 22
|
|||
|
|
host: '{{ (ansible_ssh_host | default(ansible_host)) | default(inventory_hostname) }}'
|
|||
|
|
delay: 10
|
|||
|
|
timeout: 120
|
|||
|
|
|
|||
|
|
- name: Include default vars
|
|||
|
|
ansible.builtin.include_vars:
|
|||
|
|
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
|||
|
|
extensions:
|
|||
|
|
- 'yml'
|
|||
|
|
|
|||
|
|
- name: Include host vars
|
|||
|
|
ansible.builtin.include_vars:
|
|||
|
|
file: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/inventory/host_vars/{{ ansible_hostname }}"
|
|||
|
|
|
|||
|
|
- name: Set sysctl configuration directory as fact
|
|||
|
|
tags:
|
|||
|
|
- fact
|
|||
|
|
- sysctl
|
|||
|
|
block:
|
|||
|
|
- name: Stat /usr/lib/sysctl.d/ exists
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: "/usr/lib/sysctl.d/"
|
|||
|
|
register: usr_lib_sysctl_d
|
|||
|
|
|
|||
|
|
- name: Set sysctl fact
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
sysctl_conf_dir: "{{ '/usr/lib/sysctl.d' if usr_lib_sysctl_d.stat.exists else '/etc/sysctl.d' }}"
|
|||
|
|
|
|||
|
|
- name: Debug sysctl
|
|||
|
|
ansible.builtin.debug:
|
|||
|
|
msg: "{{ sysctl_conf_dir }}"
|
|||
|
|
|
|||
|
|
- name: Get installed ssh version
|
|||
|
|
ansible.builtin.command:
|
|||
|
|
cmd: ssh -V
|
|||
|
|
changed_when: false
|
|||
|
|
failed_when: false
|
|||
|
|
register: ssh_version
|
|||
|
|
tags:
|
|||
|
|
- sshd
|
|||
|
|
- sshd_config
|
|||
|
|
|
|||
|
|
- name: Set ssh version as fact
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
ssh_installed_version: "{{ ssh_version.stderr | regex_search('^OpenSSH_([0-9+].[0-9]+)', '\\1') | join('.') | float }}" # noqa jinja[spacing]
|
|||
|
|
tags:
|
|||
|
|
- sshd
|
|||
|
|
- sshd_config
|
|||
|
|
|
|||
|
|
- name: Stat IPv6 status
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: /proc/sys/net/ipv6
|
|||
|
|
register: stat_ipv6
|
|||
|
|
|
|||
|
|
- name: Set IPv6 fact
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
system_has_ipv6: "{{ stat_ipv6.stat.exists }}"
|
|||
|
|
|
|||
|
|
- name: Update current facts
|
|||
|
|
ansible.builtin.setup: ~
|
|||
|
|
|
|||
|
|
- name: Verify auditd configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/audit/auditd.conf
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: auditd_conf
|
|||
|
|
failed_when: auditd_conf is changed
|
|||
|
|
with_items:
|
|||
|
|
- action_mail_acct = {{ auditd_action_mail_acct }}
|
|||
|
|
- admin_space_left_action = {{ auditd_admin_space_left_action }}
|
|||
|
|
- disk_error_action = {{ auditd_disk_error_action }}
|
|||
|
|
- disk_full_action = {{ auditd_disk_full_action }}
|
|||
|
|
- max_log_file = {{ auditd_max_log_file }}
|
|||
|
|
- max_log_file_action = {{ auditd_max_log_file_action }}
|
|||
|
|
- name_format = hostname
|
|||
|
|
- num_logs = {{ auditd_num_logs }}
|
|||
|
|
- space_left = {{ auditd_space_left }}
|
|||
|
|
- space_left_action = {{ auditd_space_left_action }}
|
|||
|
|
|
|||
|
|
- name: Verify systemd system.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/systemd/system.conf
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: systemd_system_conf
|
|||
|
|
failed_when: systemd_system_conf is changed
|
|||
|
|
with_items:
|
|||
|
|
- DumpCore=no
|
|||
|
|
- CrashShell=no
|
|||
|
|
- CtrlAltDelBurstAction=none
|
|||
|
|
- DefaultLimitCORE=0
|
|||
|
|
- DefaultLimitNOFILE={{ limit_nofile_hard }}
|
|||
|
|
- DefaultLimitNPROC={{ limit_nproc_hard }}
|
|||
|
|
|
|||
|
|
- name: Verify systemd user.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/systemd/user.conf
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: systemd_user_conf
|
|||
|
|
failed_when: systemd_user_conf is changed
|
|||
|
|
with_items:
|
|||
|
|
- DefaultLimitCORE=0
|
|||
|
|
- DefaultLimitNOFILE={{ limit_nofile_hard }}
|
|||
|
|
- DefaultLimitNPROC={{ limit_nproc_hard }}
|
|||
|
|
|
|||
|
|
- name: Verify postfix configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/postfix/main.cf
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: postfix_conf
|
|||
|
|
failed_when: postfix_conf is changed
|
|||
|
|
with_items:
|
|||
|
|
- disable_vrfy_command = yes
|
|||
|
|
- inet_interfaces = loopback-only
|
|||
|
|
- smtpd_banner = \$myhostname - ESMTP
|
|||
|
|
- smtpd_client_restrictions = permit_mynetworks,reject
|
|||
|
|
|
|||
|
|
- name: Stat /etc/ssh/sshd_config
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: '/etc/ssh/sshd_config'
|
|||
|
|
register: sshd_config_file
|
|||
|
|
|
|||
|
|
- name: Stat /etc/ssh/ssh_config
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: '/etc/ssh/ssh_config'
|
|||
|
|
register: ssh_config_file
|
|||
|
|
|
|||
|
|
- name: Stat /etc/ssh/sshd_config.d/01-hardening.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: '/etc/ssh/sshd_config.d/01-hardening.conf'
|
|||
|
|
register: sshd_config_directory
|
|||
|
|
|
|||
|
|
- name: Assert /etc/ssh/ssh_config permissions
|
|||
|
|
ansible.builtin.assert:
|
|||
|
|
that:
|
|||
|
|
- ssh_config_file.stat.mode == "0644"
|
|||
|
|
success_msg: "{{ ssh_config_file.stat.path }} has correct permissions: {{ ssh_config_file.stat.mode }}"
|
|||
|
|
fail_msg: "{{ ssh_config_file.stat.path }} permissions are incorrect: {{ ssh_config_file.stat.mode }}"
|
|||
|
|
|
|||
|
|
- name: Assert /etc/ssh/sshd_config permissions
|
|||
|
|
ansible.builtin.assert:
|
|||
|
|
that:
|
|||
|
|
- sshd_config_file.stat.mode == "0600"
|
|||
|
|
success_msg: "{{ sshd_config_file.stat.path }} has correct permissions: {{ sshd_config_file.stat.mode }}"
|
|||
|
|
fail_msg: "{{ sshd_config_file.stat.path }} permissions are incorrect: {{ sshd_config_file.stat.mode }}"
|
|||
|
|
|
|||
|
|
- name: Assert /etc/ssh/sshd_config.d/01-hardening.conf permissions
|
|||
|
|
ansible.builtin.assert:
|
|||
|
|
that:
|
|||
|
|
- sshd_config_directory.stat.mode == "0600"
|
|||
|
|
success_msg: "{{ sshd_config_directory.stat.path }} has correct permissions: {{ sshd_config_directory.stat.mode }}"
|
|||
|
|
fail_msg: "{{ sshd_config_directory.stat.path }} permissions are incorrect: {{ sshd_config_directory.stat.mode }}"
|
|||
|
|
when: sshd_config_directory.stat.exists
|
|||
|
|
|
|||
|
|
- name: Verify sshd_config PermitRootLogin configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/sshd_config
|
|||
|
|
line: "PermitRootLogin yes"
|
|||
|
|
state: absent
|
|||
|
|
check_mode: true
|
|||
|
|
register: sshd_config
|
|||
|
|
failed_when: sshd_config is changed
|
|||
|
|
|
|||
|
|
- name: Verify sshd_config.d PermitRootLogin configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/sshd_config.d/01-hardening.conf
|
|||
|
|
line: "PermitRootLogin yes"
|
|||
|
|
state: absent
|
|||
|
|
check_mode: true
|
|||
|
|
register: sshd_config_d
|
|||
|
|
failed_when: sshd_config_d is changed
|
|||
|
|
when: sshd_config_directory.stat.exists
|
|||
|
|
|
|||
|
|
- name: Verify sshd_config configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/sshd_config
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: sshd_config
|
|||
|
|
failed_when: sshd_config is changed
|
|||
|
|
with_items:
|
|||
|
|
- AllowGroups vagrant sudo
|
|||
|
|
- AllowAgentForwarding {{ sshd_allow_agent_forwarding }}
|
|||
|
|
- AllowTcpForwarding {{ sshd_allow_tcp_forwarding }}
|
|||
|
|
- AuthenticationMethods {{ sshd_authentication_methods }}
|
|||
|
|
- Banner {{ sshd_banner }}
|
|||
|
|
- ChallengeResponseAuthentication {{ sshd_challenge_response_authentication }}
|
|||
|
|
- Ciphers {{ sshd_ciphers }}
|
|||
|
|
- ClientAliveCountMax {{ sshd_client_alive_count_max | int }}
|
|||
|
|
- ClientAliveInterval {{ sshd_client_alive_interval | int }}
|
|||
|
|
- Compression {{ sshd_compression }}
|
|||
|
|
- GSSAPIAuthentication {{ sshd_gssapi_authentication }}
|
|||
|
|
- HostbasedAuthentication {{ sshd_hostbased_authentication }}
|
|||
|
|
- IgnoreUserKnownHosts {{ sshd_ignore_user_known_hosts }}
|
|||
|
|
- KerberosAuthentication {{ sshd_kerberos_authentication }}
|
|||
|
|
- HostKeyAlgorithms {{ sshd_host_key_algorithms }}
|
|||
|
|
- KexAlgorithms {{ sshd_kex_algorithms }}
|
|||
|
|
- LogLevel {{ sshd_log_level }}
|
|||
|
|
- LoginGraceTime {{ sshd_login_grace_time | int }}
|
|||
|
|
- Macs {{ sshd_macs }}
|
|||
|
|
- MaxAuthTries {{ sshd_max_auth_tries | int }}
|
|||
|
|
- MaxSessions {{ sshd_max_sessions | int }}
|
|||
|
|
- MaxStartups {{ sshd_max_startups }}
|
|||
|
|
- PasswordAuthentication {{ sshd_password_authentication }}
|
|||
|
|
- PermitEmptyPasswords {{ sshd_permit_empty_passwords }}
|
|||
|
|
- PermitRootLogin {{ sshd_permit_root_login }}
|
|||
|
|
- PermitUserEnvironment {{ sshd_permit_user_environment }}
|
|||
|
|
- Port {{ sshd_port | int }}
|
|||
|
|
- PrintLastLog {{ sshd_print_last_log }}
|
|||
|
|
- PrintMotd {{ sshd_print_motd }}
|
|||
|
|
- RekeyLimit {{ sshd_rekey_limit }}
|
|||
|
|
- StrictModes {{ sshd_strict_modes }}
|
|||
|
|
- TCPKeepAlive {{ sshd_tcp_keep_alive }}
|
|||
|
|
- UseDNS {{ sshd_use_dns }}
|
|||
|
|
- UsePAM {{ sshd_use_pam }}
|
|||
|
|
- X11Forwarding {{ sshd_x11_forwarding }}
|
|||
|
|
when: not sshd_config_directory.stat.exists
|
|||
|
|
|
|||
|
|
- name: Verify sshd_config.d configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/sshd_config.d/01-hardening.conf
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: sshd_config
|
|||
|
|
failed_when: sshd_config is changed
|
|||
|
|
with_items:
|
|||
|
|
- AllowGroups vagrant sudo
|
|||
|
|
- AllowAgentForwarding {{ sshd_allow_agent_forwarding }}
|
|||
|
|
- AllowTcpForwarding {{ sshd_allow_tcp_forwarding }}
|
|||
|
|
- AuthenticationMethods {{ sshd_authentication_methods }}
|
|||
|
|
- Banner {{ sshd_banner }}
|
|||
|
|
- ChallengeResponseAuthentication {{ sshd_challenge_response_authentication }}
|
|||
|
|
- Ciphers {{ sshd_ciphers }}
|
|||
|
|
- ClientAliveCountMax {{ sshd_client_alive_count_max | int }}
|
|||
|
|
- ClientAliveInterval {{ sshd_client_alive_interval | int }}
|
|||
|
|
- Compression {{ sshd_compression }}
|
|||
|
|
- GSSAPIAuthentication {{ sshd_gssapi_authentication }}
|
|||
|
|
- HostbasedAuthentication {{ sshd_hostbased_authentication }}
|
|||
|
|
- IgnoreUserKnownHosts {{ sshd_ignore_user_known_hosts }}
|
|||
|
|
- KerberosAuthentication {{ sshd_kerberos_authentication }}
|
|||
|
|
- HostKeyAlgorithms {{ sshd_host_key_algorithms }}
|
|||
|
|
- KexAlgorithms {{ sshd_kex_algorithms }}
|
|||
|
|
- LogLevel {{ sshd_log_level }}
|
|||
|
|
- LoginGraceTime {{ sshd_login_grace_time | int }}
|
|||
|
|
- Macs {{ sshd_macs }}
|
|||
|
|
- MaxAuthTries {{ sshd_max_auth_tries | int }}
|
|||
|
|
- MaxSessions {{ sshd_max_sessions | int }}
|
|||
|
|
- MaxStartups {{ sshd_max_startups }}
|
|||
|
|
- PasswordAuthentication {{ sshd_password_authentication }}
|
|||
|
|
- PermitEmptyPasswords {{ sshd_permit_empty_passwords }}
|
|||
|
|
- PermitRootLogin {{ sshd_permit_root_login }}
|
|||
|
|
- PermitUserEnvironment {{ sshd_permit_user_environment }}
|
|||
|
|
- Port {{ sshd_port | int }}
|
|||
|
|
- PrintLastLog {{ sshd_print_last_log }}
|
|||
|
|
- PrintMotd {{ sshd_print_motd }}
|
|||
|
|
- RekeyLimit {{ sshd_rekey_limit }}
|
|||
|
|
- StrictModes {{ sshd_strict_modes }}
|
|||
|
|
- Subsystem {{ sshd_subsystem }}
|
|||
|
|
- TCPKeepAlive {{ sshd_tcp_keep_alive }}
|
|||
|
|
- UseDNS {{ sshd_use_dns }}
|
|||
|
|
- UsePAM {{ sshd_use_pam }}
|
|||
|
|
- X11Forwarding {{ sshd_x11_forwarding }}
|
|||
|
|
when: sshd_config_directory.stat.exists
|
|||
|
|
|
|||
|
|
- name: Verify sshd RequiredRSASize
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/sshd_config.d/01-hardening.conf
|
|||
|
|
line: "RequiredRSASize {{ sshd_required_rsa_size }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: sshd_config_rsasize
|
|||
|
|
failed_when: sshd_config_rsasize is changed
|
|||
|
|
when: sshd_config_directory.stat.exists and ssh_installed_version is version('9.1', '>')
|
|||
|
|
|
|||
|
|
- name: Verify sshd runtime configuration
|
|||
|
|
become: true
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.command: sshd -T
|
|||
|
|
register: sshd_config
|
|||
|
|
changed_when: false
|
|||
|
|
failed_when: item not in sshd_config.stdout_lines
|
|||
|
|
with_items:
|
|||
|
|
- allowgroups sudo
|
|||
|
|
- allowgroups vagrant
|
|||
|
|
- allowagentforwarding {{ sshd_allow_agent_forwarding }}
|
|||
|
|
- allowtcpforwarding {{ sshd_allow_tcp_forwarding }}
|
|||
|
|
- authenticationmethods {{ sshd_authentication_methods }}
|
|||
|
|
- banner {{ sshd_banner }}
|
|||
|
|
- clientalivecountmax {{ sshd_client_alive_count_max | int }}
|
|||
|
|
- clientaliveinterval {{ sshd_client_alive_interval | int }}
|
|||
|
|
- compression {{ sshd_compression }}
|
|||
|
|
- logingracetime {{ sshd_login_grace_time | int }}
|
|||
|
|
- macs {{ sshd_macs }}
|
|||
|
|
- maxauthtries {{ sshd_max_auth_tries | int }}
|
|||
|
|
- maxsessions {{ sshd_max_sessions | int }}
|
|||
|
|
- maxstartups {{ sshd_max_startups }}
|
|||
|
|
- permitrootlogin {{ sshd_permit_root_login }}
|
|||
|
|
- port {{ sshd_port | int }}
|
|||
|
|
- subsystem {{ sshd_subsystem }}
|
|||
|
|
- usedns {{ sshd_use_dns }}
|
|||
|
|
- usepam {{ sshd_use_pam }}
|
|||
|
|
- x11forwarding {{ sshd_x11_forwarding }}
|
|||
|
|
|
|||
|
|
- name: Verify ssh_config configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/ssh/ssh_config
|
|||
|
|
line: " {{ item }}"
|
|||
|
|
state: present
|
|||
|
|
check_mode: true
|
|||
|
|
register: ssh_config
|
|||
|
|
failed_when: ssh_config is changed
|
|||
|
|
with_items:
|
|||
|
|
- Ciphers {{ sshd_ciphers }}
|
|||
|
|
- GSSAPIAuthentication {{ sshd_gssapi_authentication }}
|
|||
|
|
- HashKnownHosts yes
|
|||
|
|
- HostKeyAlgorithms {{ sshd_host_key_algorithms }}
|
|||
|
|
- KexAlgorithms {{ sshd_kex_algorithms }}
|
|||
|
|
- MACs {{ sshd_macs }}
|
|||
|
|
- RekeyLimit {{ sshd_rekey_limit }}
|
|||
|
|
|
|||
|
|
- name: Merge sysctl settings
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
sysctl_settings: "{{ generic_sysctl_settings | combine(ipv4_sysctl_settings) }}"
|
|||
|
|
|
|||
|
|
- name: Verify sysctl configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grep -R "^{{ item.key }}.*{{ item.value | int }}$" "{{ sysctl_conf_dir }}/"
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
with_dict: "{{ sysctl_settings | dict2items | rejectattr('key', 'search', 'nf_conntrack') | items2dict }}"
|
|||
|
|
register: sysctl_file_config
|
|||
|
|
failed_when: sysctl_file_config.rc != 0
|
|||
|
|
changed_when: sysctl_file_config.rc != 0
|
|||
|
|
|
|||
|
|
- name: Verify sysctl runtime configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
sysctl -a | grep "^{{ item.key }}.*{{ item.value | int }}$"
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
with_dict: "{{ sysctl_settings | dict2items | rejectattr('key', 'search', 'nf_conntrack') | items2dict }}"
|
|||
|
|
register: sysctl_runtime_config
|
|||
|
|
failed_when: sysctl_runtime_config.rc != 0
|
|||
|
|
changed_when: sysctl_runtime_config.rc != 0
|
|||
|
|
|
|||
|
|
- name: Stat crypto-policies config
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: /etc/crypto-policies/config
|
|||
|
|
register: crypto_policies_config
|
|||
|
|
|
|||
|
|
- name: Get crypto-policies value
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.shell: grep "^{{ crypto_policy | upper }}$" /etc/crypto-policies/config
|
|||
|
|
register: crypto_policy_set
|
|||
|
|
when: crypto_policies_config.stat.exists
|
|||
|
|
failed_when: crypto_policy_set.rc != 0
|
|||
|
|
changed_when: crypto_policy_set.rc != 0
|
|||
|
|
|
|||
|
|
- name: Verify noexec /dev/shm
|
|||
|
|
ansible.builtin.shell: grep '^tmpfs\s/dev/shm.*noexec' /proc/mounts
|
|||
|
|
register: dev_shm_noexec
|
|||
|
|
failed_when: dev_shm_noexec.rc != 0
|
|||
|
|
changed_when: dev_shm_noexec.rc != 0
|
|||
|
|
|
|||
|
|
- name: Verify /proc
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grep -E '^proc\s/proc proc rw,nosuid,nodev,noexec,relatime.*hidepid=({{ hide_pid }}|invisible)' /proc/mounts
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
register: proc_opts
|
|||
|
|
failed_when: proc_opts.rc != 0
|
|||
|
|
changed_when: proc_opts.rc != 0
|
|||
|
|
tags:
|
|||
|
|
- skip_ansible_lint
|
|||
|
|
|
|||
|
|
- name: Verify tmp.mount
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.systemd:
|
|||
|
|
name: tmp.mount
|
|||
|
|
register: tmp_mount
|
|||
|
|
failed_when: not 'nosuid,nodev,noexec' in tmp_mount.status.Options
|
|||
|
|
changed_when: not 'nosuid,nodev,noexec' in tmp_mount.status.Options
|
|||
|
|
|
|||
|
|
- name: Verify /tmp
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep '^tmpfs\s/tmp\s.*nosuid,nodev,noexec' /proc/mounts
|
|||
|
|
register: mounts_tmp
|
|||
|
|
failed_when: mounts_tmp.rc != 0
|
|||
|
|
changed_when: mounts_tmp.rc != 0
|
|||
|
|
|
|||
|
|
- name: Verify login.defs settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep "^{{ item }}$" /etc/login.defs
|
|||
|
|
register: login_defs
|
|||
|
|
failed_when: login_defs.rc != 0
|
|||
|
|
changed_when: login_defs.rc != 0
|
|||
|
|
with_items:
|
|||
|
|
- ENCRYPT_METHOD SHA512
|
|||
|
|
- FAILLOG_ENAB yes
|
|||
|
|
- LOG_OK_LOGINS yes
|
|||
|
|
- PASS_MAX_DAYS 60
|
|||
|
|
- PASS_MIN_DAYS 1
|
|||
|
|
- PASS_WARN_AGE 7
|
|||
|
|
- SHA_CRYPT_MAX_ROUNDS 65536
|
|||
|
|
- SHA_CRYPT_MIN_ROUNDS 10000
|
|||
|
|
- SU_NAME su
|
|||
|
|
- SYSLOG_SG_ENAB yes
|
|||
|
|
- SYSLOG_SU_ENAB yes
|
|||
|
|
- UMASK {{ umask_value }}
|
|||
|
|
|
|||
|
|
- name: Verify pwquality.conf settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
path: /etc/security/pwquality.conf
|
|||
|
|
line: "{{ item.key }} = {{ item.value }}"
|
|||
|
|
state: present
|
|||
|
|
mode: "0644"
|
|||
|
|
owner: root
|
|||
|
|
group: root
|
|||
|
|
check_mode: true
|
|||
|
|
register: pwquality_conf
|
|||
|
|
failed_when: pwquality_conf is changed
|
|||
|
|
with_dict: "{{ pwquality_config }}"
|
|||
|
|
|
|||
|
|
- name: Verify RedHat GRUB audit settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grubby --info="/boot/vmlinuz-$(uname -r)" | grep "^args.*{{ grub_audit_cmdline }} {{ grub_audit_backlog_cmdline }}"
|
|||
|
|
register: audit_grubenv
|
|||
|
|
failed_when: audit_grubenv.rc != 0
|
|||
|
|
changed_when: audit_grubenv.rc != 0
|
|||
|
|
when: ansible_os_family == "RedHat"
|
|||
|
|
|
|||
|
|
- name: Verify Debian audit GRUB settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep "linux.*{{ grub_audit_cmdline }} {{ grub_audit_backlog_cmdline }}" /boot/grub/grub.cfg
|
|||
|
|
register: audit_grub_cfg
|
|||
|
|
failed_when: audit_grub_cfg.rc != 0
|
|||
|
|
changed_when: audit_grub_cfg.rc != 0
|
|||
|
|
when: ansible_os_family == "Debian"
|
|||
|
|
|
|||
|
|
- name: Verify RedHat GRUB IPv6 settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell:
|
|||
|
|
cmd: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grubby --info="/boot/vmlinuz-$(uname -r)" | grep "ipv6.disable=1"
|
|||
|
|
register: audit_grubenv
|
|||
|
|
failed_when: audit_grubenv.rc != 0
|
|||
|
|
changed_when: audit_grubenv.rc != 0
|
|||
|
|
when: ansible_os_family == "RedHat" and disable_ipv6
|
|||
|
|
|
|||
|
|
- name: Verify Debian GRUB IPv6 settings
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell:
|
|||
|
|
cmd: grep "linux.*ipv6.disable=1" /boot/grub/grub.cfg
|
|||
|
|
register: audit_grub_cfg
|
|||
|
|
failed_when: audit_grub_cfg.rc != 0
|
|||
|
|
changed_when: audit_grub_cfg.rc != 0
|
|||
|
|
when: ansible_os_family == "Debian" and disable_ipv6
|
|||
|
|
|
|||
|
|
- name: Verify IPv6 sysctl configuration
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep -R "^{{ item }}$" /etc/sysctl.* /usr/lib/sysctl.d/*
|
|||
|
|
register: sysctl_ipv6_config
|
|||
|
|
failed_when: sysctl_ipv6_config.rc != 0
|
|||
|
|
changed_when: sysctl_ipv6_config.rc != 0
|
|||
|
|
with_items:
|
|||
|
|
- net.ipv6.conf.all.disable_ipv6=1
|
|||
|
|
- net.ipv6.conf.default.disable_ipv6=1
|
|||
|
|
when: disable_ipv6
|
|||
|
|
|
|||
|
|
- name: Verify IPv6 sysctl runtime configuration
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
sysctl -a | grep "^{{ item.key }}.*{{ item.value | int }}$"
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
with_dict: "{{ ipv6_disable_sysctl_settings }}"
|
|||
|
|
register: sysctl_ipv6
|
|||
|
|
changed_when: sysctl_ipv6.rc != 0
|
|||
|
|
failed_when: sysctl_ipv6.rc != 0
|
|||
|
|
when: system_has_ipv6 and disable_ipv6
|
|||
|
|
|
|||
|
|
- name: Find GRUB config files
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.find:
|
|||
|
|
paths: /boot
|
|||
|
|
patterns: "*.cfg,grubenv"
|
|||
|
|
recurse: true
|
|||
|
|
register: perm_grub_cfg
|
|||
|
|
|
|||
|
|
- name: Verify GRUB config files permission
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: "{{ item.path }}"
|
|||
|
|
changed_when: item.mode | int > 644
|
|||
|
|
failed_when: item.mode | int > 644
|
|||
|
|
with_items:
|
|||
|
|
- "{{ perm_grub_cfg.files | reject('search', '/boot/efi/EFI/ubuntu/grub.cfg') | list }}"
|
|||
|
|
loop_control:
|
|||
|
|
label: "{{ item.path }}"
|
|||
|
|
|
|||
|
|
- name: Verify systemd resolved.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep "^{{ item }}$" /etc/systemd/resolved.conf
|
|||
|
|
register: systemd_resolved_conf
|
|||
|
|
failed_when: systemd_resolved_conf.rc != 0
|
|||
|
|
changed_when: systemd_resolved_conf.rc != 0
|
|||
|
|
with_items:
|
|||
|
|
- DNS={{ dns }}
|
|||
|
|
- FallbackDNS={{ fallback_dns }}
|
|||
|
|
- DNSSEC={{ dnssec }}
|
|||
|
|
- DNSOverTLS={{ dns_over_tls }}
|
|||
|
|
|
|||
|
|
- name: Verify systemd timesyncd.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: grep "^{{ item }}$" /etc/systemd/timesyncd.conf
|
|||
|
|
register: systemd_timesyncd_conf
|
|||
|
|
failed_when: systemd_timesyncd_conf.rc != 0
|
|||
|
|
changed_when: systemd_timesyncd_conf.rc != 0
|
|||
|
|
with_items:
|
|||
|
|
- NTP={{ ntp }}
|
|||
|
|
- FallbackNTP={{ fallback_ntp }}
|
|||
|
|
|
|||
|
|
- name: Stat /etc/default/motd-news
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: /etc/default/motd-news
|
|||
|
|
register: motd_news
|
|||
|
|
|
|||
|
|
- name: Verify masked motdnews service
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.systemd:
|
|||
|
|
name: motd-news.service
|
|||
|
|
masked: true
|
|||
|
|
enabled: false
|
|||
|
|
state: stopped
|
|||
|
|
check_mode: true
|
|||
|
|
register: motdnews_service
|
|||
|
|
failed_when: motdnews_service is changed
|
|||
|
|
when: ansible_os_family == "Debian" and motd_news.stat.exists
|
|||
|
|
|
|||
|
|
- name: Stat /usr/bin/pro
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: /usr/bin/pro
|
|||
|
|
register: ubuntu_advantage_pro
|
|||
|
|
when: ansible_os_family == "Debian"
|
|||
|
|
|
|||
|
|
- name: Verify that apt_news is disabled
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
pro config show | grep '^apt_news.*False'
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
register: ubuntu_advantage_pro_state
|
|||
|
|
changed_when: ubuntu_advantage_pro_state.rc != 0
|
|||
|
|
failed_when: ubuntu_advantage_pro_state.rc != 0
|
|||
|
|
when: ansible_os_family == "Debian" and ubuntu_advantage_pro.stat.exists
|
|||
|
|
|
|||
|
|
- name: Efi fact
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
booted_with_efi: "{{ ansible_mounts | selectattr('mount', 'equalto', '/boot/efi') | list | length > 0 }}"
|
|||
|
|
|
|||
|
|
- name: Verify cracklib password list
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grep '/usr/share/dict/passwords' /var/cache/cracklib/src-dicts
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
register: cracklib_passwords
|
|||
|
|
failed_when: cracklib_passwords.rc != 0
|
|||
|
|
changed_when: cracklib_passwords.rc != 0
|
|||
|
|
when: ansible_os_family == "Debian"
|
|||
|
|
|
|||
|
|
- name: Index blacklisted kernel modules
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
modprobe -c | grep -o '^blacklist .*' | awk '{print $2}'
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
changed_when: false
|
|||
|
|
failed_when: modprobe_blacklist.rc != 0
|
|||
|
|
register: modprobe_blacklist
|
|||
|
|
when: block_blacklisted | bool
|
|||
|
|
|
|||
|
|
- name: Verify blocked blacklisted kernel modules
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
grep 'install {{ item }} /bin/true' /etc/modprobe.d/blockblacklisted.conf
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
register: modprobe_blockblacklisted
|
|||
|
|
failed_when: modprobe_blockblacklisted.rc != 0
|
|||
|
|
changed_when: modprobe_blockblacklisted.rc != 0
|
|||
|
|
with_items:
|
|||
|
|
- "{{ modprobe_blacklist.stdout_lines | sort }}"
|
|||
|
|
when: block_blacklisted | bool
|
|||
|
|
|
|||
|
|
- name: Verify sudo settings
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
sudo -l | grep "{{ item }}"
|
|||
|
|
register: sudo_settings
|
|||
|
|
failed_when: sudo_settings.rc != 0
|
|||
|
|
changed_when: sudo_settings.rc != 0
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
with_items:
|
|||
|
|
- 'use_pty'
|
|||
|
|
- 'logfile="/var/log/sudo.log"'
|
|||
|
|
- '!pwfeedback'
|
|||
|
|
- '!visiblepw'
|
|||
|
|
- 'passwd_timeout=1'
|
|||
|
|
- 'timestamp_timeout=5'
|
|||
|
|
- 'timestamp_type=tty'
|
|||
|
|
- "!rootpw"
|
|||
|
|
- "!runaspw"
|
|||
|
|
- "!targetpw"
|
|||
|
|
|
|||
|
|
- name: Stat faillock.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.stat:
|
|||
|
|
path: /etc/security/faillock.conf
|
|||
|
|
register: faillockconf
|
|||
|
|
|
|||
|
|
- name: Verify faillock.conf
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.lineinfile:
|
|||
|
|
dest: /etc/security/faillock.conf
|
|||
|
|
mode: "0644"
|
|||
|
|
state: present
|
|||
|
|
line: "{{ item }}"
|
|||
|
|
check_mode: true
|
|||
|
|
register: verify_faillock
|
|||
|
|
failed_when: verify_faillock is changed
|
|||
|
|
with_items:
|
|||
|
|
- "audit"
|
|||
|
|
- "local_users_only"
|
|||
|
|
- "deny = 5"
|
|||
|
|
- "fail_interval = 900"
|
|||
|
|
when: faillockconf.stat.exists
|
|||
|
|
|
|||
|
|
- name: Verify wireless state
|
|||
|
|
environment:
|
|||
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|||
|
|
ansible.builtin.command: nmcli -t radio
|
|||
|
|
register: wireless_state
|
|||
|
|
changed_when: not 'disabled' in wireless_state.stdout
|
|||
|
|
failed_when: not 'disabled' in wireless_state.stdout
|
|||
|
|
when: disable_wireless
|
|||
|
|
|
|||
|
|
- name: Stat firewall rules when UFW is enabled
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
ufw show added | grep '^ufw' | grep -v "'ansible\smanaged'" | sed 's/ufw //g'
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
failed_when: ufw_not_managed.rc > 1
|
|||
|
|
changed_when: false
|
|||
|
|
register: ufw_not_managed
|
|||
|
|
when: ufw_enable
|
|||
|
|
|
|||
|
|
- name: Stat firewall rules when UFW is disabled
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.shell: |
|
|||
|
|
set -o pipefail
|
|||
|
|
ufw show added | grep '^ufw' | grep "'ansible\smanaged'" | sed 's/ufw //g'
|
|||
|
|
args:
|
|||
|
|
executable: /bin/bash
|
|||
|
|
failed_when: ufw_not_managed.rc > 1
|
|||
|
|
changed_when: false
|
|||
|
|
register: ufw_not_managed
|
|||
|
|
when: not ufw_enable
|
|||
|
|
|
|||
|
|
- name: Create test user
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.user:
|
|||
|
|
name: roletestuser
|
|||
|
|
password: "{{ 'Ansible Role Test User' | password_hash('sha512') }}"
|
|||
|
|
state: present
|
|||
|
|
shell: /bin/bash
|
|||
|
|
|
|||
|
|
- name: Create test user salt
|
|||
|
|
ansible.builtin.set_fact:
|
|||
|
|
test_user_salt: "{{ lookup('password', '/dev/null chars=ascii_lowercase,ascii_uppercase,digits length=16') }}"
|
|||
|
|
|
|||
|
|
- name: Change test user password
|
|||
|
|
become: true
|
|||
|
|
ansible.builtin.user:
|
|||
|
|
name: roletestuser
|
|||
|
|
password: "{{ 'roletestuser' | password_hash('sha512', test_user_salt, rounds=656000) }}"
|
|||
|
|
register: test_user_pass
|
|||
|
|
|
|||
|
|
- name: Debug test user salt
|
|||
|
|
ansible.builtin.debug:
|
|||
|
|
msg: "{{ test_user_salt }}"
|
|||
|
|
|
|||
|
|
- name: Debug test user password
|
|||
|
|
ansible.builtin.debug:
|
|||
|
|
msg: "{{ test_user_pass }}"
|
|||
|
|
...
|