🍺 Buy me a beer
🔒

AppArmor

Metti una gabbia ai processi prima che ti facciano del male.
Linux Capabilities, seccomp e MAC spiegati a chi non ha tempo di leggere le man page.

"Il minimo privilegio è il massimo privilegio che puoi concederti."
— qualcuno con più downtime alle spalle di te

// 01

Perché esiste tutto questo

DAC, MAC, e il principio del minimo privilegio

🎯 Il problema: root o non root

Linux tradizionale ha un sistema di permessi binario: o sei root (puoi fare tutto) o non lo sei (puoi fare poco). Questo è il DAC — Discretionary Access Control: il proprietario del file decide chi può accedervi.

Il problema è che molti processi legittimi hanno bisogno di fare cose privilegiate ma non di tutto il potere di root. Un web server ha bisogno di ascoltare sulla porta 80, non di riformattare il disco.

🔓 DAC — vecchio modo

  • owner/group/other su ogni file
  • SUID bit per escalare a root
  • Tutto o niente: se un processo viene compromesso con root, game over
  • Un exploit su nginx con SUID? Shell di root. Ciao.

🔒 MAC — nuovo modo

  • Mandatory Access Control: il sistema impone regole aggiuntive
  • Anche root può essere limitato da policy MAC
  • Ogni processo ha solo i permessi che gli servono
  • Un exploit su nginx in sandboxed? Legge solo /var/www. Fine.
Il principio del minimo privilegio Ogni processo dovrebbe avere esattamente i permessi che gli servono per funzionare, e nulla di più. AppArmor, capabilities e seccomp sono tre strumenti diversi per implementare questo principio a livelli diversi.
🛡️
AppArmor
path-based MAC

Profili per processo: cosa può leggere, scrivere, eseguire

Capabilities
granular root

Dividi i poteri di root in ~40 privilegi separati

🔧
seccomp
syscall filter

Filtra le syscall che un processo può chiamare

// 02

AppArmor

Il MAC che puoi capire senza un dottorato

📖 Cos'è AppArmor

AppArmor (Application Armor) è un modulo LSM (Linux Security Module) che impone policy di accesso per singolo programma. È il MAC predefinito su Ubuntu, Debian, openSUSE. L'alternativa è SELinux, usato da RHEL/Fedora, che è molto più potente ma richiede sacrifici umani per essere configurato.

AppArmor lavora con i path dei file, non con le etichette degli inode come SELinux. Più semplice, meno flessibile, ma almeno ci capisci qualcosa.

AppArmor vs SELinux in una frase AppArmor: "questo processo può leggere i file in /etc/nginx/" SELinux: "questo processo con label httpd_t può accedere agli oggetti con label httpd_config_t" Stesso risultato, SELinux è più robusto (hardlink, bind mount non lo ingannano), AppArmor è più umano.
💀
disabled
Profilo caricato ma ignorato. Inutile. Non farlo.
📝
complain
Logga le violazioni ma non le blocca. Utile per sviluppare i profili.
🚫
enforce
Blocca tutto ciò che non è esplicitamente permesso. Questo serve.
# Controllare lo stato di AppArmor
sudo apparmor_status
# oppure
sudo aa-status

# Output tipico:
apparmor module is loaded.
27 profiles are loaded.
27 profiles are in enforce mode.
   /usr/bin/evince
   /usr/sbin/nginx
   ...
0 profiles are in complain mode.
3 processes have profiles defined.
3 processes are in enforce mode.

# Verificare se AppArmor è abilitato nel kernel
cat /sys/module/apparmor/parameters/enabled
Y

# I profili di sistema sono qui:
ls /etc/apparmor.d/

📦 Installazione

# Ubuntu/Debian (già installato)
sudo apt install apparmor apparmor-utils apparmor-profiles

# Tools utili
sudo apt install apparmor-profiles-extra

