Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for DevOps Security & Hardening By James Joyner IV · · 9 min read

Security Error Guide: 'fapolicyd: deny ... Operation not permitted' Blocked Execution

Fix fapolicyd execution denials: diagnose 'Operation not permitted' on binaries and scripts, read the fapolicyd log, trust files correctly, and add scoped allow rules safely.

  • #security-hardening
  • #troubleshooting
  • #errors
  • #fapolicyd

Exact Error Message

With the file-access policy daemon enforcing allowlisting, an attempt to run an untrusted binary fails with a generic permission error, while fapolicyd records the real reason:

$ /opt/app/bin/worker
bash: /opt/app/bin/worker: Operation not permitted

The daemon log makes the decision explicit:

fapolicyd: rule=9 dec=deny_audit perm=execute auid=1000 pid=4821 exe=/usr/bin/bash : path=/opt/app/bin/worker ftype=application/x-executable trust=0

What the Error Means

fapolicyd is an application allowlisting daemon: it intercepts file-access (especially execute and open) operations and permits them only if the target is trusted or matches an allow rule. trust=0 and dec=deny mean the binary or script is not in the trust database and no rule allows it, so the kernel returns EPERM (“Operation not permitted”) to the caller. The shell’s generic message hides the policy decision, which lives only in the fapolicyd log.

This is allowlisting working as intended — it blocks anything that was not approved, which is exactly the point on a hardened host. Engineers hit it after installing software outside the package manager, deploying a self-built binary, or running a script from a path the trust database does not cover. The fix is to add the legitimate file to the trust source or write a scoped allow rule, never to disable the daemon.

Common Causes

  • Binary installed outside the package manager. fapolicyd’s default trust comes from the RPM/dpkg database; a manually copied or make installed binary has trust=0.
  • Updated package not yet re-trusted. A binary was replaced (new hash) but the trust database still holds the old hash, so the new file is untrusted.
  • Script interpreter path not trusted. A Python or shell script under a non-standard path is denied even though the interpreter is trusted.
  • Custom trust file missing. A /etc/fapolicyd/trust.d/ entry for the app was never created or never reloaded.
  • Mutable path written at runtime. The app writes then executes a file from a writable directory, which allowlisting deliberately blocks.
  • Permissive-to-enforcing flip. The daemon was running in permissive mode during testing; switching to enforcing surfaces every previously-logged denial.

How to Reproduce the Error

On a test host with fapolicyd enforcing, place an untrusted executable and run it:

sudo install -m 0755 /bin/true /opt/app/bin/worker
/opt/app/bin/worker
bash: /opt/app/bin/worker: Operation not permitted

The denial appears in the daemon log with trust=0, confirming the file is unknown to the allowlist:

fapolicyd: rule=9 dec=deny_audit perm=execute ... path=/opt/app/bin/worker ... trust=0

Diagnostic Commands

These read-only commands show the decision, the trust state, and the active rules.

# Read the fapolicyd decision log (the real reason for EPERM)
sudo tail -n 20 /var/log/fapolicyd-access.log 2>/dev/null
sudo journalctl -u fapolicyd --since '15 min ago' | grep -i 'dec=deny'

# Is the file known to the trust database?
sudo fapolicyd-cli --list-trust 2>/dev/null | grep -F /opt/app/bin/worker

# Show the loaded rules and their order
sudo fapolicyd-cli --list 2>/dev/null

# Confirm enforcing vs permissive and daemon health
sudo systemctl status fapolicyd --no-pager
grep -E 'permissive' /etc/fapolicyd/fapolicyd.conf

# What does the package database think owns the file? (trust source)
rpm -qf /opt/app/bin/worker 2>/dev/null || dpkg -S /opt/app/bin/worker 2>/dev/null

# Inspect the file type fapolicyd evaluated
file /opt/app/bin/worker

The access log line with trust=0/dec=deny plus fapolicyd-cli --list-trust together tell you whether the fix is to trust the file or to add an allow rule.

Step-by-Step Resolution

  1. Read the decision. Find the dec=deny line for the file in /var/log/fapolicyd-access.log. Note trust, perm, and path. trust=0 means the file is simply unknown to the allowlist.

  2. Confirm the file is legitimate. Verify its source, owner, and ideally its hash against the vendor before trusting it. Allowlisting is only as good as your verification of what you add.

  3. Add the file to the trust database through a managed trust file, then reload:

    sudo fapolicyd-cli --file add /opt/app/bin/worker
    sudo fapolicyd-cli --update

    This records the file’s path and hash in the trust source so future executes are permitted while the hash matches.

  4. Prefer package-managed installs. If you can install the software via RPM/dpkg, do so — its files are trusted automatically and re-trusted on updates, avoiding manual maintenance.

  5. For scripts or directories, write a scoped allow rule in /etc/fapolicyd/rules.d/ (for example permit execute for a specific path and subject), then sudo fapolicyd-cli --update. Keep rules narrow; broad allows defeat the control.

  6. Re-test the command and confirm the access log now shows dec=allow. If you were validating in permissive mode, only flip to enforcing after all legitimate paths are trusted.

Prevention and Best Practices

  • Install software through the package manager wherever possible so files are trusted and re-trusted automatically.
  • Manage custom trust entries and rules as code under /etc/fapolicyd/trust.d/ and /etc/fapolicyd/rules.d/, with fapolicyd-cli --update in your deployment flow.
  • Verify a file’s source and hash before adding it to trust; the allowlist is only as trustworthy as what you approve.
  • Test new deployments in permissive mode first, collect the denials, trust the legitimate paths, then enforce.
  • Avoid execute-from-writable-directory patterns; they are exactly what allowlisting is designed to block.
  • Operation not permitted from SELinux/AppArmor — a different LSM denying the same operation; check which subsystem logged it.
  • Permission denied (EACCES) — ordinary Unix permissions, not allowlisting.
  • Exec format error — a corrupt or wrong-architecture binary, unrelated to trust.
  • avc: denied { execute } — the SELinux equivalent, covered in the security hardening guides.

Frequently Asked Questions

Why does the shell say “Operation not permitted” with no detail? The kernel returns EPERM to the caller; the policy reason (trust=0, dec=deny) is only in the fapolicyd access log.

Can I just stop fapolicyd to get my app running? That removes application allowlisting host-wide. Trust the specific file or add a scoped rule instead.

I added the file to trust but it is still denied. The trust database stores the file’s hash. If the binary changed after you trusted it, re-run fapolicyd-cli --file add and --update to record the new hash.

Why does a package update suddenly get denied? The new file has a different hash than the trusted one. Package-managed installs re-trust automatically; manual installs need a fresh trust update.

How do I find every denial before enforcing? Run in permissive mode, run your workloads, then read /var/log/fapolicyd-access.log for all dec=deny lines and trust the legitimate paths.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

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.