diff options
Diffstat (limited to 'nixos/modules/security/audit.nix')
-rw-r--r-- | nixos/modules/security/audit.nix | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix new file mode 100644 index 00000000000..2b22bdd9f0a --- /dev/null +++ b/nixos/modules/security/audit.nix @@ -0,0 +1,123 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.security.audit; + enabled = cfg.enable == "lock" || cfg.enable; + + failureModes = { + silent = 0; + printk = 1; + panic = 2; + }; + + disableScript = pkgs.writeScript "audit-disable" '' + #!${pkgs.runtimeShell} -eu + # Explicitly disable everything, as otherwise journald might start it. + auditctl -D + auditctl -e 0 -a task,never + ''; + + # TODO: it seems like people like their rules to be somewhat secret, yet they will not be if + # put in the store like this. At the same time, it doesn't feel like a huge deal and working + # around that is a pain so I'm leaving it like this for now. + startScript = pkgs.writeScript "audit-start" '' + #!${pkgs.runtimeShell} -eu + # Clear out any rules we may start with + auditctl -D + + # Put the rules in a temporary file owned and only readable by root + rulesfile="$(mktemp)" + ${concatMapStrings (x: "echo '${x}' >> $rulesfile\n") cfg.rules} + + # Apply the requested rules + auditctl -R "$rulesfile" + + # Enable and configure auditing + auditctl \ + -e ${if cfg.enable == "lock" then "2" else "1"} \ + -b ${toString cfg.backlogLimit} \ + -f ${toString failureModes.${cfg.failureMode}} \ + -r ${toString cfg.rateLimit} + ''; + + stopScript = pkgs.writeScript "audit-stop" '' + #!${pkgs.runtimeShell} -eu + # Clear the rules + auditctl -D + + # Disable auditing + auditctl -e 0 + ''; +in { + options = { + security.audit = { + enable = mkOption { + type = types.enum [ false true "lock" ]; + default = false; + description = '' + Whether to enable the Linux audit system. The special `lock' value can be used to + enable auditing and prevent disabling it until a restart. Be careful about locking + this, as it will prevent you from changing your audit configuration until you + restart. If possible, test your configuration using build-vm beforehand. + ''; + }; + + failureMode = mkOption { + type = types.enum [ "silent" "printk" "panic" ]; + default = "printk"; + description = "How to handle critical errors in the auditing system"; + }; + + backlogLimit = mkOption { + type = types.int; + default = 64; # Apparently the kernel default + description = '' + The maximum number of outstanding audit buffers allowed; exceeding this is + considered a failure and handled in a manner specified by failureMode. + ''; + }; + + rateLimit = mkOption { + type = types.int; + default = 0; + description = '' + The maximum messages per second permitted before triggering a failure as + specified by failureMode. Setting it to zero disables the limit. + ''; + }; + + rules = mkOption { + type = types.listOf types.str; # (types.either types.str (types.submodule rule)); + default = []; + example = [ "-a exit,always -F arch=b64 -S execve" ]; + description = '' + The ordered audit rules, with each string appearing as one line of the audit.rules file. + ''; + }; + }; + }; + + config = { + systemd.services.audit = { + description = "Kernel Auditing"; + wantedBy = [ "basic.target" ]; + + unitConfig = { + ConditionVirtualization = "!container"; + ConditionSecurity = [ "audit" ]; + }; + + + path = [ pkgs.audit ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "@${if enabled then startScript else disableScript} audit-start"; + ExecStop = "@${stopScript} audit-stop"; + }; + }; + }; +} |