feat(cis): add selectable profile and per-rule hardening toggles

This commit is contained in:
2026-05-25 04:37:33 +02:00
parent d2a19cfd5c
commit 2c35409519
23 changed files with 753 additions and 192 deletions

View File

@@ -13,7 +13,7 @@ Non-Arch targets require the appropriate package manager available from the ISO
- 4.1 [Core Variables](#41-core-variables)
- 4.2 [`system` Dictionary](#42-system-dictionary)
- 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary)
- 4.4 [`cis` Dictionary](#44-cis-dictionary)
- 4.4 [CIS Hardening](#44-cis-hardening)
- 4.5 [VMware Guest Operations](#45-vmware-guest-operations)
- 4.6 [Multi-Disk Schema](#46-multi-disk-schema)
- 4.7 [Advanced Partitioning Overrides](#47-advanced-partitioning-overrides)
@@ -59,12 +59,10 @@ Non-Arch targets require the appropriate package manager available from the ISO
Two dict-based variables drive the entire configuration:
- **`system`** -- host, network, users, disk layout, encryption, and feature toggles
- **`system`** -- host, network, users, disk layout, encryption, and feature toggles (including CIS hardening under `system.features.cis`)
- **`hypervisor`** -- virtualization backend credentials and targeting
An optional third dict **`cis`** overrides CIS hardening parameters when `system.features.cis.enabled: true`.
All three are standard Ansible variables. Place them in `group_vars/`, `host_vars/`, or inline inventory. With `hash_behaviour = merge`, dictionaries merge across scopes, so shared values go in group vars and host-specific overrides go per-host.
Both are standard Ansible variables. Place them in `group_vars/`, `host_vars/`, or inline inventory. With `hash_behaviour = merge`, dictionaries merge across scopes, so shared values go in group vars and host-specific overrides go per-host.
### Variable Placement
@@ -148,7 +146,7 @@ all:
### 4.1 Core Variables
Top-level variables outside `system`/`hypervisor`/`cis`.
Top-level variables outside `system`/`hypervisor`.
| Variable | Type | Default | Description |
| ---------------- | ------ | -------------------------- | ---------------------------------------------------- |
@@ -268,7 +266,10 @@ The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `
| Key | Type | Default | Description |
| ------------------ | ------ | -------------- | ------------------------------------ |
| `cis.enabled` | bool | `false` | Enable CIS hardening (see [4.4](#44-cis-dictionary)) |
| `cis.enabled` | bool | `false` | Enable CIS hardening (see [4.4](#44-cis-hardening)) |
| `cis.profile` | string | `default` | CIS profile: `default`, `l1`, or `l2` (see [4.4](#44-cis-hardening)) |
| `cis.rules` | dict | `{}` | Per-rule CIS overrides |
| `cis.params` | dict | `{}` | CIS parameter overrides |
| `selinux.enabled` | bool | `true` | SELinux management |
| `firewall.enabled` | bool | `true` | Firewall setup |
| `firewall.backend` | string | `firewalld` | `firewalld` or `ufw` |
@@ -457,44 +458,50 @@ system:
| `certs` | bool | `false` | TLS certificate validation (VMware) |
| `ssh` | bool | `false` | Enable SSH on guest and switch connection (VMware) |
### 4.4 `cis` Dictionary
### 4.4 CIS Hardening
When `system.features.cis.enabled: true`, the CIS role applies hardening. All values have sensible defaults; override specific keys via the `cis` dict.
When `system.features.cis.enabled: true`, the CIS role applies hardening. The behaviour is driven by three keys under `system.features.cis`:
| Key | Type | Default | Description |
| -------------------- | ------ | ------- | ------------------------------------------------ |
| `modules_blacklist` | list | see below | Kernel modules to blacklist via modprobe |
| `sysctl` | dict | see below | Sysctl key/value pairs written to `10-cis.conf` |
| `sshd_options` | list | see below | SSHD options applied via lineinfile |
| `pwquality_minlen` | int | `14` | Minimum password length |
| `tmout` | int | `900` | Shell timeout (seconds) |
| `umask` | string | `077` | Default umask in bashrc |
| `umask_profile` | string | `027` | Default umask in /etc/profile |
| `faillock_deny` | int | `5` | Failed login attempts before lockout |
| `faillock_unlock_time` | int | `900` | Lockout duration (seconds) |
| `password_remember` | int | `5` | Password history depth |
| Key | Type | Default | Description |
| --------- | ------ | ----------- | ----------------------------------------------------------------- |
| `enabled` | bool | `false` | Apply CIS hardening at all |
| `profile` | string | `default` | `default` (house baseline), `l1` (clean CIS Level 1), or `l2` |
| `rules` | dict | `{}` | Per-rule on/off overrides on top of the profile |
| `params` | dict | `{}` | Parameter overrides (deep-merged; list values replace wholesale) |
**Default modules blacklist:** `freevxfs`, `jffs2`, `hfs`, `hfsplus`, `cramfs`, `udf`, `usb-storage`, `dccp`, `sctp`, `rds`, `tipc`, `firewire-core`, `firewire-sbp2`, `thunderbolt`. `squashfs` is added automatically except on Ubuntu (snap dependency).
**Profiles.** `default` is the established house baseline (CIS Level 1 plus the USB lockdown, full module blacklist, and IPv6-disable extras, minus the usability-hostile controls). `l1` is a clean CIS Level 1: it drops the L2 extras and adds password aging, AIDE, and warning banners. `l2` is `l1` plus auditd and the L2 extras.
**Default sysctl settings** include: `kernel.yama.ptrace_scope=2`, `kernel.kptr_restrict=2`, `kernel.perf_event_paranoid=3`, `kernel.unprivileged_bpf_disabled=1`, IPv4/IPv6 hardening, ARP protection, and IPv6 disabled by default. Override individual keys:
**Per-rule overrides.** Toggle an individual rule without changing profile, e.g. keep the default profile but allow USB and IPv6 on a desktop:
```yaml
cis:
sysctl:
net.ipv6.conf.all.disable_ipv6: 0 # re-enable IPv6
net.ipv4.ip_forward: 1 # enable for routers/containers
system:
features:
cis:
enabled: true
rules:
usb_lockdown: false
ipv6_disable: false
```
**Default SSHD options** enforce: `PermitRootLogin no`, `PasswordAuthentication no`, `X11Forwarding no`, `AllowTcpForwarding no`, `MaxAuthTries 4`, and post-quantum KEX (mlkem768x25519-sha256 on OpenSSH 9.9+). Override per-option:
Rule keys: `module_blacklist`, `usb_lockdown`, `sysctl_hardening`, `ipv6_disable`, `umask_default`, `empty_password_login`, `pwquality`, `core_dumps`, `shell_timeout`, `journald_persistent`, `sudo_logfile`, `su_restriction`, `faillock`, `password_history`, `tcp_wrappers`, `crypto_policy`, `mask_services`, `cron_at_access`, `file_permissions`, `sshd_hardening`, `password_expiry`, `aide`, `warning_banners`, `auditd`, and the opt-in `grub_password` (set `rules.grub_password: true` with `params.grub_password_hash`).
**Parameters.** Override baseline values under `params` (full list in `roles/cis/vars/main.yml`):
```yaml
cis:
sshd_options:
- { option: X11Forwarding, value: "yes" }
- { option: AllowTcpForwarding, value: "yes" }
system:
features:
cis:
enabled: true
profile: l1
params:
pwquality_minlen: 16
sysctl: # dict: deep-merged over the profile's set
net.ipv4.ip_forward: 1
sshd_options: # list: REPLACES the entire default list
- {option: X11Forwarding, value: "yes"}
```
Note: providing `sshd_options` replaces the entire list. Copy the defaults from `roles/cis/defaults/main.yml` and modify as needed.
Common params: `modules_blacklist` (list), `sysctl` (dict), `sshd_options` (list), `pwquality_minlen` (14), `tmout` (900), `umask` (077), `umask_profile` (027), `faillock_deny` (5), `faillock_unlock_time` (900), `password_remember` (5), `pass_max_days` (365), `aide_cron_hour`/`aide_cron_minute`, `banner_text`, `grub_password_hash`.
### 4.5 VMware Guest Operations