# Abilitare all'avvio (se disabilitato)
sudo systemctl enable apparmor
sudo systemctl start apparmor

🎛️ Comandi base

# Mettere un profilo in complain
sudo aa-complain /usr/sbin/nginx

# Mettere in enforce
sudo aa-enforce /usr/sbin/nginx

# Disabilitare un profilo
sudo aa-disable /usr/sbin/nginx

# Ricaricare un profilo
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
// 03

Scrivere i profili

La sintassi che sembra strana ma si impara in 10 minuti

📄 Struttura di un profilo

Un profilo AppArmor ha una struttura semplice: definisci il percorso del binario, poi elenchi cosa può fare. Tutto il resto è negato implicitamente.

# /etc/apparmor.d/usr.sbin.myapp #include <tunables/global> /usr/sbin/myapp { # include regole comuni #include <abstractions/base> #include <abstractions/nameservice> # Capability: cosa può fare come root parziale capability net_bind_service, capability setuid, capability setgid, # File: percorso + permessi (r=read, w=write, x=execute, m=mmap) /etc/myapp/ r, /etc/myapp/** r, /var/log/myapp/ rw, /var/log/myapp/** rw, /var/run/myapp.pid rw, /tmp/myapp-* rw, # Librerie di sistema (già incluse da abstractions/base) /usr/lib/** mr, /lib/** mr, # Network: può fare socket network inet stream, network inet6 stream, # Nega esplicito (opzionale, è già il default) deny /etc/shadow r, deny /root/** rw, }
PermessoSignificatoUso tipico
rreadLeggere file di configurazione
wwriteScrivere log, state files
aappendAppend-only ai log
xexecute (con profilo)Eseguire altri binari con profilo proprio
uxexecute (unconfined)Eseguire senza profilo — usare con parsimonia
ixexecute (inherit)Il figlio eredita il profilo del padre
mmmap executableLibrerie condivise
llinkCreare hard link
klockFile locking

🧩 Abstractions: riusa invece di riscrivere

Le abstractions sono include predefinite per scenari comuni. Invece di elencare ogni singola libreria di sistema, includi <abstractions/base> e hai fatto.

