AppArmor på Linux i 2026: Profiler, håndhævelse og fejlfinding

Praktisk AppArmor-guide til Debian, Ubuntu og openSUSE i 2026: profil-tilstande, aa-genprof-workflow, exec-overgange, fejlfinding af denials, systemd og Kubernetes 1.30+ appArmorProfile.

AppArmor Linux 2026: Profiler & MAC

Opdateret: 6. august 2026

AppArmor er en sti-baseret Linux Security Module (LSM), der begrænser hvad et program må røre ved på filsystemet, netværket og kerne-API'et, uafhængigt af hvilken bruger det kører som. På Ubuntu og Debian er AppArmor default-MAC-laget, og i 2026 med kerne 6.11+ er den nye userspace prompt-tilstand og forbedret support for netværks-mediation blevet stabile. Jeg har kørt AppArmor i produktion i seks år på en Debian-flåde (masser af pakkeopgraderinger senere), og den her guide er workflowet jeg faktisk bruger, fra aa-genprof til enforce, med de fejlfindingsskridt der virker klokken tre om morgenen.

  • AppArmor bruger sti-baserede regler i /etc/apparmor.d/ – ingen filsystem-labels som SELinux kræver, hvilket gør profiler læsbare i git-diff.
  • Profiler har tre driftstilstande: enforce blokerer og logger overtrædelser, complain logger kun, og disabled udelades ved indlæsning.
  • Brug aa-genprof og aa-logprof til at generere en profil ved at køre programmet og lade AppArmor observere adfærden – manuelt skrevne profiler er sjældent produktionsklare.
  • Denials logges via kernen til dmesg, journalctl -k eller auditd; filtrer på apparmor="DENIED" for hurtig diagnose.
  • Kerne 6.11 introducerede userspace_prompt-tilstand der lader et desktop-værktøj godkende adgang runtime – nyttigt for udviklerlaptops, sjældent for servere.
  • Kubernetes 1.30+ har GA-support for AppArmor via securityContext.appArmorProfile-feltet; annotationsformen er deprecated.

Hvad er AppArmor, og hvordan adskiller det sig fra SELinux?

AppArmor er en Linux Security Module, der leverer Mandatory Access Control (MAC) ved at binde en profil til en eksekverbar sti. Når et program starter, slår kernen op i den indlæste profil-database, og enhver systemcall der rører filer, sockets, capabilities eller mount-points bliver tjekket mod profilens regler. Til forskel fra klassisk Unix DAC – hvor rod har alt – kan selv en root-proces under en enforce-profil ikke læse en fil, hvis profilen ikke tillader det.

