refactor(cis): extract hardcoded values to cis_defaults and add _normalize.yml

This commit is contained in:
2026-02-21 01:26:31 +01:00
parent bef15af69f
commit f74ec325ea
8 changed files with 99 additions and 79 deletions

View File

@@ -1,4 +1,85 @@
--- ---
# User-facing API: override via top-level `cis` dict in inventory.
# Merged with these defaults in _normalize.yml → cis_cfg.
cis_defaults:
modules_blacklist:
- freevxfs
- jffs2
- hfs
- hfsplus
- cramfs
- udf
- usb-storage
- dccp
- sctp
- rds
- tipc
- firewire-core
- firewire-sbp2
- thunderbolt
sysctl:
fs.suid_dumpable: 0
kernel.dmesg_restrict: 1
kernel.kptr_restrict: 2
kernel.perf_event_paranoid: 3
kernel.unprivileged_bpf_disabled: 1
kernel.yama.ptrace_scope: 2
kernel.randomize_va_space: 2
net.ipv4.ip_forward: 0
net.ipv4.tcp_syncookies: 1
net.ipv4.icmp_echo_ignore_broadcasts: 1
net.ipv4.icmp_ignore_bogus_error_responses: 1
net.ipv4.conf.all.log_martians: 1
net.ipv4.conf.all.rp_filter: 1
net.ipv4.conf.all.secure_redirects: 0
net.ipv4.conf.all.send_redirects: 0
net.ipv4.conf.all.accept_redirects: 0
net.ipv4.conf.all.accept_source_route: 0
net.ipv4.conf.all.arp_ignore: 1
net.ipv4.conf.all.arp_announce: 2
net.ipv4.conf.default.log_martians: 1
net.ipv4.conf.default.rp_filter: 1
net.ipv4.conf.default.secure_redirects: 0
net.ipv4.conf.default.send_redirects: 0
net.ipv4.conf.default.accept_redirects: 0
net.ipv6.conf.all.accept_redirects: 0
net.ipv6.conf.all.disable_ipv6: 1
net.ipv6.conf.default.accept_redirects: 0
net.ipv6.conf.default.disable_ipv6: 1
net.ipv6.conf.lo.disable_ipv6: 1
sshd_options:
- { option: LogLevel, value: VERBOSE }
- { option: LoginGraceTime, value: "60" }
- { option: PermitRootLogin, value: "no" }
- { option: StrictModes, value: "yes" }
- { option: MaxAuthTries, value: "4" }
- { option: MaxSessions, value: "10" }
- { option: MaxStartups, value: "10:30:60" }
- { option: PubkeyAuthentication, value: "yes" }
- { option: HostbasedAuthentication, value: "no" }
- { option: IgnoreRhosts, value: "yes" }
- { option: PasswordAuthentication, value: "no" }
- { option: PermitEmptyPasswords, value: "no" }
- { option: KerberosAuthentication, value: "no" }
- { option: GSSAPIAuthentication, value: "no" }
- { option: AllowAgentForwarding, value: "no" }
- { option: AllowTcpForwarding, value: "no" }
- { option: KbdInteractiveAuthentication, value: "no" }
- { option: GatewayPorts, value: "no" }
- { option: X11Forwarding, value: "no" }
- { option: PermitUserEnvironment, value: "no" }
- { option: ClientAliveInterval, value: "300" }
- { option: ClientAliveCountMax, value: "1" }
- { option: PermitTunnel, value: "no" }
- { option: Banner, value: /etc/issue.net }
pwquality_minlen: 14
tmout: 900
umask: "077"
umask_profile: "027"
faillock_deny: 5
faillock_unlock_time: 900
password_remember: 5
# Platform-specific binary names for CIS permission targets # Platform-specific binary names for CIS permission targets
cis_fusermount_binary: "{{ 'fusermount3' if is_rhel | default(false) | bool else 'fusermount' }}" cis_fusermount_binary: "{{ 'fusermount3' if is_rhel | default(false) | bool else 'fusermount' }}"
cis_write_binary: "{{ 'write' if is_rhel | default(false) | bool else 'wall' }}" cis_write_binary: "{{ 'write' if is_rhel | default(false) | bool else 'wall' }}"

View File

@@ -0,0 +1,4 @@
---
- name: Build cis_cfg from defaults and user overrides
ansible.builtin.set_fact:
cis_cfg: "{{ cis_defaults | combine(cis | default({}), recursive=true) }}"

View File

@@ -3,7 +3,7 @@
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: "/mnt/etc/profile" path: "/mnt/etc/profile"
regexp: "^(\\s*)umask\\s+\\d+" regexp: "^(\\s*)umask\\s+\\d+"
line: "umask 027" line: "umask {{ cis_cfg.umask_profile }}"
# Non-RHEL/non-Debian distros: loop evaluates to [] (intentional skip) # Non-RHEL/non-Debian distros: loop evaluates to [] (intentional skip)
- name: Prevent Login to Accounts With Empty Password - name: Prevent Login to Accounts With Empty Password

View File

@@ -1,4 +1,7 @@
--- ---
- name: Normalize CIS configuration
ansible.builtin.include_tasks: _normalize.yml
- name: Include CIS hardening tasks - name: Include CIS hardening tasks
ansible.builtin.include_tasks: "{{ cis_task }}" ansible.builtin.include_tasks: "{{ cis_task }}"
loop: loop:

View File

@@ -1,23 +1,8 @@
--- ---
- name: Disable Kernel Modules - name: Disable Kernel Modules
vars: vars:
cis_modules_base:
- freevxfs
- jffs2
- hfs
- hfsplus
- cramfs
- udf
- usb-storage
- dccp
- sctp
- rds
- tipc
- firewire-core
- firewire-sbp2
- thunderbolt
cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}" cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}"
cis_modules_all: "{{ cis_modules_base + cis_modules_squashfs }}" cis_modules_all: "{{ cis_cfg.modules_blacklist + cis_modules_squashfs }}"
ansible.builtin.copy: ansible.builtin.copy:
dest: /mnt/etc/modprobe.d/cis.conf dest: /mnt/etc/modprobe.d/cis.conf
mode: "0644" mode: "0644"

View File

@@ -6,17 +6,17 @@
line: "{{ item.content }}" line: "{{ item.content }}"
loop: loop:
- { path: /mnt/etc/security/limits.conf, regexp: '^\*\s+hard\s+core\s+', content: "* hard core 0" } - { 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 = 14 } - { 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*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*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*ocredit\s*=', content: ocredit = -1 }
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*lcredit\s*=', content: lcredit = -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" }}' - path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
regexp: '^\s*umask\s+\d+' regexp: '^\s*umask\s+\d+'
content: umask 077 content: "umask {{ cis_cfg.umask }}"
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}' - path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
regexp: '^\s*(export\s+)?TMOUT=' regexp: '^\s*(export\s+)?TMOUT='
content: export TMOUT=900 content: "export TMOUT={{ cis_cfg.tmout }}"
- path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}' - path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}'
regexp: '^\s*#?\s*Storage=' regexp: '^\s*#?\s*Storage='
content: Storage=persistent content: Storage=persistent
@@ -36,7 +36,7 @@
}} }}
regexp: '^\s*auth\s+required\s+pam_faillock\.so' regexp: '^\s*auth\s+required\s+pam_faillock\.so'
content: >- content: >-
auth required pam_faillock.so onerr=fail audit silent deny=5 unlock_time=900 auth required pam_faillock.so onerr=fail audit silent deny={{ cis_cfg.faillock_deny }} unlock_time={{ cis_cfg.faillock_unlock_time }}
- path: >- - path: >-
/mnt/etc/{{ /mnt/etc/{{
"pam.d/common-account" "pam.d/common-account"
@@ -55,7 +55,7 @@
}} }}
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so' regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
content: >- content: >-
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5 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.deny, regexp: '^ALL:\s*ALL', content: "ALL: ALL" }
- { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" } - { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" }
loop_control: loop_control:

View File

@@ -4,31 +4,7 @@
path: /mnt/etc/ssh/sshd_config path: /mnt/etc/ssh/sshd_config
regexp: ^\s*#?{{ item.option }}\s+.*$ regexp: ^\s*#?{{ item.option }}\s+.*$
line: "{{ item.option }} {{ item.value }}" line: "{{ item.option }} {{ item.value }}"
loop: loop: "{{ cis_cfg.sshd_options }}"
- { option: LogLevel, value: VERBOSE }
- { option: LoginGraceTime, value: "60" }
- { option: PermitRootLogin, value: "no" }
- { option: StrictModes, value: "yes" }
- { option: MaxAuthTries, value: "4" }
- { option: MaxSessions, value: "10" }
- { option: MaxStartups, value: "10:30:60" }
- { option: PubkeyAuthentication, value: "yes" }
- { option: HostbasedAuthentication, value: "no" }
- { option: IgnoreRhosts, value: "yes" }
- { option: PasswordAuthentication, value: "no" }
- { option: PermitEmptyPasswords, value: "no" }
- { option: KerberosAuthentication, value: "no" }
- { option: GSSAPIAuthentication, value: "no" }
- { option: AllowAgentForwarding, value: "no" }
- { option: AllowTcpForwarding, value: "no" }
- { option: KbdInteractiveAuthentication, value: "no" }
- { option: GatewayPorts, value: "no" }
- { option: X11Forwarding, value: "no" }
- { option: PermitUserEnvironment, value: "no" }
- { option: ClientAliveInterval, value: "300" }
- { option: ClientAliveCountMax, value: "1" }
- { option: PermitTunnel, value: "no" }
- { option: Banner, value: /etc/issue.net }
loop_control: loop_control:
label: "{{ item.option }}" label: "{{ item.option }}"

View File

@@ -5,35 +5,6 @@
mode: "0644" mode: "0644"
content: | content: |
## CIS Sysctl configurations ## CIS Sysctl configurations
fs.suid_dumpable=0 {% for key, value in cis_cfg.sysctl | dictsort %}
kernel.dmesg_restrict=1 {{ key }}={{ value }}
kernel.kptr_restrict=2 {% endfor %}
kernel.perf_event_paranoid=3
kernel.unprivileged_bpf_disabled=1
kernel.yama.ptrace_scope=2
kernel.randomize_va_space=2
# Network
# Disable forwarding; override in inventory for routers/containers
net.ipv4.ip_forward=0
net.ipv4.tcp_syncookies=1
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
net.ipv4.conf.all.log_martians=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.default.log_martians=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.default.secure_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv6.conf.all.accept_redirects=0
# Disable IPv6; override in inventory if IPv6 is needed
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.accept_redirects=0
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1