The FreeBSD MAC Framework: Policy Hooks Beyond Traditional Unix Permissions
How FreeBSD's TrustedBSD MAC framework composes policy modules, labels and access checks, plus a safe path for selecting, testing, and auditing policy.
FreeBSD’s Mandatory Access Control framework is not one monolithic security mode. It is a set of kernel entry points, label mechanisms, and lifecycle services through which policy modules can mediate operations involving credentials, files, sockets, processes, interfaces, and other objects. Modules such as mac_bsdextended, mac_portacl, MLS, Biba, and partition policies implement different models.
DAC remains present underneath MAC
Traditional Unix discretionary access control (owner, group, mode bits, ACLs, privilege) still applies. A MAC policy can add another denial or constraint; enabling it does not erase ordinary permissions. Conversely, a permissive MAC decision does not grant access that DAC denies.
This composition explains why “permission denied” needs evidence. Check credentials, ACLs, file flags, Capsicum state, jail context, and loaded MAC policies rather than changing whichever control is most visible.
kldstat
sysctl security.mac
Available sysctls depend on compiled or loaded modules. Do not load an enforcement policy on a remote-only production host before understanding its labels, boot behavior, and recovery path.
Policy modules choose their own semantics
mac_bsdextended(4) implements a rule system that can match subjects and objects by IDs, groups, filesystems, ownership, and requested access. ugidfw(8) manages those rules. It is useful for adding constraints that mode bits alone cannot express, but a broad rule can lock out administration.
mac_portacl(4) controls binding to selected TCP/UDP ports according to UID/GID rules, offering a narrower alternative to granting a process general privilege merely to bind a low port. Biba and MLS label data and subjects according to integrity or confidentiality models and require much more extensive planning.
The presence of a framework hook does not mean every policy labels every object or mediates every operation. Read the specific module manual and test the exact system calls and filesystems in use.
Labels can be persistent or transient
Some policies associate labels with credentials and filesystem objects. Filesystem support, mount options, extended attributes, and policy configuration determine whether labels persist. A backup or copy tool that preserves file contents and POSIX metadata may still omit MAC labels, changing enforcement after restore.
Before selecting a labeled policy, test package upgrades, boot environments, backups, archive/copy tools, jails, NFS, removable media, and rescue mounts. Document how labels are initialized and repaired; relabeling a large live filesystem is not a routine emergency action.
Load and enforce in controlled stages
Begin with a written subject/object matrix: which service identity needs which operation on which resources, and which negative cases must fail. Build the policy in a VM or console-accessible clone. Keep a known-good boot environment and know how to unload or disable the module from single-user mode when the policy permits it.
For mac_bsdextended, add a narrow rule and list the resulting ruleset:
ugidfw list
The exact rule syntax belongs to ugidfw(8) and should be generated from reviewed IDs, not examples copied with arbitrary numeric users. Stable service accounts matter; reusing a UID can transfer policy consequences to an unrelated account.
Do not enable several complex policies at once. When a test fails, identify which layer denied it through logs, audit records, controlled comparison, and policy-specific diagnostics. A stack of unobserved denials is not defense in depth; it is an outage whose cause is hidden.
Jails and capabilities solve different problems
Jails constrain namespace and privilege context, while Capsicum limits a process to capabilities derived from already-held descriptors. MAC policies can mediate operations across those boundaries. None is a universal substitute for the others, and none makes an unsafe network service trustworthy.
Combine the smallest policy with service privilege separation, read-only datasets, file flags where appropriate, pf, resource limits, updates, and audit logging. Verify behavior after every FreeBSD upgrade because kernel hooks, module behavior, and application access patterns can change.
Make recovery part of acceptance
Acceptance tests should cover boot, service start, expected access, explicit denies, log rotation, package update, backup/restore, jail lifecycle, policy reload, and console recovery. Preserve loaded module hashes, sysctls, rules, label state, and audit evidence.
The MAC framework adds precise policy insertion points. Security comes from selecting a narrowly understood module and proving its entire lifecycle—not from enabling the most restrictive-sounding acronym.
Related:
- How to Place FreeBSD Tunables in sysctl.conf, loader.conf, or rc.conf Correctly
- Fixing FreeBSD ZFS Boot Environments That Consume Unexpected Pool Space
Sources: