SELinux Policy Deep Dive: semanage, audit2allow, and Custom Modules

When labels and booleans are not enough, you need local SELinux policy controls that stay maintainable. This post shows how to use semanage for persistent mappings, when to generate modules with audit2allow, and how to reason about minimal custom policy rules.

Table of Contents

This final post in the series focuses on advanced workflows for durable SELinux administration.

Persistent Policy Controls

Custom Module Workflows

semanage essentials for daily policy management

semanage manages persistent SELinux policy customizations without editing base policy packages. It is the preferred interface for local administration changes that should survive relabel and reboot.

Common subcommands worth memorizing:

  • semanage fcontext manages persistent file context mappings
  • semanage port maps service types to network ports
  • semanage boolean inspects and sets boolean policy state
  • semanage login maps Linux users to SELinux user identities

Useful package note for RHEL-family systems:

sudo dnf install -y policycoreutils-python-utils selinux-policy-devel

setroubleshoot-server is optional but helpful for guided diagnostics.

Persistent file labeling with fcontext plus restorecon

Use this sequence whenever application data lives outside default paths.

sudo semanage fcontext -a -t httpd_sys_content_t '/srv/myapp(/.*)?'
sudo restorecon -Rv /srv/myapp
ls -Zd /srv/myapp /srv/myapp/index.html

semanage fcontext defines the expected mapping and restorecon enforces it on filesystem objects.

To review existing custom mappings:

sudo semanage fcontext -l | grep /srv/myapp

Port labeling for custom service ports

If your service moves to a non-default port, policy may deny bind operations until the port is labeled for the expected type.

sudo semanage port -l | grep http_port_t
sudo semanage port -a -t http_port_t -p tcp 8443
sudo semanage port -l | grep 8443

If a mapping already exists, replace -a with -m for modify.

audit2allow workflow with required review

audit2allow can generate local allow rules from AVC evidence, but generated output is only a draft and must be reviewed before installation.

Safe workflow:

sudo ausearch -m avc -ts recent > /tmp/myapp-avc.log
cat /tmp/myapp-avc.log | audit2allow -w
cat /tmp/myapp-avc.log | audit2allow -a
cat /tmp/myapp-avc.log | audit2allow -M myapp_local

What to review before install:

  • Confirm AVC records map to expected application behavior
  • Remove rules that grant access beyond the intended path or class
  • Prefer label and boolean fixes if they solve the issue cleanly
  • Keep module scope narrow to the smallest required permissions

Install and manage local policy modules

After review, install the generated package and verify module state.

sudo semodule -i myapp_local.pp
sudo semodule -l | grep myapp_local
sudo semodule -r myapp_local
sudo semodule -R
  • -i installs a compiled policy package
  • -r removes a local module by name
  • -R rebuilds and reloads active policy module set

Minimal authoring example with a .te file

Generated rules are useful starting points, but writing a minimal module directly is sometimes cleaner.

Example .te file:

module myapp_read_config 1.0;

require {
    type myapp_t;
    type myapp_conf_t;
    class file { getattr open read };
}

allow myapp_t myapp_conf_t:file { getattr open read };

Compile and package with classic toolchain:

checkmodule -M -m -o myapp_read_config.mod myapp_read_config.te
semodule_package -o myapp_read_config.pp -m myapp_read_config.mod
sudo semodule -i myapp_read_config.pp

Authoring guidance:

  • Start with one narrow allow rule and test behavior
  • Add only the missing permission from current AVC evidence
  • Re-run the failing workflow and confirm no unrelated access is opened

Decision guide for choosing the right fix

Use this sequence to avoid unnecessary custom modules.

Is label wrong -> fix with restorecon or semanage fcontext
Is expected behavior behind boolean -> enable specific boolean
Is port type mismatch -> map with semanage port
Still denied for valid behavior -> create minimal local module
Behavior looks unsafe or surprising -> escalate and review app design

This approach keeps policy predictable and easier to audit over time.