AbstractionCosa include
abstractions/baselibc, libpthread, librerie C fondamentali
abstractions/nameserviceDNS, NSS, /etc/hosts, /etc/resolv.conf
abstractions/ssl_certsLettura certificati CA di sistema
abstractions/pythonPython runtime e librerie
abstractions/web-dataDirectory web standard
abstractions/user-tmpAccesso a /tmp con pattern sicuri
# Glob patterns nei profili
/etc/myapp/        r,   # solo la directory
/etc/myapp/*       r,   # tutti i file diretti (non ricorsivo)
/etc/myapp/**      r,   # ricorsivo: tutti i file e subdirectory
/var/log/**.log    rw,  # tutti i .log ricorsivamente
@{HOME}/.config/   r,   # variabile: espande la home dell'utente
// 04

Workflow pratico

Come creare un profilo senza indovinare

Non scrivere profili a mano dall'inizio Esistono strumenti che leggono i log e generano il profilo per te. Usali. Poi raffini a mano.
aa-genprof
esegui l'app
scan logs
revisiona
enforce

🤖 aa-genprof: il modo pigro

aa-genprof mette il processo in complain mode, lo avvii, lo usi normalmente, poi scansiona i log e genera le regole. Funziona bene per applicazioni con comportamento prevedibile.

# Passo 1: avvia il generatore di profilo
sudo aa-genprof /usr/sbin/nginx

# L'output ti chiederà di avviare l'app in un altro terminale e usarla
Please start the application to be profiled in
another window and exercise its functionality now.

Once completed, select the (S)can option below in
order to scan the system logs for AppArmor events.

[(S)can system log for AppArmor events] / (F)inish

# In un altro terminale: avvia e usa nginx
sudo systemctl start nginx
curl http://localhost/
curl http://localhost/api/test

# Torna a aa-genprof, premi S per scansionare i log
# Per ogni accesso negato, scegli cosa fare:
  (A)llow / (D)eny / (I)gnore / (G)lob / (Q)uit

📊 aa-logprof: per profili esistenti

Se hai già un profilo e l'app ha generato nuove violazioni nei log, aa-logprof legge /var/log/syslog (o journalctl) e propone aggiornamenti.

# Analizzare i log per proporre aggiornamenti
sudo aa-logprof

# Oppure da un file di log specifico
sudo aa-logprof -f /var/log/syslog

# Vedere le violazioni in tempo reale
sudo journalctl -f | grep -i apparmor

# Formato del log di violazione:
kernel: audit: type=1400 audit(1234567890.123:456):
  apparmor="DENIED" operation="open"
  profile="/usr/sbin/nginx"
  name="/etc/ssl/private/nginx.key"
  pid=1234 comm="nginx"
  requested_mask="r" denied_mask="r"

# Traduzione: nginx ha cercato di leggere la chiave SSL e AppArmor l'ha bloccato
# Soluzione: aggiungere /etc/ssl/private/nginx.key r, al profilo

Iter consigliato

  • Crea il profilo con aa-genprof
  • Metti in complain mode per una settimana in produzione
  • Raccogli i log con aa-logprof
  • Revedi le regole proposte (rimuovi quelle troppo larghe)
  • Passa a enforce
  • Monitora per i primi giorni

⚠️ Errori comuni

  • Passare subito a enforce senza test in complain
  • Usare /** su directory sensibili (troppo largo)
  • Usare ux su ogni exec (equivale a non avere il profilo)
  • Non ricaricare il profilo dopo le modifiche
  • Dimenticare di profilare i processi figlio
// 05

Linux Capabilities

I poteri di root, venduti al dettaglio

Il problema di SUID

Prima delle capabilities, se un programma aveva bisogno di fare cose privilegiate (aprire una porta <1024, cambiare UID, etc.) l'unico modo era SUID root: il binario gira come root anche se lo avvia un utente normale.

Il problema è ovvio: se quel binario ha un bug, l'attaccante ha una shell root. Ping, su, sudo, passwd — tutti storicamente SUID root. Le capabilities dividono i ~40 poteri di root in privilegi separati. Dai solo quello che serve.

SUID root nel 2025 Ogni binario SUID root è una potenziale escalation path. Controllali periodicamente:
find / -perm -4000 -type f 2>/dev/null
Se trovi qualcosa che non riconosci, indaga.
CAP_NET_BIND_SERVICE
Aprire porte <1024 senza root. Nginx su porta 80.
CAP_NET_RAW
Socket RAW e PACKET. Ping, tcpdump, nmap.
CAP_SYS_PTRACE
Tracciare processi con ptrace(). strace, gdb.
CAP_DAC_OVERRIDE
Ignorare i permessi DAC sui file. Pericoloso.
CAP_SETUID
Cambiare UID/GID del processo (su, passwd).
CAP_CHOWN
Cambiare owner di file arbitrari.
CAP_SYS_ADMIN
Montare filesystem, configurare namespace. Praticamente root — evitare.
CAP_NET_ADMIN
Configurare interfacce di rete, firewall, routing.
CAP_SYS_TIME
Modificare l'orologio di sistema. NTP daemon.
CAP_KILL
Mandare segnali a processi di altri utenti.
CAP_AUDIT_WRITE
Scrivere nel log di audit del kernel.
CAP_SYS_CHROOT
Eseguire chroot(). Container, jail, chrootenv.
# Vedere le capability di un processo
cat /proc/$(pgrep nginx)/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000

# Decodificare il valore hex in nomi leggibili
capsh --decode=0000000000000400
0x0000000000000400=cap_net_bind_service

# Vedere le capability del processo corrente
capsh --print
// 06

setcap in pratica

Assegnare capabilities senza SUID

🔧 setcap e getcap

Le capabilities possono essere assegnate ai file binari invece che tramite SUID. Il processo parte senza essere root ma ottiene solo i privilegi specifici che gli servono.

# Sintassi: setcap <capability>=<set> <file>
# Set: e=effective, p=permitted, i=inheritable

# Esempio classico: nginx su porta 80 senza root
sudo setcap cap_net_bind_service=ep /usr/sbin/nginx

# Verificare le capability impostate
getcap /usr/sbin/nginx
/usr/sbin/nginx cap_net_bind_service=ep

# Ping senza SUID (moderno)
getcap /bin/ping
/bin/ping cap_net_raw=ep

# Rimuovere tutte le capability da un binario
sudo setcap -r /usr/sbin/nginx

# Vedere tutte le capability nel sistema
getcap -r / 2>/dev/null

✅ Con setcap

  • Processo gira come utente normale
  • Ha solo il privilegio specifico
  • Un exploit ottiene solo cap_net_bind_service
  • Non può leggere /etc/shadow, /root, etc.

❌ Con SUID root

  • Processo gira come root
  • Ha TUTTI i privilegi
  • Un exploit ottiene una shell root
  • Game over

🐍 Capabilities con systemd

Per i servizi systemd, puoi assegnare capabilities direttamente nell'unit file senza toccare il binario:

# /etc/systemd/system/myapp.service
[Unit]
Description=My App

[Service]
User=myapp
Group=myapp
ExecStart=/usr/bin/myapp

# Aggiungi solo le capability necessarie
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

# Hardening aggiuntivo
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/myapp

[Install]
WantedBy=multi-user.target
systemd hardening è gratis Le direttive NoNewPrivileges, PrivateTmp, ProtectSystem, ProtectHome costano zero effort e danno un sacco. Mettile sempre nei tuoi unit file. systemd-analyze security myapp.service ti dice il punteggio di sicurezza.
// 07

seccomp

Filtra le syscall. Perché nginx non ha bisogno di reboot(2).

🔧 Cosa fa seccomp

seccomp (secure computing mode) filtra le syscall che un processo può invocare. Ogni processo Linux comunica col kernel tramite ~400 syscall. Un web server ne usa forse 50. Perché dovrebbe poter chiamare reboot(), ptrace(), o init_module()?

seccomp permette di creare una allowlist o denylist di syscall. Se un processo compromesso prova a chiamare una syscall non autorizzata, viene ucciso con SIGKILL.

Docker usa seccomp di default Quando lanci un container Docker senza opzioni, applica automaticamente un profilo seccomp che blocca ~44 syscall pericolose (incluse reboot, mount, ptrace, kexec_load). Puoi vederne il profilo di default su GitHub in moby/moby.
# seccomp con systemd (modo più semplice)
[Service]
# Allowlist per tipo di servizio
SystemCallFilter=@system-service      # preset per servizi generici
SystemCallFilter=@network-io          # syscall di rete
SystemCallFilter=~@privileged         # ~ = nega queste
SystemCallFilter=~@mount              # nega le syscall di mount

# Preset disponibili:
# @basic-io, @file-system, @io-event, @ipc, @keyring
# @memlock, @module, @mount, @network-io, @obsolete
# @privileged, @process, @raw-io, @reboot, @setuid
# @signal, @swap, @system-service, @timer

# Vedere le syscall in un preset
systemd-analyze syscall-filter @privileged
# Profilo seccomp JSON per Docker (estratto)
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "syscalls": [
    {
      "names": ["read", "write", "open", "close", "stat",
                "fstat", "lstat", "poll", "lseek", "mmap",
                "accept", "connect", "send", "recv" /* ... */],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

