Skip to content

MAC Profiles SELinux and AppArmor

Antonios Voulvoulis edited this page Jul 11, 2026 · 2 revisions

MAC Profiles: SELinux and AppArmor

NFTBan uses MAC profiles as defense-in-depth around the nftband daemon.

Normal Linux permissions decide whether a service may run. SELinux and AppArmor decide what that service may access after it is running. This matters because nftband manages nftables firewall state, SSH protection sets, ban/unban state, and runtime firewall objects.

NFTBan ships MAC integration for the two main Linux families:

  • SELinux for EL/RHEL-family systems such as AlmaLinux, Rocky Linux, CentOS Stream, and RHEL.
  • AppArmor for Debian and Ubuntu systems.

These profiles are not a replacement for polkit. Polkit authorizes user-requested privileged actions. SELinux and AppArmor confine running processes. If SELinux denies nftband access to netlink/nftables, polkit cannot override that denial.

Scope boundary

Shipped MAC coverage includes:

  • SELinux policy for nftband on EL systems.
  • AppArmor profile for nftband on Debian/Ubuntu systems.
  • The SELinux daemon-netlink fix for Enforcing hosts.

Current limitations (tracked future hardening, not yet shipped):

  • Daemon least-privilege redesign.
  • Audit-log uid/gid/pid changes.
  • Whitelist verification or immutable-file health checks.
  • Polkit changes.
  • Public JSON health schema changes (current health-JSON schema is 1.84.0).

Why both?

Both SELinux and AppArmor are Mandatory Access Control (MAC) systems: they confine a running process after normal Linux permissions have already allowed it to execute. Linux distributions ship different MAC systems, so NFTBan ships both.

Family MAC system NFTBan profile
EL / RHEL family (AlmaLinux, Rocky, CentOS Stream, RHEL) SELinux policy module nftban
Debian / Ubuntu AppArmor profile usr.lib.nftban.bin.nftband

nftband runs privileged (it programs nftables via netlink). Without MAC, a daemon compromise has broad system reach. With MAC, nftband can access only the NFTBan paths plus the netlink/nftables operations it needs, and is blocked from everything else.

What ships today

SELinux (EL family)

  • Ships nftban.te / nftban.if / nftban.fc and a compiled nftban.pp under /usr/share/nftban/selinux/. The .pp is compiled at package build (refpolicy devel Makefile); the RPM %post installs it with semodule -i (guarded by selinuxenabled).
  • Labels /usr/lib/nftban/bin/nftband as nftband_exec_t and transitions the daemon into the nftband_t domain on exec.
  • Keeps NoNewPrivileges=true and still transitions, via the process2:nnp_transition permission — NNP is not relaxed on any host.
  • Allows the netlink/nftables operations the daemon needs, and fixes the EL Enforcing failure cannot list tables: socket: permission denied.

The nftband_t domain today is intentionally broad — it matches what the daemon does today (it shells out to several system tools). Tightening it is a tracked follow-up that must come after the daemon's shell-outs are reduced.

AppArmor (Debian / Ubuntu)

  • Ships the daemon profile to /etc/apparmor.d/usr.lib.nftban.bin.nftband, confining /usr/lib/nftban/bin/nftband.
  • Default mode: complain (denials logged, not enforced) for safe fleet soak.
  • Uses the attach_disconnected profile flag, which is required because the unit runs the daemon in a private mount namespace; without it the systemd notify socket fails and the daemon cannot signal readiness.
  • The DEB postinst loads the profile (apparmor_parser -r); postrm removes it (apparmor_parser -R).

Daemon

The daemon received two small defensive nil-guards so it degrades gracefully instead of crashing when a systemd-passed socket fd is mediated to nil under confinement. This is a robustness fix, not a least-privilege redesign (a tracked future hardening item). Because of these, the daemon binary is not byte-identical to v1.146.0.

Why polkit is not the fix

Polkit answers whether a user may request a privileged action. SELinux answers whether a process domain may access a kernel/socket/object type. If SELinux denies nftband netlink access, polkit cannot override it.

The EL Enforcing socket: permission denied is a kernel MAC denial against the daemon's domain. The correct fix is a SELinux policy module granting that domain the netlink/nftables access it needs — which is what NFTBan ships.

Operator commands and checks

SELinux

sestatus
semodule -l | grep nftban
ps -eZ | grep nftband                     # expect ...:nftband_t:...
restorecon -Rv /usr/sbin/nftban /usr/lib/nftban/bin /etc/nftban \
  /var/lib/nftban /var/log/nftban /run/nftban /var/cache/nftban
ausearch -m avc -ts recent | grep -i nftban   # expect none

AppArmor

aa-status
apparmor_parser -r /etc/apparmor.d/usr.lib.nftban.bin.nftband
journalctl -k | grep -i 'apparmor=.*profile="nftband"'
dmesg | grep -i apparmor | grep nftband

Expected behavior

  • SELinux Enforcing: nftband runs in nftband_t, manages NFTBan nftables objects, no socket: permission denied, no NFTBan AVC denials.
  • SELinux Permissive / Disabled: install hooks no-op (guarded by selinuxenabled); behavior unchanged.
  • AppArmor complain: profile loaded, daemon works normally, denials logged but not enforced. A future enforce flip is a separate lane.

Troubleshooting

Symptom Action
SELinux module not loaded semodule -i /usr/share/nftban/selinux/nftban.pp (needs selinuxenabled)
nftband in init_t, not nftband_t run the restorecon line, then systemctl restart nftband
AVC denials persist collect `ausearch -m avc -ts recent
AppArmor profile not loaded apparmor_parser -r /etc/apparmor.d/usr.lib.nftban.bin.nftband; confirm AppArmor enabled
Daemon stuck "activating" under AppArmor confirm the profile flag reads flags=(complain attach_disconnected) and reload

For a bug report, include: distro + version, sestatus/aa-status, ps -eZ | grep nftband, the AVC/AppArmor log lines since the daemon restart, and systemctl status nftband.

Distro coverage

Family Status
Ubuntu 22.04 / 24.04 / 26.04, Debian 12 / 13 (AppArmor complain) validated
AlmaLinux 9, Rocky 9, CentOS Stream 9 (SELinux Enforcing) validated
EL10 family (AlmaLinux 10 / Rocky 10 / CentOS Stream 10, SELinux Enforcing) validation in progress
EL8 (SELinux Enforcing) out of scope — its older policy lacks types the module requires

Related and future work

  • Daemon least-privilege redesign (tracked future hardening).
  • Audit-log uid/gid/pid and whitelist/immutable-file verification (tracked future hardening).
  • Reduce daemon shell-outs, then tighten the SELinux domain + AppArmor profile and consider flipping AppArmor to enforce.

See also

Clone this wiki locally