Learn SELinux - audit2allow & Local Policy Generation
Episode 7 of 23

Learn SELinux - audit2allow & Local Policy Generation

Mastering the SELinux denial debugging workflow: reading deny logs with ausearch, translating them into policy rules with audit2allow, creating and installing local policy modules, plus best practices so you don't get trapped into generating policies blindly.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 6, you learned to use booleans to resolve denials without touching the policy. But not every denial has a boolean. Sometimes your application genuinely needs rights that the built-in policy doesn't allow — for example reading log files in a special format, or accessing a specific device.

For cases like this, SELinux provides a very powerful yet dangerous tool: audit2allow. This tool reads the audit log and translates denials into ready-to-use policy rules. Interestingly, this tool can also make you forget how to "read" denials — which is why this episode focuses on the correct workflow, not just running the generation engine.

The Correct Denial Debugging Workflow

Before touching audit2allow, make sure the debugging process follows a disciplined order. Skipping a step makes you analyze the wrong data:

  1. Make sure enforcing mode is active — denials only produce logs when enforcing, not just permissive.
  2. Reproduce the problem — denials only appear if the application actually tries the blocked operation.
  3. Find the denial in the logs with ausearch or grep.
  4. Read and understand the denial — its subject, object, permission, and class.
  5. Choose the narrowest solution: booleans first, then file context, then a policy module.
  6. Generate, install, and test the module, then verify the denial is truly gone.

Important

Step 5 is what separates an engineer who understands SELinux from one who's just "shooting". If a boolean already allows that behavior, don't generate a module. A policy module is the last resort, not the first — every allow rule you add expands one domain's attack surface.

Reading Denials with ausearch

The audit log is the source of truth. The first command you must master:

Search for the latest AVC denials
sudo ausearch -m AVC -ts recent

The result looks roughly like this:

LinuxExample denial record
type=AVC msg=audit(1710000000.123:456): avc:  denied  { read } for  pid=1234
    comm="httpd" name="index.html" dev="sda1" ino=5678
    scontext=system_u:system_r:httpd_t:s0
    tcontext=system_u:object_r:var_log_t:s0 tclass=file permissive=0

This is SELinux's "police report", and there are five elements you must read:

  • scontext is the subject — the domain of the process trying to access, here httpd_t.
  • tcontext is the object — the label of the file or resource being targeted, here var_log_t.
  • tclass is the object typefile, dir, tcp_socket, and so on.
  • { read } is the permission being requested.
  • permissive=0 confirms this access was truly blocked, not just recorded.

To filter denials belonging to one process, add the -c flag with the command name, for example ausearch -m AVC -c httpd. Get used to filtering — a production server's audit log can contain thousands of denials irrelevant to your problem.

audit2allow: The Denial Translator

After understanding the denial, it's time to ask for rule suggestions. Run audit2allow -a to read the entire audit log and generate proposed rules:

View the suggested rules
sudo audit2allow -a

If your log only contains the denial above, the output will look like this:

LinuxRule suggestions from audit2allow
#============= httpd_t ==============
allow httpd_t var_log_t:file read;
allow httpd_t var_log_t:file open;

Notice: audit2allow only translates denials into one-to-one allow rules. It doesn't know your business context. For this machine, reading the var_log_t file is "needed" — but from a security standpoint, allowing httpd to read all system log files is a bad idea. This is where your understanding from step 4 determines the quality of the output.

Creating and Installing a Policy Module

Once you're satisfied with the proposed rules, generate the module with the -M flag:

Generate a local policy module
sudo audit2allow -a -M myhttpd

This command produces two files: myhttpd.te (the policy source in Type Enforcement format) and myhttpd.pp (the compiled policy package). Here's the contents of myhttpd.te:

LinuxContents of myhttpd.te
module myhttpd 1.0;
 
require {
    type httpd_t;
    type var_log_t;
    class file { read open };
}
 
allow httpd_t var_log_t:file { read open };

Install the module into the active policy with semodule -i, then verify and test:

Install, check, and test the module
sudo semodule -i myhttpd.pp
sudo semodule -l | grep myhttpd

If it turns out it's not needed, remove it with semodule -r myhttpd. Removing is easier than editing — so don't hesitate to try and clean up.

Best Practice: Don't Generate Blindly

Local policy modules are a legitimate tool, but misuse can damage system security. Follow these principles:

  • Understand every rule before installing. If you can't explain one line of allow, don't install it.
  • Prefer the narrower solution. Is there a fitting boolean? Use the boolean. Is it a file label problem? Label with semanage fcontext, not by giving the domain access to an entire object type.
  • Beware of overly generic object types. In the example above, var_log_t is used by all system log files. Allowing httpd_t to read var_log_t means also allowing it to read the audit log. It's safer to label the application's dedicated log directory with a new type, then allow access to only that type.
  • Avoid excessive dontaudit. The -D flag produces dontaudit rules to suppress noise. Too much dontaudit actually hides the signs of real attacks — denials that could have been an indicator of compromise become invisible.
  • Test on a separate host first, then propagate the same module with config management for consistency.

Warning

Never run audit2allow -a -M on a production server that's been running for a long time without filtering first. An audit log containing months of denials will produce a giant module that allows all sorts of domain and object combinations — the exact opposite of what SELinux wants. Always filter with ausearch -c or -ua so only denials from one subject are generated.

Closing

In this episode 7, you've held the correct denial debugging workflow: reading AVC records with ausearch and understanding its five important elements, asking for rule suggestions with audit2allow -a, creating a module with audit2allow -M, installing with semodule -i, and the principles for not generating policies blindly.

The key takeaways:

  • The audit log is the source of truth; read before generating.
  • Choose the narrowest solution: booleans first, fcontext second, module last.
  • Beware of overly generic object types and excessive dontaudit.

So far we've talked about processes and domains — not yet about the humans logging into the system. In episode 8, we'll discuss Users, Roles & MLS/MCS: how SELinux users are mapped from Linux users, the role of role in restricting domain transitions, and two classification mechanisms — Multi-Level Security for hierarchical data and Multi-Category Security for isolation between workloads.

Learn SELinux - audit2allow & Local Policy Generation | Learn SELinux