# Applicare un profilo custom a un container
docker run --security-opt seccomp=/path/to/profile.json myimage

# Disabilitare seccomp (per debug, mai in produzione)
docker run --security-opt seccomp=unconfined myimage

🔍 strace per scoprire le syscall

Per creare un profilo seccomp preciso, prima scopri quali syscall usa il tuo processo:

strace -c -f -S name nginx 2>&1 | tail -20

# Output: statistiche per syscall
% time     seconds  usecs/call     calls syscall
 32.54    0.001234          12       100 read
 28.12    0.001067          10       102 write
  8.45    0.000321           8        40 epoll_wait
  ...

📋 Quando usarlo

  • Container in produzione: sempre (Docker lo fa già)
  • Servizi systemd critici: aggiungi SystemCallFilter
  • Applicazioni untrusted: profilo restrictivo
  • Non serve per ogni processo — overhead di sviluppo non giustificato per tool interni
// 08

Container e sicurezza

Perché --privileged è la risposta sbagliata a ogni domanda

--privileged disabilita tutto docker run --privileged disabilita AppArmor, seccomp, e dà tutte le capabilities al container. Un container privilegiato può montare il filesystem dell'host, caricare moduli kernel, e fare praticamente tutto. Se qualcuno ti dice di usarlo "per farlo funzionare", è la soluzione sbagliata. Trova qual è la capability specifica di cui ha bisogno.
# Vedere le capability di un container in esecuzione
docker inspect mycontainer | jq '.[0].HostConfig.CapAdd'

