nixpkgs/nixos/modules/security/audit.nix
2023-01-21 18:08:38 +01:00

124 lines
3.7 KiB
Nix

{ 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 = lib.mdDoc ''
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 = lib.mdDoc "How to handle critical errors in the auditing system";
};
backlogLimit = mkOption {
type = types.int;
default = 64; # Apparently the kernel default
description = lib.mdDoc ''
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 = lib.mdDoc ''
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 = lib.mdDoc ''
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";
};
};
};
}