How to Configure Audit(4) for Security Event Logging on FreeBSD
Setting up FreeBSD's Basic Security Module audit framework to log security-relevant events with the right level of detail without drowning in noise.
FreeBSD’s audit(4) subsystem, inherited from the Sun/TrustedBSD Basic Security Module lineage, provides kernel-level security event auditing that’s meaningfully more detailed and tamper-resistant than ordinary syslog — it’s built specifically to answer “who did what, when, and did it succeed or fail” for security-relevant actions, with an event classification system that lets you scope exactly what gets recorded instead of an all-or-nothing firehose.
Enabling the audit subsystem
Auditing isn’t active by default and needs to be explicitly enabled and configured before it captures anything:
sysrc auditd_enable="YES"
The core configuration lives under /etc/security/audit_control, which defines what gets audited, where logs are stored, and how log rotation is handled.
Understanding audit classes
Rather than auditing individual syscalls one by one, audit(4) groups related events into classes — lo (login/logout), ad (administrative actions), fw (file write), fc (file creation), fd (file deletion), pc (process creation), and others — and you configure auditing at the class level, which is both more manageable and closer to how you actually think about “what do I need visibility into”:
# /etc/security/audit_control
dir:/var/audit
flags:lo,aa,ad,fd,fc
minfree:20
naflags:lo,aa
policy:cnt,argv
filesz:10M
flags sets the classes audited for successful actions by ordinary authenticated users. naflags sets classes audited for events with no authenticated subject at all — failed login attempts before authentication succeeds, for instance, which matter enormously for security monitoring but don’t have a “user” to attribute them to in the normal sense.
Choosing a starting flag set that isn’t overwhelming
A genuinely common mistake is enabling broad classes like fc,fd,fw (all file creation, deletion, and write events) system-wide on a busy machine, which floods the audit trail with routine, uninteresting activity — every log rotation, every temp file, every package manager operation — burying the security-relevant events you actually wanted visibility into. A more targeted starting point focuses on authentication and administrative action classes first:
flags:lo,aa,ad
naflags:lo,aa
lo (login/logout), aa (authentication and authorization events), and ad (administrative actions — user/group management, most privileged configuration changes) capture the events most commonly relevant to a security audit trail without the noise of routine file I/O. File-level auditing (fc, fd, fw) is worth adding selectively, scoped to specific directories via audit’s path-based configuration, rather than applied blanket system-wide.
Scoping file auditing to specific paths
Rather than auditing every file event everywhere, audit_control’s companion mechanisms let you narrow file-related auditing to directories that actually matter — configuration directories, credential stores, specific application data:
sysctl security.audit.fstat_flags
is a good introductory check, but for genuinely path-scoped auditing, combining broader class-based audit with praudit post-processing (filtering the audit trail for events touching specific paths, rather than trying to configure the kernel to only audit specific paths in the first place) is usually the more practical approach on FreeBSD, since kernel-level auditing decisions are made per-class rather than per-path.
Reading the audit trail
Audit logs are stored in a binary format, not human-readable text — praudit converts them into a readable stream:
praudit -l /var/audit/current
The -l flag prints each record on a single line rather than the default multi-line format, which is considerably easier to grep and filter through when investigating a specific event class or timeframe:
praudit -l /var/audit/current | grep -i "failed"
Rotating and retaining audit logs
audit_control’s filesz directive (shown above as 10M) triggers automatic log rotation once the current audit trail file reaches that size, and expire-after/minfree directives (also configurable in the same file) control retention and disk-space protection — minfree specifically defines a filesystem free-space percentage below which the audit daemon will take protective action (by default, this can include halting further auditing or even the system, depending on policy settings) rather than silently filling the disk with audit data and taking down unrelated services when the filesystem runs out of space.
expire-after:30d AND 500M
This example expires audit trail files older than 30 days and only once total audit storage exceeds 500MB — combining both conditions with AND means both thresholds must be exceeded before old files are purged, which is a reasonable default balancing retention against unbounded disk consumption on a system with a meaningful volume of audited activity.
Restarting auditd after configuration changes
Configuration changes to audit_control require restarting the audit daemon to take effect — simply editing the file doesn’t retroactively change what the currently-running daemon is doing:
service auditd restart
Verify the new configuration actually took effect by checking praudit’s output for the classes you just added or removed, rather than assuming a config file edit was sufficient on its own — a restart that silently fails (a syntax error in audit_control, for instance) is far better caught immediately via verification than discovered weeks later when an investigation turns up a gap in the audit trail that should have been captured.