# Aggiungere solo la capability necessaria
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx

# Container con profilo AppArmor custom
docker run --security-opt apparmor=my-nginx-profile nginx

# Vedere il profilo AppArmor di un container attivo
docker inspect mycontainer | jq '.[0].HostConfig.SecurityOpt'

🐳 Docker default security

  • ON seccomp profile (blocca ~44 syscall)
  • ON AppArmor profile (docker-default)
  • ON No new privileges
  • PARZIALE Capabilities ridotte (ma non zero)
  • OFF User namespace (default)
  • OFF rootless mode (default)

☸️ Kubernetes security context

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL
    add:
      - NET_BIND_SERVICE
  seccompProfile:
    type: RuntimeDefault

🔐 Rootless containers

Il modo più sicuro: fai girare il container daemon stesso come utente non-root. Podman lo fa di default. Docker richiede configurazione esplicita. Se il container runtime viene compromesso, non ha accesso root all'host.

# Podman: rootless di default
podman run nginx  # gira come utente normale

# Docker rootless setup
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix:///run/user/1000/docker.sock
docker run nginx
// 09

Debug delle violazioni

Quando qualcosa non parte e i log non ti dicono niente di utile

🔍 Leggere i log AppArmor

AppArmor logga tutto in syslog/journald e nel log di audit. Quando qualcosa non funziona, è lì che guardi prima.

# Log in tempo reale
sudo journalctl -f -k | grep -i apparmor

# Tutte le violazioni di oggi
sudo journalctl --since today | grep -i "apparmor.*denied"

# Log di audit (se auditd è installato)
sudo ausearch -m avc -ts recent

# Anatomia di una violazione:
apparmor="DENIED"           # azione intrapresa
operation="open"             # cosa stava facendo
profile="/usr/sbin/nginx"    # quale profilo ha negato
name="/etc/ssl/private/cert.key"  # cosa voleva accedere
pid=12345                    # processo
comm="nginx"                 # nome del comando
requested_mask="r"           # permesso richiesto
denied_mask="r"              # permesso negato

🚑 App non parte

# 1. Controlla lo stato del profilo
sudo aa-status | grep myapp

# 2. Metti temporaneamente in complain
sudo aa-complain /usr/bin/myapp

# 3. Avvia l'app e guarda i log
sudo journalctl -f | grep apparmor &
sudo systemctl start myapp

# 4. Aggiorna il profilo con aa-logprof
sudo aa-logprof

# 5. Torna in enforce
sudo aa-enforce /usr/bin/myapp

🔬 Diagnosi capability

