fapolicyd på Linux i 2026: Application allowlisting med trust database og fanotify

Praktisk guide til fapolicyd på RHEL 10 og Fedora: trust database, DNF-plugin, custom rules, fejlfinding med debug-deny og produktions-checklist for STIG og CIS compliance.

fapolicyd på RHEL 10: Guide (2026)

Opdateret: 7. august 2026

fapolicyd er en application-allowlisting-daemon, der bruger kernens fanotify-API til at blokere eksekvering af enhver binær, som ikke findes i en trust database. På RHEL 10, Fedora 40+ og Oracle Linux 10 gør fapolicyd én simpel ting: hvis en fil ikke er installeret af DNF/RPM eller eksplicit tilladt, kan den ikke køre. I denne guide viser jeg, hvordan du opsætter fapolicyd i produktion, tilføjer custom trust-poster for tredjepartsapps, fejlfinder nægtelser med --debug-deny, og kombinerer daemonen med SELinux for at opfylde DISA STIG. Ærligt talt: det er en af de få hærdningstiltag, der giver reel værdi uden at koste udviklerne søvn, hvis man opsætter det rigtigt.

  • fapolicyd bruger kernens fanotify-hook (Linux ≥ 5.0, med fuld FAN_OPEN_EXEC_PERM-support fra 5.9) til at afvise ukendte binærer med EACCES.
  • RPM-installerede pakker er automatisk trusted via en DNF-plugin, der lytter til alle transaktioner og opdaterer /var/lib/fapolicyd/.
  • fapolicyd og SELinux komplementerer hinanden. SELinux svarer på "hvad må processen gøre?", fapolicyd svarer på "må denne binær overhovedet køre?".
  • Standardpolitikken known-libs er produktionsegnet fra dag ét. Restrictive egner sig kun til dedikerede kiosk- eller compliance-workloads.
  • Fejlfinding sker i to trin: fapolicyd --permissive --debug-deny først, derefter fapolicyd-cli --check-trustdb og custom rules i /etc/fapolicyd/rules.d/.
  • fapolicyd blokerer ikke processer, der kører som root. Du skal stadig have SELinux, systemd-sandboxing og sudo-hærdning på plads for et fuldt kill chain-forsvar.

Hvad er fapolicyd, og hvordan fungerer det med fanotify?

fapolicyd (File Access Policy Daemon) er en user-space daemon, der abonnerer på kerne-events via fanotify(7) og træffer beslutningen "må denne fil eksekveres?" før kernen leverer siden til execve(). Når en proces forsøger at åbne eller køre en fil, sender kernen en FAN_OPEN_EXEC_PERM-notifikation til daemonen, som svarer med enten FAN_ALLOW eller FAN_DENY. Ved deny returnerer kernen EACCES 'Permission denied' til den kaldende proces.