Den vigtigste praktiske forskel fra SELinux er modellen: SELinux etikettererer hver inode i filsystemet (xattr) og matcher processer mod labels, mens AppArmor matcher på absolute stier. Det betyder:

  • Læsbarhed: En AppArmor-profil ser ud som /usr/sbin/nginx { /var/log/nginx/*.log w, } – enhver kan læse den. SELinux-politik kræver typisk audit2allow-mellemled.
  • Mount-følsomhed: Fordi regler er sti-baserede, kan bind-mounts og symlinks omgå eller ændre effektiv politik. SELinux-labels følger med inoden.
  • Værktøjskæde: AppArmor tilbyder aa-genprof til automatisk profilgenerering; SELinux har audit2allow, men workflow er tungere.
  • Stacking: Siden kerne 5.1 kan AppArmor køre stacket med andre LSM'er som Landlock, BPF-LSM og Yama – ikke gensidigt eksklusivt med SELinux i teorien, men i praksis vælger distributioner én.

Debian, Ubuntu og openSUSE forsender AppArmor som default. RHEL, CentOS Stream, Fedora og Rocky bruger SELinux. Hvis du kører en blandet flåde, hold én politik pr. distribution – forsøg på at cross-porte profiler koster mere tid end at vedligeholde to sæt. For yderligere sandboxing af upriviligerede processer supplerer Landlock LSM til uprivilegeret sandboxing AppArmor pænt – Landlock håndterer applikations-selv-sandboxing, mens AppArmor giver system-wide policy.

AppArmor-profilernes tilstande: enforce, complain og prompt

Hver profil kører i én af følgende tilstande, og at forstå dem er forskellen mellem en profil du kan releasere og en der låser produktion ude:

TilstandBlokerer overtrædelserLogger overtrædelserAnvendelse
enforceJaJaProduktion
complainNejJaProfil-udvikling, canary-udrulninger
unconfinedNejNejProfil indlæst men helt inaktiv – sjælden
disabledNejNejProfil ekskluderet ved indlæsning; symlink i /etc/apparmor.d/disable/
prompt (6.11+)Efter runtime-svarJaDesktop; en userspace-agent besvarer forespørgsler
killDræber processenJaAggressiv – sjældent brugt uden for research

Bemærk at deny-regler i en profil altid håndhæves – også i complain-tilstand. Det er en fælde: en profil du tror bare logger, kan stadig blokere hvis den indeholder en eksplicit deny. Skift tilstand med aa-complain /path/til/binær eller aa-enforce /path/til/binær. Ændringen er øjeblikkelig og persistent på tværs af reboots, fordi den ændrer flags= i profilens header.

Den nye userspace_prompt-tilstand, som blev merged i kerne 6.11 og aktiveret som default i Ubuntu 24.10, kræver en running userspace-listener (typisk apparmor-notify). Den er tænkt til desktop-scenarier hvor en bruger interaktivt godkender fil-adgang – på servere skal du ikke aktivere den, da forespørgsler uden agent bliver blokeret indtil timeout.

Installation og verifikation på Debian, Ubuntu og openSUSE

AppArmor følger med kernen, men userspace-værktøjerne installeres separat. På et frisk Debian 12- eller Ubuntu 24.04-system er kernen allerede kompileret med CONFIG_SECURITY_APPARMOR=y, og de fleste kerne-standardprofiler er indlæst. For at få hele værktøjssættet inklusive aa-genprof og aa-logprof:

# Debian/Ubuntu
sudo apt update
sudo apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra

# openSUSE Leap 15.6 / Tumbleweed
sudo zypper install apparmor-profiles apparmor-utils yast2-apparmor

# Verificer status
sudo aa-status

aa-status rapporterer noget lignende:

apparmor module is loaded.
42 profiles are loaded.
38 profiles are in enforce mode.
   /usr/bin/man
   /usr/lib/snapd/snap-confine
   /usr/sbin/cups-browsed
   ...
4 profiles are in complain mode.
   /usr/bin/lsb_release
   ...
12 processes have profiles defined.
10 processes are in enforce mode.
2 processes are in complain mode.
0 processes are unconfined but have a profile defined.

Sidste linje er den, du typisk skal reagere på: en proces der er "unconfined men har en profil defineret" betyder at binæret startede før profilen blev indlæst, eller at profilen ikke matcher det aktuelle path (fx efter en pakkeopdatering flyttede binæret). Genstart tjenesten eller reload profilen med sudo systemctl reload apparmor.

Skal AppArmor være default LSM på en kerne bygget med flere LSM'er, tilføj lsm=landlock,lockdown,yama,integrity,apparmor,bpf til kernelinjen i GRUB. Rækkefølgen matcher indlæsningsrækkefølgen – AppArmor skal stå før bpf, hvis du også kører BPF-LSM.

Generér din første profil med aa-genprof

Ærligt talt: manuelt skrevne AppArmor-profiler er en tabsforretning. En moderne tjeneste rører hundredvis af filer, læser environment-variable, forbinder til DNS og skriver til journald, og at nedskrive alle regler i hånden fører altid til huller. Brug i stedet aa-genprof-workflowet, som er den officielle måde Debian AppArmor-teamet anbefaler at bootstrappe profiler.

Antag at vi hærder en lille Python-microservice på /opt/svc/api. I én terminal starter vi aa-genprof:

sudo aa-genprof /opt/svc/api

Værktøjet opretter en skabelonprofil i complain-tilstand og beder dig køre programmet. I en anden terminal:

# Kør tjenesten og træn den mod reelle requests
sudo systemctl start svc-api
curl -X POST http://127.0.0.1:8080/orders -d '{"item":"test"}'
curl http://127.0.0.1:8080/health
# Ram alle endpoints der køres i produktion – dette definerer profilens dækning
sudo systemctl stop svc-api

Vend tilbage til aa-genprof-terminalen og tryk S for "scan log for events". Værktøjet gennemgår hver observeret operation og spørger, om du vil tillade, afvise, glob-abstrahere den, eller anvende en genbrugt abstraction. Vælg glob for logfiler (fx /var/log/svc/*.log) og abstractions for standard-adfærd (abstractions/base, abstractions/python, abstractions/nameservice).

Resultatet er en profil som denne:

#include <tunables/global>

/opt/svc/api {
  #include <abstractions/base>
  #include <abstractions/python>
  #include <abstractions/nameservice>

  capability net_bind_service,
  capability setgid,
  capability setuid,

  network inet stream,
  network inet6 stream,

  /opt/svc/api mr,
  /opt/svc/lib/**.py r,
  /opt/svc/lib/**.so mr,
  /var/log/svc/*.log w,
  /run/svc/*.pid rw,
  /etc/svc/config.yaml r,
  /etc/ssl/certs/ca-certificates.crt r,

  owner /proc/*/status r,
  owner /proc/*/task/*/status r,

  deny /etc/shadow r,
  deny /root/** rwklx,
}

Læg profilen i /etc/apparmor.d/opt.svc.api (bindestreg-punktuering matcher stien), reload med sudo apparmor_parser -r /etc/apparmor.d/opt.svc.api, og skift til enforce: sudo aa-enforce /opt/svc/api.

Exec-overgange: Px, Cx, ix og hvorfor ux er farligt

Et af de mest oversete elementer i AppArmor-profiler er exec-overgange: hvad der sker når din confined proces starter et child. Kernen skal bestemme, hvilken profil (om nogen) child'en skal køre under. Jeg har set flere reviews hvor et enkelt ux-flag stille og roligt annullerede alt det hærdningsarbejde vi lige havde afsluttet. AppArmor definerer fem overgangsmodifikatorer:

  • ix (inherit) – Child arver moderprofilen. Bruges når hjælpescriptet er trusted og hører til samme tjeneste.
  • Px (profile transition) – Child skifter til en anden navngiven profil. Brug denne til at kalde eksterne binærer der har egne profiler (fx /usr/bin/gpg).
  • Cx (child profile) – Overgang til en child-profil defineret indlejret i moderprofilen. God til hjælpescripts der ikke fortjener egen top-level profil.
  • Ux (unconfined med scrubbing) – Kør uden profil, men ryd op i miljøvariable som LD_PRELOAD. Sjældent den rigtige løsning.
  • ux (unconfined) – Kør fuldstændig uden confinement. Dette er en escape hatch, og bruges den forkert, ophæver den hele profilens værdi.

Konkret eksempel: en confined webserver der skal kunne kalde /usr/bin/convert (ImageMagick) til billedbehandling. Hvis du skriver /usr/bin/convert ux,, kan convert nu læse alt filsystemet og skrive hvor som helst – hvis convert eksploderer via et CVE i en billed-parser, er profilen omgået. Skriv i stedet:

/usr/bin/convert Px -> imagemagick_worker,

Og definer imagemagick_worker som separat profil med kun de nødvendige rettigheder (læs upload-mappen, skriv til tmp, ingen netværk). Denne pattern – en snæver worker-profil bag hver Px-transition – er kernen i god AppArmor-hygiejne. Kombineret med systemd-tjeneste-hærdning med sandboxing-direktiver får du to lag confinement, hvilket sætter en meningsfyldt barre for exploit-authors.

Sådan fejlfinder du AppArmor-denials i logs

Når en profil blokerer noget, fejler applikationen ofte med kryptiske "Permission denied" – uden at nævne AppArmor. Første skridt i triaging er altid at spørge: er AppArmor skyldig? Bekræft med disse fire kommandoer:

# 1. Seneste kerne-denials
sudo journalctl -k --since "10 minutes ago" | grep -i "apparmor.*DENIED"

# 2. Live-følg mens du reproducerer
sudo journalctl -k -f | grep apparmor

# 3. dmesg for kortere retention
sudo dmesg | grep apparmor | tail -20

# 4. auditd hvis installeret
sudo ausearch -m AVC -ts recent | grep apparmor

Et typisk denial-log-hit ser sådan ud:

audit: type=1400 audit(1723027845.123:456): apparmor="DENIED" \
  operation="open" profile="/opt/svc/api" name="/etc/svc/secrets.yaml" \
  pid=12934 comm="python3" requested_mask="r" denied_mask="r" \
  fsuid=1000 ouid=0

Nøglefelterne er operation (hvad blev forsøgt), profile (hvilken regel spillede), name (hvad blev rørt), og requested_mask/denied_mask (hvilke bits blev afvist – r for read, w for write, x for execute, k for lock, l for link, m for mmap-exec). Med denne info kan du enten manuelt tilføje en linje /etc/svc/secrets.yaml r, til profilen, eller lade aa-logprof guide dig:

sudo aa-logprof

aa-logprof gennemgår alle nyere denials og præsenterer samme allow/deny/glob-menu som aa-genprof, men mod den eksisterende profil. Dette er standardværktøjet til iterativ profil-refinement efter en pakkeopdatering ændrede binærets adfærd. For dybere kontinuerlig indsigt kombinerer jeg AppArmor-events med Linux auditd til systemrevision og trusseldetektion, hvilket giver samlet rapportering på tværs af MAC- og syscall-events.

AppArmor og systemd: tjenester, ConfinePolicy og namespace-integration

systemd og AppArmor kombineres på to måder. Den simpleste er at lade AppArmor auto-attach en profil, når binæret starter – det virker uden systemd-side konfiguration, så længe profilens header matcher binærets absolute sti. Denne default er tilstrækkelig for de fleste tjenester.

Den anden – og mere kontrollerede – er at bruge AppArmorProfile= direktivet i unit-filen:

# /etc/systemd/system/svc-api.service.d/apparmor.conf
[Service]
AppArmorProfile=svc-api-worker

Fordelen er, at systemd så sætter transitionen op før execve, hvilket giver dig atomicitet – enten kører tjenesten under svc-api-worker, eller også starter den ikke. Uden dette direktiv kan en fejlkonfigureret profil betyde, at tjenesten kører kortvarigt unconfined, hvis profilen ikke er indlæst i tide efter reboot.

Kombiner AppArmor-profilen med systemd's egne sandboxing-direktiver – ProtectSystem=strict, NoNewPrivileges=yes, PrivateTmp=yes, RestrictAddressFamilies= – for defense-in-depth. AppArmor giver dig sti-baseret MAC; systemd giver dig namespace-, capability- og seccomp-restriktioner. De supplerer hinanden i stedet for at overlappe.

Fra systemd 254 blev der tilføjet LoadCredential=-support med AppArmor-integration: credentials udleveres via en anonymt mounted tmpfs, som profilens regler kan referere til uden at hardcode-sti. Dette fjerner en typisk grund til at bløde profilen op (for at læse en runtime-genereret nøgle).

AppArmor i containere og Kubernetes 1.30+

Container-runtimes har haft AppArmor-support i årevis – Docker anvender en default-profil kaldet docker-default, og containerd/CRI-O har tilsvarende defaults. For custom-profiler i Kubernetes: fra 1.30 er AppArmor-API'en GA, og annotationsformen (container.apparmor.security.beta.kubernetes.io/) er deprecated til fjernelse i 1.33.

Ny syntax i pod-spec:

apiVersion: v1
kind: Pod
metadata:
  name: api-hardened
spec:
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: svc-api-worker
  containers:
  - name: api
    image: registry.example.com/svc-api:v2.4.1

type kan være RuntimeDefault (brug runtimens default), Localhost (brug en named profil indlæst på noden), eller Unconfined (deaktivér AppArmor for pod'en – kræver typisk explicit undtagelse i Pod Security Standards). Profilen skal være tilgængelig på hver node hvor pod'en kan schedules; brug en DaemonSet eller MachineConfig/Ignition til at distribuere profil-filer under /etc/apparmor.d/.

Vær opmærksom på at container-profiler skal håndtere overlayfs-stier. En regel som /app/config.yaml r, virker i containeren, men kernens denial-log viser stien som /var/lib/containerd/.../rootfs/app/config.yaml. AppArmor håndterer dette internt via chroot-relative matching, men hvis du ser mismatched stier i logs, tjek at abstractions/base er inkluderet – den håndterer standard-overlayfs-plumbing.

Produktions-tjekliste og løbende profilvedligeholdelse

Efter tre år med AppArmor på tværs af ca. 500 noder har vi indkogt følgende tjekliste for enhver ny profil, der går i produktion. Den er ikke lang, men hvert punkt er lært på den hårde måde:

  1. Version-kontrol profiler i git. /etc/apparmor.d/ hører hjemme i din config-management (Ansible, Salt, cluster-config). Manuel edit på noden efterlader drift.
  2. Kør syntaks-check før deploy: sudo apparmor_parser -Q -T -K profile-fil validerer uden at indlæse.
  3. Deploy i complain-tilstand først. Sæt flags=(complain) i profilens header. Kør i syv dage. Gennemgå aa-logprof-output.
  4. Enforce på canary-noder i 48 timer før flåde-rollout. Overvåg tjenestens error-rate og AppArmor-denial-count.
  5. Alertér på nye denials. Send apparmor="DENIED"-events til dit SIEM med thresholds – en pludselig spike kan være en pakkeopdatering, en misconfig, eller (sjældent) en aktiv attack.
  6. Genkør genprof efter major pakkeopgraderinger. Nye afhængigheder betyder nye stier. Behandl profilopdatering som en del af opgraderingsproceduren for tjenesten.
  7. Test disaster recovery: Kan du booten med apparmor=0 på kernelinjen, hvis en dårlig profil forhindrer systemd i at starte? Hav proceduren dokumenteret og testet.
  8. Undgå ux uden godkendelse. Enhver ux-linje bør kræve en PR-kommentar og security-review; det er den eneste måde at holde profilhygiejne på tværs af et team.

På vores flåde eksporterer vi aa-status-output som en Prometheus-metric via node_exporter's textfile-collector, hvilket lader os plotte antallet af profiler i enforce over tid. En pludselig drop – f.eks. fra 42 til 38 – betyder oftest at en pakkeopdatering flyttede et binært, og en profil ikke længere matcher. Det er den slags langsomme regression, ingen enkelt log-linje fanger.

Ofte stillede spørgsmål

Kan AppArmor og SELinux køre samtidigt på samme system?

Teknisk kan de stackes siden kerne 5.1, men i praksis forsender ingen mainstream-distribution begge samtidigt. Debian, Ubuntu og openSUSE vælger AppArmor; RHEL-familien vælger SELinux. Skift kræver rekompilering af kernen med lsm=-parameter og genskabelse af alle labels/profiler – ikke noget du gør på en produktionsserver.

Hvordan deaktiverer jeg en enkelt AppArmor-profil uden at slå hele AppArmor fra?

Kør sudo aa-disable /path/til/binær. Kommandoen opretter et symlink i /etc/apparmor.d/disable/, som forhindrer profilen i at blive indlæst ved næste reload. Sæt tilbage med sudo aa-enforce /path/til/binær. Undgå at slette profil-filen – du mister historikken.

Hvad er forskellen mellem aa-complain og bare at fjerne enforce-flaget?

aa-complain modificerer profil-filens header ved at tilføje flags=(complain) og reloader profilen. Effekten er persistent. En manuel edit gør præcis det samme, men aa-complain er transaktionel – den fejler pænt, hvis parse-check ikke består. Brug værktøjet fremfor sed.

Hvorfor viser aa-status "0 processes are unconfined" selvom nogle af mine tjenester ikke har profiler?

aa-status tæller kun processer der matcher en indlæst profil. En proces uden nogen profil-match rapporteres slet ikke – den er totalt uden AppArmor-relation, hvilket er default for ethvert binært uden en /etc/apparmor.d/-fil. For at se helt unconfined processer, kryds-tjek ps aux mod ls /etc/apparmor.d/.

Kan AppArmor beskytte mod kernel-eksploits?

Nej – AppArmor kører i kernen og validerer syscalls, men kan ikke beskytte mod eksploits der kompromitterer kernen selv. For det lag, kombinér med kernel-hærdning (lockdown-mode, KASLR, SMEP/SMAP), en aktuel kerne (sikkerhedspatches), og gerne en ekstra runtime-detektor som Tetragon eller Falco der ser suspekt syscall-adfærd fra kerne-space via eBPF.

Om Forfatteren Adaeze Okonkwo

Adaeze runs platform security at a Series C fintech in Lagos, where she spent the last three years migrating a sprawling Debian estate to immutable Flatcar nodes on bare-metal Kubernetes. Before that she was a senior SRE at Andela for five years and did a two-year stint at Interswitch hardening PCI-DSS Linux hosts the old-fashioned way - Lynis scans, Bastille, and a lot of shell scripts that probably shouldn't have existed. She holds OSCP, CKS, and is one of the few people who has actually read the entire CIS Debian 12 benchmark cover to cover. She maintains a public set of OpenSCAP profiles tuned for African ISP environments where bandwidth assumptions matter. Adaeze writes about the operational side of Linux security: SSH key rotation that actually happens, log shipping that survives a reboot, and incident response runbooks people will read at 3am.