Debian GNU/Linux · Logging & auditing

Lock the audit configuration and load every valid rule

When it fails: The audit rules can be changed or disabled on the running system. Anyone with root can remove audit rules or turn auditing off without a reboot, and one bad rule stops the loading of all the rules after it.

CB-DEB-AUDIT-029mediumactiveautomatedautomated fixprofile server-hardenedprofile desktop-hardened

Description

Three settings close the audit rule set:

  • -c at the start of the rules (/etc/audit/rules.d/01-cirsec-initialize.rules): if one rule fails to load, auditctl reports it and carries on with the next ones;
  • -e 2 as the last rule (/etc/audit/rules.d/99-cirsec-finalize.rules): once loaded, the configuration is immutable, rules cannot be added or removed and auditing cannot be turned off until the next reboot;
  • the compiled /etc/audit/audit.rules matches the files in /etc/audit/rules.d/ (augenrules --check), so what is on disk is what loads at boot.

The fix writes both files and loads the rules, which locks the configuration at once.

This control requires all the rule controls, so remediate.sh loads every rule before locking. The check reports enabled 2 only once the lock is loaded. While the configuration is locked, augenrules refuses to rebuild /etc/audit/audit.rules: after a rule file changes, the rules-files check fails until the next reboot, when auditd rebuilds and loads the rules.

Rationale

An intruder with root access first deletes the audit rules (auditctl -D) or stops auditing. With an immutable configuration this needs a reboot, which is itself visible. -c keeps one wrong rule from silently dropping all the following ones.

Impact

Changing the audit rules needs a reboot. Fixes of the rule controls CB-DEB-AUDIT-016 to 028 run after this one has locked the configuration write their files and ask for a reboot.

Setting

Location/etc/audit/rules.d/01-cirsec-initialize.rules, /etc/audit/rules.d/99-cirsec-finalize.rules
DefaultNot set (configuration can be changed at any time)
Recommended-c first, -e 2 last, audit.rules up to date

Audit

  1. Run auditctl -s | grep enabled. It must print enabled 2.
  2. Run grep -hx -- '-c' /etc/audit/rules.d/*.rules. It must print -c.
  3. Run grep -v '^#' /etc/audit/audit.rules | grep . | tail -n 1. It must print -e 2.
  4. Run augenrules --check. It must report no change.

Automated: every check must pass. Scanners report each check by its id.

immutable

Audit configuration locked in the kernel.

script: |
  command -v auditctl >/dev/null 2>&1 || { echo "auditctl not found: auditd is not installed"; exit 1; }
  s=$(auditctl -s 2>/dev/null | sed -n 's/^enabled //p')
  echo "enabled ${s:-unknown}"
  [ "$s" = 2 ]

rules-files

-c and -e 2 in the rules, compiled rules up to date.

script: |
  command -v augenrules >/dev/null 2>&1 || { echo "augenrules not found: auditd is not installed"; exit 1; }
  rc=0
  if grep -hqx -- '-c' /etc/audit/rules.d/*.rules 2>/dev/null; then echo "-c set"; else echo "-c missing"; rc=1; fi
  l=$(grep -v '^[[:space:]]*#' /etc/audit/audit.rules 2>/dev/null | grep . | tail -n 1)
  echo "last rule: ${l:-none}"
  [ "$l" = "-e 2" ] || rc=1
  o=$(augenrules --check 2>&1)
  case $o in
    *"No change"*) echo "audit.rules matches rules.d" ;;
    *) echo "audit.rules differs from rules.d: $o"; rc=1 ;;
  esac
  exit "$rc"

Remediation

  1. Create /etc/audit/rules.d/01-cirsec-initialize.rules containing -c.
  2. Create /etc/audit/rules.d/99-cirsec-finalize.rules containing -e 2.
  3. Run augenrules --load once all the other rules are in place. The configuration is then locked until the next reboot.
Restartnone
Disruptionlow

Rollback

Delete /etc/audit/rules.d/99-cirsec-finalize.rules and 01-cirsec-initialize.rules, run augenrules and reboot.

finalize

command -v augenrules >/dev/null 2>&1 || { echo "auditd is not installed" >&2; exit 1; }
for x in 01-cirsec-initialize:-c '99-cirsec-finalize:-e 2'; do
  f=/etc/audit/rules.d/${x%%:*}.rules
  if [ "$(grep -v '^#' "$f" 2>/dev/null)" != "${x#*:}" ]; then
    cb_backup "$f"
    printf '%s\n' '# Written by CIRSEC Baselines CB-DEB-AUDIT-029' "${x#*:}" >"$f"
  fi
  chmod 640 "$f"
done
if auditctl -s | grep -q '^enabled 2'; then
  echo "audit rules are already locked: reboot to load changes"
else
  augenrules --load >/dev/null
fi

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64, BIOS boot. Fails on fresh install; fix writes -c and -e 2 and locks (enabled 2); augenrules --check reports no change; locked after reboot; a changed rule file makes rules-files fail until reboot.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64, BIOS boot. Fails on fresh install; fix writes -c and -e 2 and locks (enabled 2); augenrules --check reports no change; locked after reboot; a changed rule file makes rules-files fail until reboot.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr