chroot fallisce su OpenVPN: regola AppArmor o capability?
Aggiungere un percorso a un profilo AppArmor non concede mai CAP_SYS_CHROOT, e un chroot negato può derivare da tre livelli separati. Stabilisci quale modificare.
Il daemon registra un fallimento di chroot e il profilo sembra già generoso, quindi la correzione tentatrice è allargare una regola di percorso e riavviare. Di solito non cambia nulla, perché chroot è controllato da un privilegio che il profilo non è mai stato in grado di concedere. Decidere quale livello toccare richiede tre controlli economici: cosa al processo è consentito trattenere, cosa la politica permette e cosa il lanciatore ha rimosso prima che entrambi contassero.
Due cancelli separati, e solo uno appartiene ad AppArmor
chroot richiede CAP_SYS_CHROOT nello spazio dei nomi utente in cui gira il processo. È un privilegio Linux convenzionale, verificato dal kernel prima che la chiamata faccia qualcosa con i percorsi. Separatamente, un profilo AppArmor in applicazione può negare la capability anche quando il processo la possiede, perché la politica obbligatoria viene consultata in aggiunta alle credenziali, non al loro posto. La distinzione conta in pratica: i due livelli vivono in file diversi e di solito li mantengono persone diverse.
Quei due cancelli falliscono con lo stesso errno. Entrambi producono EPERM, ed entrambi si presentano come un generico reclamo di permessi nel log del daemon, ed è per questo che indovinare il contenuto del profilo spreca tanto tempo. La regola di percorso che stavi per aggiungere controlla l'accesso ai file dopo che chroot riesce; non ha alcun peso sul fatto che l'operazione sia permessa.
Un terzo cancello sta a monte di entrambi. Se il gestore del servizio o un wrapper hanno avviato il processo senza la capability nel loro insieme limite, nessuna modifica ad AppArmor può rimetterla, e aggiungere capability al profilo documenta solo un'intenzione che il kernel non onorerà.
Leggi lo stato dei privilegi prima di modificare la politica
Parti dal processo in esecuzione, non dalla configurazione che credi attiva. Il file di stato espone cinque maschere di capability e il flag no-new-privileges, e rispondono a domande diverse:
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpnCapEff è l'insieme che conta per la chiamata: una capability presente in CapBnd ma assente in CapEff è permessa dal tetto e non è attualmente detenuta. CapAmb conta quando il binario non è privilegiato nel senso usuale di setuid, perché le capability ambientali sono il modo in cui un servizio conserva un privilegio attraverso exec senza capability di file. CapInh contiene bit che potrebbero essere ereditati attraverso exec, ma solo quando l'insieme ereditabile del file eseguito lo consente, quindi un bit lì non concede nulla da solo.
Poi conferma quale profilo è davvero agganciato. Un processo può essere confinato da un nome di profilo che non ti aspettavi, soprattutto quando una distribuzione spedisce insieme il proprio file di unit e il proprio profilo, e leggere /proc/PID/attr/current lo risolve in una riga. Confrontare il file che hai modificato con il profilo che il kernel riporta è la differenza tra correggere la causa e modificare un file che nessuno carica.
Aggiungi la regola di capability solo quando il percorso di audit lo dice
I record del kernel e dell'audit nominano il livello che ha rifiutato. Cerca un diniego che porti l'operazione capable e il nome sys_chroot, insieme al profilo che l'ha prodotto. Se quel record esiste, il processo aveva già la capability e AppArmor l'ha rifiutata, e la modifica mirata è una regola di capability dentro il profilo reale:
capability sys_chroot,Tienila come frammento nel profilo che è realmente agganciato, e verifica la parola chiave contro il riferimento ai profili apparmor.d invece di copiare una regola da un tutorial non correlato. Inserirla in un include condiviso la concede a ogni programma confinato che tira dentro quel file, e passare il profilo in modalità complain nasconde il diniego successivo invece di risolvere questo. La regola del profilo rimuove un diniego di politica; non fabbrica privilegi, quindi un processo che non ha mai avuto CAP_SYS_CHROOT nel suo insieme effettivo fallirà comunque con la regola presente.
Se il record di audit non mostra alcun diniego di AppArmor, smetti di modificare il profilo. Stai guardando un problema di credenziali, e il lavoro restante è nel file di unit o nel processo genitore, non nel linguaggio di politica.
Il gestore del servizio tiene un proprio tetto
systemd imposta un insieme limite di capability per ogni servizio, e per impostazione predefinita quell'insieme è stretto. Qualunque cosa sia esclusa lì non è disponibile al processo, qualunque cosa dica il profilo, ed è per questo che le unit indurite possono fallire un'operazione che funzionava quando il daemon veniva avviato a mano. Le direttive pertinenti sono facili da leggere nell'unit e facili da trascurare quando l'applicazione segnala il guasto. I drop-in spediti da un pacchetto di distribuzione sono la fonte abituale di un tetto che nessuno ricorda di aver configurato.
Alzare il tetto e concedere la capability sono due modifiche, non una. Una voce dell'insieme limite dice che il processo può detenere la capability; non la aggiunge all'insieme effettivo, e NoNewPrivs può impedire che una successiva elevazione riesca anche quando tutto il resto combacia. Aggiungi solo ciò che serve all'operazione, mantieni intatto il resto dell'indurimento, e resisti alla scorciatoia di concedere ogni capability per far passare una chiamata.
Anche l'ordine conta. Cambia prima il tetto dell'unit, verifica che CapEff contenga ora il bit, e solo allora decidi se AppArmor nega ancora. Fare entrambe le modifiche insieme ti lascia di nuovo a indovinare se il guasto persiste.
Dopo che il diniego è sparito, controlla la prigione stessa
Un chroot riuscito sposta la radice, quindi ogni percorso che il daemon tocca dopo viene risolto dentro il nuovo albero. Configurazione mancante, un nodo di dispositivo assente o una directory di certificati non copiata appariranno ora come un errore di file, e quell'errore è un problema di disposizione, non di capability. Leggi l'evento successivo invece di aggiungere un'altra autorizzazione ampia. Lo spostamento cambia anche il significato di un percorso relativo, ed è per questo che le guide di indurimento insistono sui percorsi assoluti nella configurazione del daemon.
Aiuta anche ricordare cosa chroot non fa. È un confine di percorso, non una sandbox: un processo che conserva il privilegio può uscirne, ed è per questo che la capability viene di solito lasciata cadere dopo la chiamata e che il confinamento deve restare intorno al daemon. Tenere insieme il profilo AppArmor, l'insieme limite delle capability e le restrizioni del filesystem è lo scopo dell'esercizio, non un residuo da rimuovere quando il servizio parte.
La regola di decisione è breve. Se il record di audit nomina AppArmor, aggiungi una regola di capability al profilo agganciato. Se l'insieme effettivo è vuoto, correggi il tetto dell'unit e l'ordine di rilascio del privilegio. Se il profilo non è quello che hai modificato, smetti di modificare e trova quello vero.