feat(cis): add selectable profile and per-rule hardening toggles
This commit is contained in:
@@ -1,31 +1,138 @@
|
||||
---
|
||||
- name: Add Security related lines into config files
|
||||
- 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
|
||||
|
||||
- name: Configure account lockout
|
||||
when: cis_effective_rules.faillock | default(false)
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ item.path }}"
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.content }}"
|
||||
line: "{{ item.line }}"
|
||||
loop:
|
||||
- { path: /mnt/etc/security/limits.conf, regexp: '^\*\s+hard\s+core\s+', content: "* hard core 0" }
|
||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*minlen\s*=', content: "minlen = {{ cis_cfg.pwquality_minlen }}" }
|
||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*dcredit\s*=', content: dcredit = -1 }
|
||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ucredit\s*=', content: ucredit = -1 }
|
||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ocredit\s*=', content: ocredit = -1 }
|
||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*lcredit\s*=', content: lcredit = -1 }
|
||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
||||
regexp: '^\s*umask\s+\d+'
|
||||
content: "umask {{ cis_cfg.umask }}"
|
||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
||||
regexp: '^\s*(export\s+)?TMOUT='
|
||||
content: "export TMOUT={{ cis_cfg.tmout }}"
|
||||
- path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}'
|
||||
regexp: '^\s*#?\s*Storage='
|
||||
content: Storage=persistent
|
||||
- path: /mnt/etc/sudoers
|
||||
regexp: '^\s*Defaults\s+logfile='
|
||||
content: Defaults logfile="/var/log/sudo.log"
|
||||
- path: /mnt/etc/pam.d/su
|
||||
regexp: '^\s*#?\s*auth\s+required\s+pam_wheel\.so'
|
||||
content: auth required pam_wheel.so
|
||||
- path: >-
|
||||
/mnt/etc/{{
|
||||
"pam.d/common-auth"
|
||||
@@ -35,7 +142,7 @@
|
||||
else "pam.d/system-auth"
|
||||
}}
|
||||
regexp: '^\s*auth\s+required\s+pam_faillock\.so'
|
||||
content: >-
|
||||
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/{{
|
||||
@@ -46,17 +153,53 @@
|
||||
else "pam.d/system-auth"
|
||||
}}
|
||||
regexp: '^\s*account\s+required\s+pam_faillock\.so'
|
||||
content: account required pam_faillock.so
|
||||
- path: >-
|
||||
/mnt/etc/pam.d/{{
|
||||
"common-password"
|
||||
if is_debian | bool
|
||||
else "passwd"
|
||||
}}
|
||||
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
|
||||
content: >-
|
||||
password [success=1 default=ignore] pam_unix.so obscure sha512 remember={{ cis_cfg.password_remember }}
|
||||
- { path: /mnt/etc/hosts.deny, regexp: '^ALL:\s*ALL', content: "ALL: ALL" }
|
||||
- { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" }
|
||||
line: account required pam_faillock.so
|
||||
loop_control:
|
||||
label: "{{ item.content }}"
|
||||
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 }}"
|
||||
|
||||
Reference in New Issue
Block a user