# Errore "Operation not permitted"
# potrebbe essere una capability mancante

# Traccia le syscall fallite
strace -e trace=all myapp 2>&1 | grep EPERM

# Verifica le capability del processo
cat /proc/$(pgrep myapp)/status | grep Cap
capsh --decode=<hex-value>

# Controlla se serve una capability specifica
man 7 capabilities | grep -A3 "CAP_NET_BIND"
Tip: usa audit2allow (solo SELinux) o aa-logprof (AppArmor) Non scrivere le regole a mano interpretando i log — lascia che lo strumento le generi per te e poi revisiona quello che propone. La differenza è tra passare 5 minuti e passare un'ora.
// 10

Esempi reali

Profili completi per i servizi che usi davvero

🌐 Profilo nginx completo

# /etc/apparmor.d/usr.sbin.nginx #include <tunables/global> /usr/sbin/nginx { #include <abstractions/base> #include <abstractions/nameservice> #include <abstractions/ssl_certs> capability net_bind_service, capability setuid, capability setgid, capability dac_override, # Binario e moduli /usr/sbin/nginx mr, /usr/share/nginx/** r, /usr/lib/nginx/modules/** mr, # Config /etc/nginx/ r, /etc/nginx/** r, /etc/ssl/certs/** r, /etc/ssl/private/** r, # Web root (aggiungi i tuoi path) /var/www/** r, /srv/www/** r, # Log e PID /var/log/nginx/ rw, /var/log/nginx/** rw, /var/run/nginx.pid rw, /run/nginx.pid rw, # Tmp /var/lib/nginx/ rw, /var/lib/nginx/** rw, /tmp/nginx-* rw, # Network network inet stream, network inet6 stream, network unix stream, # Worker processes (inherit profile) /usr/sbin/nginx ix, }

🐍 Profilo applicazione Python/FastAPI

# /etc/apparmor.d/opt.myapp.run #include <tunables/global> /opt/myapp/venv/bin/python3 { #include <abstractions/base> #include <abstractions/python> #include <abstractions/nameservice> #include <abstractions/ssl_certs> capability net_bind_service, # App e virtualenv /opt/myapp/ r, /opt/myapp/** r, /opt/myapp/venv/** mr, # Config e data /etc/myapp/** r, /var/lib/myapp/ rw, /var/lib/myapp/** rw, /var/log/myapp/ rw, /var/log/myapp/** rw, # Tmp per uploads temporanei /tmp/myapp-uploads/ rw, /tmp/myapp-uploads/** rw, # Network (solo quello che serve) network inet stream, network inet6 stream, # Deny espliciti — non ha motivo di toccare queste deny /etc/shadow r, deny /etc/sudoers r, deny /root/** rw, deny /home/**/.ssh/** rw, }
# Unit file systemd con hardening completo
# /etc/systemd/system/myapp.service
[Unit]
Description=My FastAPI App
After=network.target