Denne arkitektur er interessant af tre grunde. For det første kører policy-logikken i user-space, hvilket betyder at jeg kan opdatere rules uden en genstart eller en kernel-modul reload. For det andet er beslutningen synkron: kernen blokerer tråden, indtil daemonen svarer, så der er ingen race condition, hvor en binær når at eksekvere før policy tjekkes. For det tredje bygger fapolicyd på det samme fanotify-lag, som antivirus-vendorer og filsystem-scannere har brugt i årevis. Det betyder, at koden er velafprøvet og har fået mange kerne-fixes siden Linux 5.0 (relevante CVE'er inkluderer CVE-2022-1998 og CVE-2023-52447, begge lukket i mainline).

Den underliggende idé er application allowlisting: i stedet for at forsøge at identificere malware ved signaturer, definerer du hvilke binærer, der er tilladte, og alt andet nægtes. Det er et implicit deny-domæne, hvilket gør det til et af de få tekniske kontroller, der reelt matcher NIST 800-53 AC-3 "Access Enforcement" for eksekverbar kode uden manuel labeling af hver enkelt binær.

Hvad er forskellen mellem fapolicyd og SELinux?

fapolicyd og SELinux besvarer to forskellige spørgsmål og bør køre samtidigt. SELinux svarer på "hvad må denne kørende proces gøre?" ved hjælp af Mandatory Access Control (MAC) baseret på security-contexts (labels) på processer og filer. fapolicyd svarer på "må denne fil overhovedet eksekveres?" ved at slå filen op i en trust database. En binær kan være SELinux-labelet korrekt og stadig blive nægtet af fapolicyd, fordi den ikke kom fra en trusted kilde.

DimensionfapolicydSELinux
Primære spørgsmålMå denne fil køre?Hvad må denne proces gøre?
ModelApplication allowlisting (trust-baseret)Mandatory Access Control (label-baseret)
Kernemekanismefanotify (user-space daemon)LSM-hooks (in-kernel)
InstallationskildebevidsthedJa, sporer DNF/RPM-transaktionerNej, behandler alle filer ens
IntegritetstjekSHA-256-hash pr. filIkke indbygget (bruges med IMA)
Blokerer root?Nej (regulær bruger-eksekvering)Ja (også unconfined_r med targeted policy justeret)
Standardpolitik-størrelse~30 rulesTusindvis af rules
Fejlfindingsværktøjfapolicyd --debug-denyausearch -m avc + audit2allow

I praksis giver kombinationen en reel defense-in-depth. En angriber, der kompromitterer en webserver, skal både forbi SELinux (som blokerer httpd_t fra at læse /etc/shadow) og forbi fapolicyd (som blokerer eksekvering af en drop'et payload i /tmp/). Hvis du kun har SELinux og en angriber vinder en labeling-workaround via en 0-day, standser fapolicyd stadig payload'et. Hvis du kun har fapolicyd, og angriberen bruger en trusted binær som bash til living-off-the-land, fanger SELinux det. En sammenlignelig tilgang, process-level konfinering på tværs af LSM'er, dækkes i vores guide til AppArmor-profiler og håndhævelse.

Installation og standardpolitikker på RHEL 10 og Fedora

fapolicyd er tilgængelig i AppStream på RHEL 8, 9 og 10, og i standard-repo på Fedora, Rocky, AlmaLinux og Oracle Linux. Fra RHEL 10 (GA januar 2026) er DNF-pluginet split ud til fapolicyd-dnf-plugin, så du skal installere begge pakker for at få automatisk trust-registrering ved package-transaktioner.

# RHEL 10 / Rocky 10 / AlmaLinux 10
sudo dnf install -y fapolicyd fapolicyd-dnf-plugin
sudo systemctl enable --now fapolicyd

# Verificér at daemonen kører og lytter på fanotify
sudo systemctl status fapolicyd
sudo journalctl -u fapolicyd -n 20 --no-pager

# Fedora 40+
sudo dnf install -y fapolicyd
sudo systemctl enable --now fapolicyd

Ved installation deployes to standardpolitikker i /etc/fapolicyd/rules.d/. Du vælger mellem dem ved at ændre selinux-lignende symlink via fagenrules. I praksis bør du dog starte med known-libs:

  • known-libs (standard). Tillader kun eksekvering af trusted ELF-binærer og trusted shared libraries. Uploadede scripts og fremmede binærer blokeres, men systemet kører normalt. Anbefales til alle produktions-workloads.
  • restrictive. Blokerer eksekvering via runtime-linkeren og tillader kun trusted ELF- og Python-programmer. God til compliance-workloads (STIG-appendix, PCI-DSS single-purpose hosts), men vil bryde de fleste udviklingsmiljøer.
# Kompilér rules og reload daemon
sudo fagenrules --load
sudo systemctl restart fapolicyd

# Verificér aktiv policy og rule-count
sudo fapolicyd-cli --list | head -30
sudo fapolicyd-cli --check-config

Trust database, DNF-plugin og SHA-256-integritet

Kernen i fapolicyd er trust database'en, en on-disk database over hver fil, daemonen anser for tillid-værdig, sammen med filens SHA-256-hash. Databasen lever i /var/lib/fapolicyd/ og fyldes fra tre kilder: RPM database (initial population), DNF-pluginet (løbende opdateringer ved dnf install/upgrade), og manuelt tilføjede filer via fapolicyd-cli --file add.

SHA-256-hashen betyder, at hvis en angriber overskriver en trusted binær (fx /usr/bin/python3) med en trojansk version, vil fapolicyd nægte eksekvering, fordi den on-disk hash ikke længere matcher databasen. Det er en simpel, men effektiv integritetsmekanisme, der ligner Linux IMA i formål, men er væsentligt lettere at drifte i praksis.

# Tjek trust database for hash-mismatches efter en fs-ændring
sudo fapolicyd-cli --check-trustdb

# Se hvad daemonen ved om en specifik fil
sudo fapolicyd-cli --file check /usr/bin/curl

# Optæl trust-poster (typisk 50-100k på et fuldt RHEL 10-system)
sudo fapolicyd-cli --dump-db | wc -l

DNF-pluginet registrerer sig som en post-transaction-hook i /etc/dnf/plugins/fapolicyd.conf. Hver gang du kører dnf install, sender pluginet en notifikation til daemonen om at genopfriske trust-posterne for de berørte filer. Det er derfor, du bør genstarte fapolicyd før du kører store dnf-transaktioner. Hvis daemonen er nede, tabes notifikationerne, og du får en periode, hvor nyinstallerede binærer nægtes, indtil du kører fapolicyd-cli --update. Jeg ramte selv den fælde første gang, jeg rullede fapolicyd ud på en Foreman-styret flåde, og det tog et par timer at forstå, hvorfor Zabbix-agenten pludselig var død på 40 hosts efter en simpel patch-cyklus.

Sådan tilføjer du applikationer til fapolicyd allowlist

De fleste "hvorfor kan jeg ikke køre denne binær?"-scenarier løses ved at tilføje trust-poster, ikke ved at skrive custom rules. Trust-poster er den simpleste form for allowlisting: fapolicyd beregner filens SHA-256-hash, gemmer den i databasen, og næste gang binæren åbnes, får den FAN_ALLOW. Der er tre måder at gøre det på.

1. Enkelte filer via fapolicyd-cli

# Tilføj en enkelt binær (fx en tredjepartsagent)
sudo fapolicyd-cli --file add /opt/nessus/sbin/nessus-service

# Tilføj rekursivt en hel mappe
sudo fapolicyd-cli --file add /opt/datadog-agent/

# Aktivér ændringerne
sudo fapolicyd-cli --update

2. Trust-fil pr. leverandør (anbefalet til CI)

Til produktion foretrækker jeg at holde tredjepartstrust adskilt fra RPM-trust ved at oprette en fil i /etc/fapolicyd/trust.d/. Filerne læses automatisk ved daemon-start og kan versionsstyres i Git.

# /etc/fapolicyd/trust.d/50-datadog.trust
/opt/datadog-agent/bin/agent 45217920 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
/opt/datadog-agent/embedded/bin/python3 8912384 f7a9c8b6d4e2a1c3b5d7e9f1a3c5b7d9e1f3a5c7b9d1e3f5a7c9b1d3e5f7a9c1
# Format: <absolut_sti> <størrelse_i_bytes> <sha256_hash>
# Generér korrekte hashes med
sha256sum /opt/datadog-agent/bin/agent
stat -c '%s' /opt/datadog-agent/bin/agent

# Reload trust-filer
sudo fapolicyd-cli --update
sudo systemctl restart fapolicyd

3. Trust en hel mappe uden hashes (mindre sikker)

Hvis du absolut må tillade en hel mappe uden hash-check, kan du oprette en custom rule (se afsnittet nedenfor). Undgå det på multi-tenant hosts, fordi det svækker fapolicyds integritetsgaranti.

Sådan fejlfinder du fapolicyd-nægtelser

Hver gang jeg deployer fapolicyd i et nyt miljø, bruger jeg samme workflow: kør i permissive mode med --debug-deny, saml en uge af logs, tilføj de manglende trust-poster, og skift derefter til enforce. Denne "audit first, enforce second"-tilgang er den samme filosofi som SELinux-hærdning og AppArmor aa-genprof, og den forhindrer stort set alle udrulnings-outages.

# Stop den aktive daemon
sudo systemctl stop fapolicyd

# Kør interaktivt i permissive-mode og logg kun deny-events
sudo fapolicyd --permissive --debug-deny 2> /var/log/fapolicyd-debug.log

# I en anden terminal, kør de applikationer/scripts der skal testes
# Efter en dag eller uge, gennemgå log og tilføj trust
grep 'dec=deny' /var/log/fapolicyd-debug.log | awk '{print $NF}' | sort -u

Loggen indeholder linjer som rule=13 dec=deny_audit perm=execute auid=1000 pid=8421 exe=/usr/bin/bash: path=/tmp/setup.sh ftype=text/x-shellscript trust=0. De to nøglefelter er rule= (hvilken rule matchede) og trust=0 (filen findes ikke i trust-databasen). Hvis trust=1, og du stadig får deny, er det en policy-issue, ikke en trust-issue, så skal du se på hvilken specifik rule, der ramte.

# Se rules med linje-numre for at matche rule=N i loggen
sudo fapolicyd-cli --list | grep -E '^\s*[0-9]+\.'

# Efter fejlfinding — stop debug-instansen og start service igen
sudo systemctl start fapolicyd
sudo systemctl status fapolicyd

For persistent audit-baseret fejlfinding kan du ændre deny_audit til deny_syslog i den relevante rule for at få events i /var/log/messages uden at skulle køre daemonen interaktivt. Kombiner med journal-integrationen i vores guide til Linux auditd og trusseldetektion for centraliseret logsamling.

Custom rules i /etc/fapolicyd/rules.d

Custom rules kommer i spil, når trust-poster alene ikke er nok, fx når du vil tillade en binær kun for en specifik UID, eller nægte eksekvering af scripts i midlertidige mapper, selv om de er trusted. Rules matches i numerisk rækkefølge på filnavnet, så konventionen er at give lav-numre til allow-rules og høje til catch-all deny.

# /etc/fapolicyd/rules.d/30-devops-allow.rules
# Tillad kun DevOps-brugere (gid=2001) at køre kubectl fra /usr/local/bin
allow perm=execute gid=2001 : path=/usr/local/bin/kubectl

# Nægt alle andre eksekveringer fra /tmp og /var/tmp uanset trust
deny_audit perm=execute all : dir=/tmp/
deny_audit perm=execute all : dir=/var/tmp/

# Nægt eksekvering af filer med write-mount (fx tmpfs)
deny_audit perm=execute all : ftype=application/x-executable device=tmpfs

Rule-syntaksen følger mønsteret <decision> perm=<perm> <subject> : <object>. Subject-felter inkluderer uid, gid, auid, pid, exe, trust og pattern. Object-felter inkluderer path, dir, device, ftype (MIME) og sha256hash. Se man fapolicyd.rules(5) for den fulde grammatik. Den blev opdateret i fapolicyd 1.3 (december 2025) med support for label=-attributter, der integrerer med SELinux-contexts.

# Test dine rules før reload
sudo fapolicyd-cli --check-config
sudo fapolicyd-cli --check-syntax /etc/fapolicyd/rules.d/30-devops-allow.rules

# Kompilér og aktivér
sudo fagenrules --load
sudo systemctl restart fapolicyd

Automatisering med RHEL system role og Ansible

Ingen sætter fapolicyd op manuelt på 500 servere. Red Hat har leveret en officiel fapolicyd RHEL system role siden RHEL 9.3, og den er stadig den nemmeste vej til flåde-udrulning. Rollen håndterer installation, konfiguration, custom trust-filer og enforcing-toggle idempotent.

---
# playbook: fapolicyd-baseline.yml
- name: Deploy fapolicyd baseline
  hosts: linux_servers
  become: true
  vars:
    fapolicyd_setup_enable_service: true
    fapolicyd_setup_state: present
    fapolicyd_setup_permissive: false
    fapolicyd_setup_integrity: sha256
    fapolicyd_setup_trust:
      - /opt/datadog-agent/
      - /opt/nessus/
    fapolicyd_setup_rules:
      - name: 30-devops-allow
        content: |
          allow perm=execute gid=2001 : path=/usr/local/bin/kubectl
          deny_audit perm=execute all : dir=/tmp/
  roles:
    - redhat.rhel_system_roles.fapolicyd

Rollen er del af rhel-system-roles-pakken (også tilgængelig som Ansible Galaxy collection redhat.rhel_system_roles). Den håndterer også edge cases som at deaktivere fapolicyd midlertidigt under større DNF-upgrades, noget du helt sikkert vil glemme første gang, du prøver at køre dnf upgrade på en flåde med fapolicyd aktivt.

For Debian/Ubuntu-hosts, hvor fapolicyd ikke er standard, anbefaler jeg at bruge AppArmor med strikte confine-profiler som funktionel erstatning, kombineret med systemd-sandboxing på tjeneste-niveau. Der findes en debian-port af fapolicyd på GitHub, men den er upstream-versioneret bagud og har historisk haft integrationsproblemer med apt.

Produktions-checklist og compliance (STIG, CIS)

fapolicyd nævnes eksplicit i DISA STIG for RHEL 8/9 (kontrol V-230560) og i CIS Benchmark for RHEL 10 (kontrol 4.1.4). Begge kræver, at daemonen er installeret, aktiveret og kører i enforcing mode. STIG kræver desuden, at trust-databasen bygges op af RPM-transaktioner (dvs. DNF-pluginet skal være aktivt).

Følgende er min minimums-checkliste, før jeg promoterer fapolicyd fra permissive til enforce i produktion:

  1. Baseline audit i mindst 7 dage. Kør permissive, saml alle dec=deny og adressér dem.
  2. Dokumentér alle trust-poster i Git. Brug /etc/fapolicyd/trust.d/*.trust-filer, ikke fapolicyd-cli --file add direkte.
  3. Test dnf-upgrade-flow. Kør dnf upgrade i en test-vm og bekræft, at DNF-pluginet opdaterer trust uden manuelle indgreb.
  4. Kombinér med SELinux enforcing. fapolicyd blokerer ikke root-eksekvering, så du har brug for MAC til at konfinere privilegerede processer.
  5. Overvåg fapolicyd-cli --check-trustdb-output. Send det til din SIEM via Wazuh eller lignende IDS for at fange filsystem-tampering.
  6. Hav en break-glass-procedure. Dokumentér, hvordan on-call disabler daemonen (systemctl stop fapolicyd), hvis en fejlkonfiguration låser dem ude af sudo.

Referencedokumentation: Red Hat Enterprise Linux 10 Security Hardening (fapolicyd chapter), upstream fapolicyd på GitHub, og Oracle Linux 10 fapolicyd-guide (matcher RHEL 10-adfærd 1:1).

Ofte stillede spørgsmål

Kan fapolicyd blokere applikationer, der kører som root?

Nej. fapolicyd's dokumenterede scope er at blokere uautoriseret eksekvering fra regulære brugere. Root-processer kan altid eksekvere hvad som helst, uanset trust-database. For root-konfinering skal du bruge SELinux (targeted policy med unconfined_r deaktiveret) eller systemd-sandboxing med NoNewPrivileges og CapabilityBoundingSet.

Hvad er forskellen mellem known-libs og restrictive policy?

known-libs tillader eksekvering af enhver trusted ELF-binær og linker kun trusted shared libraries. Det er standard og produktionsegnet. restrictive går et skridt videre og blokerer eksekvering via runtime-linkeren (fx ld.so /path/to/binary) og tillader kun ELF- og Python-programmer. Vælg restrictive til compliance-hosts, der ikke skal køre udviklerværktøj.

Hvordan tilføjer jeg tredjepartsapplikationer til fapolicyd trust database?

Placér en .trust-fil i /etc/fapolicyd/trust.d/ med formatet <sti> <størrelse> <sha256> pr. linje, og kør fapolicyd-cli --update. Alternativt kan du bruge fapolicyd-cli --file add <sti> til hurtig ad hoc-tilføjelse, men filer i trust.d/ er nemmere at versionsstyre og re-deploye med Ansible.

Hvorfor blokerer fapolicyd mit script, selv om det ligger i /usr/local/bin?

Placering betyder intet for fapolicyd. Trust gør. /usr/local/bin er ikke automatisk trusted, fordi filer, der ikke er installeret af DNF/RPM, ikke havner i trust-databasen. Enten kopier scriptet ind via en RPM-pakke, eller tilføj det eksplicit med fapolicyd-cli --file add /usr/local/bin/mit-script.sh efterfulgt af fapolicyd-cli --update.

Kan jeg bruge fapolicyd på Ubuntu eller Debian?

Der findes en community-port på GitHub, men den er ikke pakket i officielle apt-repos og har historisk haltet efter upstream. For Debian/Ubuntu-produktion er den mere realistiske vej AppArmor kombineret med systemd-sandboxing og et EDR-værktøj. Vent med fapolicyd på Debian, indtil pakningssituationen stabiliseres, formentlig med Debian 13 "Trixie"-successor.

Yuki Tanaka
Om Forfatteren Yuki Tanaka

Linux kernel security engineer with a background in eBPF and LSM. Likes hardening more than she likes sleeping.