219 lines
8.0 KiB
YAML
219 lines
8.0 KiB
YAML
---
|
|
- name: Restrict core dumps
|
|
when: cis_effective_rules.core_dumps | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/security/limits.conf
|
|
regexp: '^\*\s+hard\s+core\s+'
|
|
line: "* hard core 0"
|
|
|
|
- name: Ensure the systemd coredump drop-in directory exists (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.core_dumps | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.file:
|
|
path: /mnt/etc/systemd/coredump.conf.d
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Disable systemd core dump storage and backtraces (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.core_dumps | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.copy:
|
|
dest: /mnt/etc/systemd/coredump.conf.d/10-cis.conf
|
|
mode: "0644"
|
|
content: |
|
|
[Coredump]
|
|
Storage=none
|
|
ProcessSizeMax=0
|
|
|
|
- name: Set password quality requirements
|
|
when: cis_effective_rules.pwquality | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/security/pwquality.conf
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
loop:
|
|
- {regexp: '^\s*#?\s*minlen\s*=', line: "minlen = {{ cis_cfg.pwquality_minlen }}"}
|
|
- {regexp: '^\s*#?\s*dcredit\s*=', line: "dcredit = -1"}
|
|
- {regexp: '^\s*#?\s*ucredit\s*=', line: "ucredit = -1"}
|
|
- {regexp: '^\s*#?\s*ocredit\s*=', line: "ocredit = -1"}
|
|
- {regexp: '^\s*#?\s*lcredit\s*=', line: "lcredit = -1"}
|
|
loop_control:
|
|
label: "{{ item.line }}"
|
|
|
|
# Stricter complexity SSG cis_server_l1 checks; affects only new-password changes.
|
|
- name: Set strict password quality requirements (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.pwquality | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/security/pwquality.conf
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
loop:
|
|
- {regexp: '^\s*#?\s*difok\s*=', line: "difok = {{ cis_cfg.pwquality_difok }}"}
|
|
- {regexp: '^\s*#?\s*maxrepeat\s*=', line: "maxrepeat = {{ cis_cfg.pwquality_maxrepeat }}"}
|
|
- {regexp: '^\s*#?\s*maxsequence\s*=', line: "maxsequence = {{ cis_cfg.pwquality_maxsequence }}"}
|
|
- {regexp: '^\s*#?\s*minclass\s*=', line: "minclass = {{ cis_cfg.pwquality_minclass }}"}
|
|
- {regexp: '^\s*#?\s*dictcheck\s*=', line: "dictcheck = {{ cis_cfg.pwquality_dictcheck }}"}
|
|
- {regexp: '^\s*#?\s*enforce_for_root\b', line: "enforce_for_root"}
|
|
loop_control:
|
|
label: "{{ item.line }}"
|
|
|
|
- name: Set the default shell umask
|
|
when: cis_effective_rules.umask_default | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
|
regexp: '^\s*umask\s+\d+'
|
|
line: "umask {{ cis_cfg.umask }}"
|
|
|
|
- name: Set the shell idle timeout
|
|
when: cis_effective_rules.shell_timeout | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
|
regexp: '^\s*(export\s+)?TMOUT='
|
|
line: "export TMOUT={{ cis_cfg.tmout }}"
|
|
|
|
# A drop-in survives systemd upgrades; the RHEL vendor journald.conf does not.
|
|
- name: Ensure the journald drop-in directory exists
|
|
when: cis_effective_rules.journald_persistent | default(false)
|
|
ansible.builtin.file:
|
|
path: /mnt/etc/systemd/journald.conf.d
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Enable persistent journald storage
|
|
when: cis_effective_rules.journald_persistent | default(false)
|
|
ansible.builtin.copy:
|
|
dest: /mnt/etc/systemd/journald.conf.d/10-cis.conf
|
|
mode: "0644"
|
|
content: |
|
|
[Journal]
|
|
Storage=persistent
|
|
|
|
- name: Compress large journald log files (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.journald_persistent | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.copy:
|
|
dest: /mnt/etc/systemd/journald.conf.d/20-cis-compress.conf
|
|
mode: "0644"
|
|
content: |
|
|
[Journal]
|
|
Compress=yes
|
|
|
|
- name: Log sudo commands
|
|
when: cis_effective_rules.sudo_logfile | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/sudoers
|
|
regexp: '^\s*Defaults\s+logfile='
|
|
line: 'Defaults logfile="/var/log/sudo.log"'
|
|
|
|
- name: Require a pty for sudo (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.sudo_logfile | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/sudoers
|
|
regexp: '^\s*Defaults\s+use_pty\b'
|
|
line: "Defaults use_pty"
|
|
|
|
- name: Restrict su to the wheel group
|
|
when: cis_effective_rules.su_restriction | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/pam.d/su
|
|
regexp: '^\s*#?\s*auth\s+required\s+pam_wheel\.so'
|
|
line: auth required pam_wheel.so
|
|
|
|
# authselect wires the pam_faillock stack via the feature; deny/unlock_time live
|
|
# in faillock.conf, the supported place (pam_faillock(8) deprecates module args).
|
|
- name: Configure account lockout (authselect)
|
|
when:
|
|
- cis_effective_rules.faillock | default(false)
|
|
- is_authselect | bool
|
|
block:
|
|
- name: Enable the authselect faillock feature
|
|
ansible.builtin.command: "{{ chroot_command }} authselect enable-feature with-faillock"
|
|
register: cis_faillock_result
|
|
changed_when: cis_faillock_result.rc == 0
|
|
|
|
- name: Set faillock thresholds
|
|
ansible.builtin.lineinfile:
|
|
path: /mnt/etc/security/faillock.conf
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
create: true
|
|
mode: "0644"
|
|
loop:
|
|
- {regexp: '^\s*#?\s*deny\s*=', line: "deny = {{ cis_cfg.faillock_deny }}"}
|
|
- {regexp: '^\s*#?\s*unlock_time\s*=', line: "unlock_time = {{ cis_cfg.faillock_unlock_time }}"}
|
|
loop_control:
|
|
label: "{{ item.line }}"
|
|
|
|
- name: Configure account lockout
|
|
when:
|
|
- cis_effective_rules.faillock | default(false)
|
|
- not is_authselect | bool
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ item.path }}"
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
loop:
|
|
- path: '/mnt/etc/{{ "pam.d/common-auth" if is_debian | bool else "pam.d/system-auth" }}'
|
|
regexp: '^\s*auth\s+required\s+pam_faillock\.so'
|
|
line: >-
|
|
auth required pam_faillock.so onerr=fail audit silent deny={{ cis_cfg.faillock_deny }} unlock_time={{ cis_cfg.faillock_unlock_time }}
|
|
- path: '/mnt/etc/{{ "pam.d/common-account" if is_debian | bool else "pam.d/system-auth" }}'
|
|
regexp: '^\s*account\s+required\s+pam_faillock\.so'
|
|
line: account required pam_faillock.so
|
|
loop_control:
|
|
label: "{{ item.regexp }}"
|
|
|
|
- name: Enforce password history
|
|
when: cis_effective_rules.password_history | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: >-
|
|
/mnt/etc/pam.d/{{
|
|
"common-password"
|
|
if is_debian | bool
|
|
else "passwd"
|
|
}}
|
|
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
|
|
line: >-
|
|
password [success=1 default=ignore] pam_unix.so obscure sha512 remember={{ cis_cfg.password_remember }}
|
|
|
|
# SSG cis_server_l1 checks pam_pwhistory (not pam_unix remember) in the auth-stack
|
|
# files; affects only password changes, so no login-lockout risk. EL9 has no
|
|
# authselect path here (same direct-edit the faillock rule above uses).
|
|
- name: Enforce password reuse limit via pam_pwhistory (CIS L1+)
|
|
when:
|
|
- cis_effective_rules.password_history | default(false)
|
|
- cis_strict | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ item }}"
|
|
regexp: '^\s*password\s+(requisite|required)\s+pam_pwhistory\.so'
|
|
line: "password requisite pam_pwhistory.so use_authtok remember={{ cis_cfg.pwhistory_remember }} enforce_for_root"
|
|
insertbefore: '^\s*password\s+.*pam_unix\.so'
|
|
loop: >-
|
|
{{
|
|
['/mnt/etc/pam.d/system-auth', '/mnt/etc/pam.d/password-auth']
|
|
if is_rhel | bool
|
|
else (['/mnt/etc/pam.d/common-password'] if is_debian | bool else [])
|
|
}}
|
|
loop_control:
|
|
label: "{{ item }}"
|
|
|
|
|
|
- name: Configure TCP wrappers
|
|
when: cis_effective_rules.tcp_wrappers | default(false)
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ item.path }}"
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
loop:
|
|
- {path: /mnt/etc/hosts.deny, regexp: '^ALL:\s*ALL', line: "ALL: ALL"}
|
|
- {path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', line: "sshd: ALL"}
|
|
loop_control:
|
|
label: "{{ item.path }}"
|