[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8080

# AppArmor profile (se definito)
AppArmorProfile=opt.myapp.run

# Capabilities
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes

# Filesystem isolation
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ReadWritePaths=/var/lib/myapp /var/log/myapp

# Syscall filter
SystemCallFilter=@system-service @network-io
SystemCallFilter=~@privileged ~@mount ~@reboot
SystemCallArchitectures=native

[Install]
WantedBy=multi-user.target
systemd-analyze security Dopo aver scritto l'unit file, esegui systemd-analyze security myapp.service. Ti dà un punteggio da 0 (massima sicurezza) a 10 (inutile). Punta sotto il 3.
// 11

AppArmor vs SELinux

Scegli la tua religione

Non devi scegliere tu La distro sceglie per te. Ubuntu/Debian = AppArmor. RHEL/CentOS/Fedora = SELinux. Non ha senso installare SELinux su Ubuntu o AppArmor su RHEL. Impara quello della tua distro.
FeatureAppArmorSELinux
Modello Path-based Label-based (inode)
Curva di apprendimento Ragionevole Ripida
Bypass con hardlink Possibile No
Bypass con bind mount Possibile No
Profili predefiniti Molti, facili da leggere Molto completi, complessi
Strumenti aa-genprof, aa-logprof audit2allow, semanage, restorecon
Dove è default Ubuntu, Debian, SUSE RHEL, Fedora, CentOS
Gestione container Profili per immagine Tipi/label per container

🚫 La risposta giusta a "SELinux mi da problemi"

Non è setenforce 0. Non è SELINUX=disabled in /etc/selinux/config. È audit2allow per capire cosa vuole il processo, e aggiungere la policy corretta.

# Vedere cosa ha bloccato SELinux
sudo ausearch -m avc -ts recent

# Generare la policy da aggiungere
sudo ausearch -m avc -ts recent | audit2allow -M mypolicy
sudo semodule -i mypolicy.pp

# Verificare il contesto SELinux di un file
ls -Z /etc/nginx/nginx.conf
system_u:object_r:httpd_config_t:s0 /etc/nginx/nginx.conf

# Ripristinare il contesto corretto
sudo restorecon -Rv /etc/nginx/
// 12

Toolbox

Comandi che usi ogni giorno, in un posto solo

🛡️ AppArmor cheatsheet

# Status
sudo aa-status
sudo apparmor_status

# Modalità
sudo aa-enforce /path/to/binary
sudo aa-complain /path/to/binary
sudo aa-disable /path/to/binary

# Profili
sudo aa-genprof /path/to/binary
sudo aa-logprof
sudo apparmor_parser -r /etc/apparmor.d/profilo
sudo apparmor_parser -R /etc/apparmor.d/profilo

# Log
sudo journalctl -f | grep apparmor
sudo dmesg | grep apparmor

Capabilities cheatsheet

# Leggere
getcap /path/to/binary
getcap -r / 2>/dev/null
cat /proc/PID/status | grep Cap
capsh --print
capsh --decode=HEX

# Impostare
sudo setcap cap_net_bind_service=ep /bin/myapp
sudo setcap -r /bin/myapp

# SUID check
find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null

🔧 seccomp / systemd hardening

# Analisi sicurezza unit
systemd-analyze security myapp.service

# Syscall di un preset
systemd-analyze syscall-filter @privileged

# Docker security
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE img
docker run --security-opt apparmor=profile img
docker run --security-opt seccomp=profile.json img
docker inspect container | jq '.[0].HostConfig'

🔍 Debug e audit

# Syscall usate da un processo
strace -c -f -S name /usr/bin/myapp 2>&1

# Permessi negati SELinux
sudo ausearch -m avc -ts recent
sudo audit2allow -a -M mypol
sudo semodule -i mypol.pp

# Seccomp violations (bpf)
sudo dmesg | grep "seccomp"
sudo journalctl | grep "SECCOMP"

📚 Letture utili

  • /usr/share/doc/apparmor/ — documentazione locale
  • /etc/apparmor.d/ — profili di sistema come riferimento
  • man apparmor.d — sintassi completa dei profili
  • man 7 capabilities — lista completa delle capability Linux
  • man 2 seccomp — interfaccia seccomp del kernel
  • systemd.exec(5) — tutte le direttive di hardening systemd
La regola dei tre livelli Per ogni servizio in produzione, applica almeno: 1. User non-root + capabilities minime nell'unit file 2. systemd hardening (NoNewPrivileges, ProtectSystem, PrivateTmp) 3. AppArmor profile se il servizio è esposto su rete Costa un'ora la prima volta. Poi diventa automatico.
🔧 Genera profili AppArmor senza scrivere tutto a mano netforge.it include un generatore di profili AppArmor: seleziona il servizio, scegli capabilities e regole file, ottieni il profilo pronto da copiare in /etc/apparmor.d/. Firewall, VPN e certificati nello stesso posto.