SELinux & AppArmor Denial Decoder Prompt
Decode SELinux AVC denials and AppArmor DENIED entries, identify the right fix (label, policy module, profile tweak), and avoid disabling LSMs as a shortcut.
- Target user
- Linux sysadmins on RHEL/Fedora (SELinux) and Ubuntu/Debian (AppArmor)
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior Linux security engineer who can read SELinux AVC denial messages and AppArmor `DENIED` entries fluently. You know that the right answer is almost never `setenforce 0` or `aa-disable` — it's a targeted policy fix.
I will provide:
- Which LSM is involved: SELinux (`audit.log` / `journalctl _TRANSPORT=audit`) or AppArmor (`dmesg` / `journalctl -k`)
- The failing application + what operation it was trying to do
- One or more raw denial messages
- The OS / distro / kernel
- Current enforcement mode (`getenforce` / `aa-status`)
Your job:
### For SELinux:
1. **Decode the AVC record line by line**:
- `denied { <op> }` — the denied operation (read, write, open, execute, connect, name_connect, getattr, etc.)
- `scontext=` — source context (the process trying to do it)
- `tcontext=` — target context (the file/socket/etc. being acted on)
- `tclass=` — class of object (file, dir, sock_file, tcp_socket, etc.)
- `pid=` and `comm=` — what process
2. **Identify the right fix in priority order**:
- **Wrong label on a file or directory** (most common) → `restorecon -Rv <path>` or `semanage fcontext -a -t <type> '<path>(/.*)?'` then `restorecon`
- **Service needs a boolean toggled** → `getsebool -a | grep <hint>`, then `setsebool -P <name> on`
- **Custom port** the service binds to is unlabeled → `semanage port -a -t <type> -p tcp <port>`
- **Legitimate but undocumented behavior** → generate a local module with `audit2allow -M mymodule`, review, then load with `semodule -i mymodule.pp`
- **Bug in policy** → file with the distro, run in permissive for that domain only with `semanage permissive -a <type>`
3. **Never recommend** `setenforce 0` as the fix. If unblocking urgently is required, use `semanage permissive -a <domain>` to permissive-mode only the one domain.
### For AppArmor:
1. **Decode the DENIED line**:
- `apparmor="DENIED"` confirms the denial
- `operation="open" / "exec" / "connect"`
- `profile="<name>"` — which profile applied
- `name="<path>"` — what was accessed
- `requested_mask=` vs `denied_mask=` (r, w, x, m for mmap-exec, a for append, l for link)
2. **Identify the right fix**:
- **Path needed in profile** → use `aa-logprof` (interactive) to add the rule
- **Profile too restrictive for a legitimate path** → edit `/etc/apparmor.d/<profile>` and `apparmor_parser -r`
- **App should be unconfined** (rare; usually wrong answer) → `aa-disable <profile>` (reversible) or remove the profile symlink in `/etc/apparmor.d/disable/`
3. **Never recommend** `systemctl stop apparmor` as the fix.
### For both:
4. **Distinguish "real denial that should pass" from "real denial that should NOT pass"**: sometimes the LSM is correctly blocking suspicious behavior. Before adding allow rules, ask if the app *should* be doing this.
5. **Mark DESTRUCTIVE actions** clearly: disabling enforcement, mass-restorecon on `/`, loading audit2allow output without review.
---
LSM: [SELinux / AppArmor]
Distro + version: [e.g., RHEL 9 / Ubuntu 22.04]
Current mode: [`getenforce` output / `aa-status` summary]
Application + operation: [e.g., nginx trying to read /opt/myapp/static]
Denial messages (multiple OK, paste raw):
```
[PASTE — full audit.log AVC lines OR full dmesg/journal apparmor DENIED lines]
```
What you've already tried:
[DESCRIBE]
Run this prompt with AI
Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.
Why this prompt works
SELinux and AppArmor denials look opaque (“permission denied” with no obvious file-permission reason) and the path of least resistance is to disable enforcement — which is almost always the wrong answer in production. This prompt forces a targeted fix: relabel the file, toggle the boolean, or add the specific rule.
How to use it
- Always paste the raw audit/dmesg line. Summarized versions (“nginx can’t read /opt/…”) miss the
tcontextandtclassthat tell you whether the fix is a label, a port, or a boolean. - For SELinux, run
ausearch -m AVC -ts recent | headand paste several lines — the first denial is often masked by cascades. - For AppArmor, paste from
journalctl -k --grep="apparmor=\"DENIED\""ordmesg | grep -i apparmor. - Mention if you’re in
permissive/complainmode — denials still log but operations succeed; useful for finding all the rules you’d need.
Useful commands
SELinux
# Status
getenforce
sestatus
# Find recent AVC denials
sudo ausearch -m AVC -ts recent
sudo ausearch -m AVC -ts today
sudo journalctl _TRANSPORT=audit --since "1 hour ago" | grep AVC
# Friendly summary with hints
sudo sealert -l "*" # if setroubleshoot-server installed
sudo sealert -a /var/log/audit/audit.log
# Inspect labels
ls -Z /path/to/file
ps -eZ | grep <process>
ss -tnlpZ # socket labels
# Common fixes
# 1. Relabel a path to the correct default type
sudo restorecon -Rv /path/to/dir
# 2. Add a custom path with a specific label persistently
sudo semanage fcontext -a -t httpd_sys_content_t '/opt/myapp/static(/.*)?'
sudo restorecon -Rv /opt/myapp/static
# 3. Allow a service on a custom port
sudo semanage port -a -t http_port_t -p tcp 8081
# 4. Toggle a boolean
getsebool -a | grep -i httpd
sudo setsebool -P httpd_can_network_connect on
# 5. Generate a local module from denials (REVIEW THE .te FILE FIRST)
sudo ausearch -m AVC -ts recent | audit2allow -M mymodule
cat mymodule.te # REVIEW
sudo semodule -i mymodule.pp
# 6. Permissive a single domain (better than setenforce 0)
sudo semanage permissive -a httpd_t
# Inspect a domain
sudo seinfo -tnginx_t -x # if setools installed
AppArmor
# Status
sudo aa-status
# Find denials
sudo journalctl -k --since "1 hour ago" | grep -i apparmor
sudo dmesg | grep -i apparmor
# Friendly view + interactive fix
sudo aa-logprof # asks you per denial what to do; updates profile
# Profile management
ls /etc/apparmor.d/
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myapp # reload after edit
sudo aa-enforce /etc/apparmor.d/usr.bin.myapp
sudo aa-complain /etc/apparmor.d/usr.bin.myapp # complain (still log, allow)
sudo aa-disable /etc/apparmor.d/usr.bin.myapp # reversible; symlinks to disable/
# Generate a new profile (start in complain)
sudo aa-genprof /usr/bin/myapp
Decoding a typical SELinux AVC
type=AVC msg=audit(1700000000.123:456): avc: denied { read } for pid=12345 comm="nginx"
name="config.json" dev="dm-0" ino=12345
scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:default_t:s0
tclass=file permissive=0
Reading:
denied { read }— operation blockedcomm="nginx"— what was doing itscontext=...httpd_t— nginx is running ashttpd_t(correct)tcontext=...default_t— the file has thedefault_tlabel (this is the problem — nginx can’t read default_t files)tclass=file— it’s a regular file
Fix: relabel the file to httpd_sys_content_t:
sudo semanage fcontext -a -t httpd_sys_content_t '/opt/myapp/config.json'
sudo restorecon -v /opt/myapp/config.json
Decoding a typical AppArmor DENIED
audit: type=1400 audit(1700000000.123:456): apparmor="DENIED"
operation="open" profile="/usr/sbin/nginx"
name="/opt/myapp/config.json" pid=12345 comm="nginx"
requested_mask="r" denied_mask="r" fsuid=33 ouid=33
Reading:
apparmor="DENIED"— confirmedoperation="open"+requested_mask="r"— read openprofile="/usr/sbin/nginx"— the nginx profile appliedname="/opt/myapp/config.json"— the path it tried to read
Fix: add /opt/myapp/** r, to the nginx profile:
sudo nano /etc/apparmor.d/usr.sbin.nginx
# add inside the profile braces:
/opt/myapp/** r,
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
Common findings this catches
- App reads a file copied with
cpfrom/tmpto/var/www/html—cppreserves the source label by default. Usecp --no-preserve=contextorrestorecon -vafter. - Service can’t bind to port 8081 — SELinux port label only allows 80 and 443 for
http_port_t. Add 8081 viasemanage port. - Container runtime denials on a new mount — container needs SELinux context
container_file_t. Use:Zor:zmount option in Docker/Podman. - AppArmor breaks after binary upgrade — profile path no longer matches; profile needs regenerating with
aa-genprof. - SELinux booleans you forgot existed:
httpd_can_network_connect,samba_export_all_rw,nfs_export_all_rw. Toggling beats relabeling for these.
When the LSM is correctly blocking
Before adding allow rules, ask:
- Should
nginxbe reading files in/home/<user>/? Probably not. - Should
mysqldbe writing to/etc/? No. - Should an SSH daemon be executing files in
/tmp? Suspicious.
A denial is sometimes the LSM doing its job. Investigate before you allow.
When to escalate
- A flurry of denials right after a CVE-fix package update — possible policy regression upstream; check distro bug tracker.
- Repeated AVCs from a domain you don’t recognize — possible compromise; engage security.
- Recommendations to load large auto-generated audit2allow modules — review module by module with a second engineer.
Related prompts
-
Linux Server Troubleshooting Prompt
Help diagnose CPU, memory, disk, network, and service issues on Ubuntu or RHEL servers from raw command output.
-
Linux Server Hardening Prompt
Walk an AI through a CIS-style hardening review of a Linux server — services, users, SSH, kernel parameters, file permissions — with safe, ordered remediation.
-
Sudoers & Systemd Services Review Prompt
AI review of /etc/sudoers (and /etc/sudoers.d/*) and systemd service unit files for privilege escalation, unsafe defaults, and hardening gaps.
-
Linux fapolicyd Application Allowlisting Prompt
Design, test, and roll out fapolicyd application allowlisting so only trusted binaries and scripts execute, without locking yourself out or breaking legitimate app updates, package installs, and interpreters.
More Linux Admins prompts & error guides
Browse every Linux Admins prompt and troubleshooting guide in one place.
Reading prompts? Get all 500 in one free PDF
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.