A service on a RHEL host is failing and the audit log shows an SELinux AVC denial. Walk through how you decide between correcting a label, flipping a policy boolean, and generating a custom policy module — and say what it could mean when the denial you expect never appears in the log at all.
answer
- read scontext, tcontext, tclass, permission
- label first, boolean second, module last
- -P or it dies at reboot
- audit2allow drafts, you review
- dontaudit hides real denials
basics
~20 sRead the denial's source context, target context, object class and permission. A wrong target label means relabel it. A supported optional behaviour means set the boolean that enables it. Only a genuinely new access justifies a reviewed custom module. Missing denials usually mean a dontaudit rule is suppressing them.
solid answer
~60 sStart with the denial record itself: `scontext` says which domain asked, `tcontext` says what it asked about, `tclass` names the object class and the braces name the permission. Then work down a fixed ladder. First ask whether the *target* is mislabelled — most real denials are a file or directory in the wrong place or moved rather than copied, and the answer is `restorecon`. Second, ask whether the policy already anticipates this behaviour as optional: `getsebool -a` and `semanage boolean -l` list the tunables, and a supported case is enabled with `setsebool -P`, which persists across reboots. Only if neither applies do you write policy — collect the full denial set in a permissive domain, run `audit2allow`, and *read what it generates* before building and installing a module, because `audit2allow` will happily grant far more than intended. And if the denial you expect is absent, suspect a `dontaudit` rule: `semodule -DB` rebuilds the policy with those suppressions off so the hidden denials appear, and `semodule -B` restores normal behaviour afterwards.
code
bash · 16 lines# 1. what exactly was denied?
ausearch -m AVC -ts recent
ausearch -m AVC -ts recent | audit2why
# 2. is a supported tunable enough?
semanage boolean -l | grep httpd
setsebool -P httpd_can_network_connect on
# 3. only then, a reviewed local module
ausearch -m AVC -ts recent | audit2allow -M myapp_local
cat myapp_local.te
semodule -i myapp_local.pp
# expected denial missing? unmask the dontaudit rules
semodule -DB
semodule -Bgo deeper
Know where denials are recorded and that the first question is whether the file is labelled correctly for where it sits, not whether SELinux should be turned off.
Explain the fields of a denial record and the difference between a labelling fix, a policy boolean, and a custom module, including why setsebool needs -P to persist.
Demonstrate the ordered triage under production pressure: narrow the change to the smallest thing that is wrong, collect the full denial set in a permissive domain before writing policy, and review generated rules rather than installing them blind.
Own the policy-exception process: who may install a local module, how it is reviewed and version-controlled, and how the organisation avoids the drift where every host carries a different set of hand-installed grants.
## Read the record before you touch anything An AVC denial is a complete statement of the question the kernel asked and the answer it gave. The fields that matter: - `avc: denied { read }` — the permission requested, on the object class named by `tclass`. - `scontext=...:httpd_t:s0` — the subject: which domain the calling process was in. - `tcontext=...:user_home_t:s0` — the object's label. - `tclass=file` — the object class: file, dir, tcp_socket, unix_stream_socket, capability, and so on. Those four values usually diagnose the problem on their own. `httpd_t` denied `read` on a `user_home_t` file is a labelling problem. `httpd_t` denied `name_connect` on a database port type is a boolean or a port-label problem. `httpd_t` denied a capability is a much bigger conversation about what the application thinks it needs. `ausearch -m AVC -ts recent` pulls the recent records, and `audit2why` explains a denial in prose, including telling you when a boolean would have permitted it — which is often the fastest route from record to fix. ## The ladder, in order of preference **1. Is the target labelled correctly?** This is the most common cause by a wide margin, because labels ride on inodes and data gets moved, restored from backup, or written into a path the policy has no rule for. If the type in `tcontext` is not what that path should have, fix the label — with a `semanage fcontext` rule where the path is non-standard — and the denial disappears without any policy change at all. Nothing has been weakened. **2. Is there a boolean for this?** SELinux policy ships with named tunables covering behaviours that are legitimate for some deployments and not others: whether the web server may make outbound network connections, whether it may write to particular content, whether a daemon may read user home directories. `getsebool -a` lists them all with their current values; `semanage boolean -l` adds a description column. Enable with `setsebool -P <boolean> on` — the `-P` is what makes it survive a reboot, and forgetting it produces the classic "it worked until the machine restarted" incident. A boolean is a change the policy author anticipated and scoped, which makes it far safer than a rule you write yourself. **3. Does the application genuinely need an access the policy does not model?** Only now do you write policy. The method matters: ```bash semanage permissive -a myapp_t # confine nothing for this one domain # exercise the application fully ausearch -m AVC -ts recent | audit2allow -M myapp_local cat myapp_local.te # READ THIS semodule -i myapp_local.pp semanage permissive -d myapp_t ``` Two things make this professional rather than reckless. First, running the domain permissive means the application does not die at its first denial, so you collect the *whole* set of accesses rather than fixing them one reboot at a time. Second, `audit2allow` output is a draft, not an answer: it converts observed denials into allow rules mechanically, and if the application misbehaved during collection — or if an attacker's probe was in the log — those accesses get blessed too. Rules granting broad permissions on `capability`, or write access to a system type, are the ones to challenge. ## When the denial is not there Some denials are deliberately silenced. Policy contains `dontaudit` rules for accesses that programs routinely attempt and do not need, so the logs are not drowned in noise. The consequence is that a real failure can produce no visible denial at all. Rebuild the policy with suppressions disabled to see them: ```bash semodule -DB # disable dontaudit rules and rebuild # reproduce the failure, read the log semodule -B # rebuild normally again ``` Always put it back. The other explanations for a missing record are more mundane and worth ruling out first: `auditd` not running (denials then land in the kernel ring buffer and the journal instead of `/var/log/audit/audit.log`), rate limiting under a flood of identical records, or the AVC cache — a repeated identical access can be answered from cache without generating another log line, which is why counting occurrences in the log understates how often it happened. ## The two anti-patterns Switching the machine to permissive, or disabling SELinux, converts a specific containment failure into a general loss of containment, and it usually gets forgotten until an audit finds it. Blanket-installing an unread `audit2allow` module does the same thing more quietly, because the resulting grant is invisible unless someone reads the module. In both cases the honest interview answer is that the fix must be as narrow as the problem: relabel one path, or flip one boolean, or install a module whose rules you can defend line by line.
- Why is enabling a boolean considered safer than installing a module generated by audit2allow?A boolean is a tunable the policy author defined deliberately: its scope is bounded, documented and reviewed as part of the policy. An audit2allow module is a mechanical translation of whatever denials happened to be in the log into allow rules, with no notion of intent — so it can grant capabilities or write access to system types that nobody asked for. Booleans are a supported switch; generated modules are code you now own.
- What goes wrong if you run setsebool without -P?The change applies to the running system only. It is not written to the persistent boolean store, so the next reboot silently reverts it and the service fails again — usually weeks later, during an unrelated maintenance window, with nobody connecting the two events. Always use `-P` for a fix, and reserve the non-persistent form for a quick test you intend to undo.
- Why might the number of AVC records in the log understate how often an access was actually attempted?SELinux decisions are cached in the access vector cache, so a repeated identical access can be answered from cache without producing another audit record. Audit rate limiting can drop records under a flood as well. Treat AVC records as evidence that something happened, not as a reliable count of how many times.
- A denial names tclass=capability rather than a file. How does that change your reading?It means the application asked the kernel for a privileged operation — not for access to an object you can relabel. No amount of restorecon will help. The right response is to ask why the process needs that privilege at all: often it is a misconfiguration such as binding a low port or trying to change ownership, and the correct fix is in the application's configuration rather than in the policy.
saying these in an interview costs you the question
- Just run audit2allow and install whatever it prints
- setenforce 0 is a legitimate fix for a denial
- No denial in the log proves SELinux is not involved
- setsebool without -P is a permanent change
- Every denial means the policy is wrong and needs a new module