Con le funzionalità di Linux, suddivido i diritti di root in privilegi piccoli e chiaramente definiti, riducendo così drasticamente il rischio. In questo modo controllo in modo mirato quali processi possono eseguire azioni particolari e limito la superficie di attacco di ogni applicazione.
Punti centrali
- A grana fine anziché essere onnipotenti: suddividere i diritti di root in privilegi più specifici.
- Funzionalità dei file anziché Set-UID: associare i diritti necessari direttamente ai file binari.
- Insiemi di capacità Configurare in modo mirato: Permitted, Effective, Inheritable, Bounding.
- Separazione dei privilegi: Separare rigorosamente servizi, strumenti e attività.
- La difesa in profondità: Integrare le funzionalità con sudo, i ruoli e i protocolli.
Perché separare i privilegi di root?
Un account root consente Accesso completo a livello di file e processi, ma è proprio questo che può portare a errori dalle gravi conseguenze. Basta un comando errato o un exploit e l’intera installazione va in tilt. Limito quindi le operazioni di ampia portata allo stretto necessario, riducendo così l’entità dei danni e i tempi di ripristino. Il principio dei privilegi minimi mantiene i servizi snelli e controllabili. Disattivo l’accesso diretto come root, mi affido ai ruoli e registro log completi.
Le funzionalità di Linux spiegate in breve
Le "capabilities" di Linux suddividono i classici privilegi di root in categorie ben definite Privilegi. Ogni processo riceve solo i moduli di cui ha effettivamente bisogno per svolgere il proprio compito, come ad esempio il binding alle porte inferiori a 1024 o l’invio di segnali specifici. In questo modo si evita il precedente approccio “tutto o niente”. Il kernel gestisce questi moduli per ogni singolo processo e li applica in modo rigoroso. In questo modo il controllo rimane granulare e tracciabile.
Dal punto di vista tecnico, collego le competenze a Processi (tramite i loro set di capacità) oppure a File (come attributi estesi security.capability ai file binari ELF). Nel caso del execve()All'avvio, il kernel unisce le capacità dei file con i set dei processi: in parole povere, le capacità consentite derivanti dall'attributo del file, insieme ai diritti ereditabili dal processo chiamante, vengono combinate nel nuovo set "Permitted" e – se contrassegnate come tali – attivate contemporaneamente nel set "Effective". Ciò evita le deviazioni Set-UID e mantiene i privilegi visibili e verificabili.
Comprendere i set di capacità nel contesto dei processi
Ogni processo dispone di diverse categorie di diritti, che io utilizzo in modo mirato controllo. Il Permitted-Set definisce ciò che un processo può possedere in linea di principio. L'Effective-Set stabilisce ciò che è attualmente attivo. L'Inheritable-Set regola quali privilegi possono essere trasferiti ai processi figli. Il Bounding-Set fissa un limite massimo rigido e impedisce ai processi di superarlo.
Funzionalità ambientali e Securebits
Oltre ai set già noti, c'è il Set ambient, che presso il execve() non scade automaticamente. Lo utilizzo quando un processo senza privilegi necessita in modo mirato di diritti minimi su più eseguire-dovrebbe ignorare i livelli (ad esempio quando si richiamano programmi di supporto esterni). I diritti "ambient" vengono inclusi nei diritti effettivi solo se il file richiamato non imposta a sua volta alcuna "file capability": in questo modo evito un'escalation indesiderata.
Con i Securebits gestisco i dettagli delle transizioni, ad esempio se un processo, dopo il cambio di UID, possa mantenere le capacità precedentemente impostate (keepcaps) oppure se, in linea di principio, non gli sia consentito ottenere nuovi privilegi (no_new_privs). Nella pratica, impiego Securebits in modo rigoroso e rinuncio alla comodità per interrompere le catene di exploit.
File Capabilities al posto di Set-UID
Sostituisco i file binari con Set-UID con le funzionalità dei file per ridurre il rischio di abbassare. Anziché assegnare i privilegi di root a un programma, mi limito a impostare i diritti necessari. Una modifica tipica si presenta così: setcap 'cap_net_bind_service=+ep' /usr/bin/meinserver. Con getcap -r / verifico quali file contengono le competenze. Ciò riduce sensibilmente i percorsi di escalation.
È importante che le funzionalità dei file si applichino solo a File binari ELF funzionano. Gli script dell’interprete (ad es. Python, Bash) non li ereditano in modo affidabile. In questi casi, incapsulo l’azione privilegiata in un piccolo programma di supporto verificato staticamente oppure utilizzo la socket activation, in modo che il mio servizio non debba effettuare il binding autonomamente. Inoltre, tengo sotto controllo i permessi dei file: le capabilities conferiscono diritti speciali nei confronti del kernel, ma sostituiscono nessuno ACL standard o permessi POSIX.
Durante la copia o la compressione, le competenze si perdono rapidamente: cp senza supporto XATTR, impostazione errata umask oppure rimuovere un artefatto di build da un file system privo di attributi estesi security.capability senza dirlo. Per questo motivo lavoro in modo riproducibile e utilizzo: cp --preserve=xattr ..., tar --xattrs, rsync -X. Nelle build dei pacchetti imposto esplicitamente le funzionalità dei file nello script di installazione, provo l'installazione in una macchina virtuale pulita e verifico getcap nel CI.
Separazione dei privilegi con scenari realistici
Un server web necessita dell'accesso alle porte 80/443, ma non ai moduli del kernel né ai riavvii del sistema, pertanto imposto CAP_NET_BIND_SERVICE e nient’altro. Un agente di backup può leggere e scrivere file, ma non può modificare la configurazione di rete. Uno strumento di monitoraggio dispone di diritti di lettura sugli indicatori, ma non ha diritti di modifica. Queste limitazioni confinano gli attacchi a livello locale, impedendo loro di avere ripercussioni su tutto il sistema. È proprio questa separazione a rendere i servizi gestibili e a tenere sotto controllo le configurazioni errate.
Combinare con sudo e i ruoli
Le funzionalità non sostituiscono un codice pulito Struttura dei ruoli, le integrano. Concedo i diritti sudo con molta cautela, utilizzo percorsi di comando completi e mi tengo alla larga da regole generiche come „ALL=(ALL) ALL“. Ogni autorizzazione viene registrata. I gruppi raggruppano le responsabilità, mentre le capacità stabiliscono limiti tecnici nei processi. In questo modo si creano competenze chiare senza privilegi eccessivi.
Insidie comuni e buone pratiche
- Non utilizzare CAP_SYS_ADMIN come abbreviazione: Questo diritto è un concetto generico. Lo sostituisco con alternative più specifiche (ad es.
CAP_SYS_CHROOT,CAP_SYS_TIME,CAP_SYS_NICE) oppure rinuncia del tutto. - I diritti sui file rimangono rigorosamente in vigore: Le funzionalità non disattivano il DAC in modo generale. Senza
CAP_DAC_OVERRIDEIl kernel continua a rispettare i bit di proprietà e di modalità. Continuo quindi ad assegnare i diritti di lettura in misura minima. - Indurimento del percorso: Se assegno delle funzionalità di file a un file binario, impedisco lo spoofing del PATH (percorsi assoluti in
sudoers, diritti di scrittura disabilitati sulle directory presenti nel percorso di ricerca). - Pubblica presto, pubblica spesso: I processi potrebbero avviarsi con più diritti del necessario. Rimuovo le autorizzazioni superflue subito dopo l'operazione delicata (
prctl()/libcap) e impostano_new_privs, ove possibile. - Limitare l'ereditarietà: Mantengo ridotti i set “Inheritable” e “Ambient”. I processi figli non devono aprire nuove porte.
- Verifica della pipeline di compilazione e distribuzione: Confermo che
security.capabilityvenga mantenuto e che nessuna fase di staging (livelli dei container, NFS, scanner di artefatti) rimuova gli XATTR.
Panoramica delle principali capacità e dei rischi
Prima di assegnare i privilegi, definisco chiaramente quelli necessari e ne valuto il rischio. La tabella seguente mostra alcuni esempi tipici con le relative conseguenze e classificazione. Prendo sempre in considerazione delle alternative per evitare di concedere diritti eccessivi. Soprattutto CAP_SYS_ADMIN Li concedo con estrema parsimonia. Ove possibile, sostituisco i privilegi di ampia portata con varianti mirate e circoscritte.
| Capacità | Scopo | Il rischio | Esempio |
|---|---|---|---|
| CAP_NET_BIND_SERVICE | Assegnare le porte inferiori a 1024 | Da basso a medio | Server web sulle porte 80/443 |
| CAP_SYS_BOOT | Riavvia il sistema | Alto | Riavvio programmato |
| CAP_SYS_MODULE | Caricare/rimuovere i moduli del kernel | Molto alto | Gestione dei driver |
| CAP_SYS_ADMIN | Operazioni amministrative versatili | Molto alto | Varie attività di manutenzione |
| CAP_SETUID / CAP_SETGID | Cambia UID/GID | Medio-alto | Cambio di mansioni durante il servizio |
Oltre alla tabella, sto valutando proprio ora CAP_SYS_PTRACE (debug dei processi), CAP_NET_ADMIN (parametrizzazione della rete) e CAP_DAC_OVERRIDE (aggirare le restrizioni di accesso ai file) è una pratica estremamente criticabile. Spesso esistono modelli che consentono di evitare tali restrizioni: endpoint dedicati per le metriche anziché lo “snooping” dei processi, l’attivazione dei socket o il reindirizzamento delle porte anziché i diritti di bind e diritti sui file ben definiti anziché un aggiramento generico del DAC.
Hardening nei container e nell'hosting
Negli ambienti multi-tenant ritengo che le competenze debbano essere radicalmente piccolo e impedisco l'ereditarietà nei processi figli. I container traggono notevoli vantaggi non appena il bounding set è ben definito. Combino questo approccio con spazi isolati per il filesystem e i processi. Per avere una panoramica degli approcci di isolamento, trovo utile questa introduzione a Isolamento dei processi. In questo modo i servizi rimangono separati, anche se un'applicazione presenta dei problemi.
In pratica, imposto i contenitori di default su „rimuovi tutto, aggiungi in modo selettivo“: --cap-drop=ALL --cap-add=NET_BIND_SERVICE per i servizi web, nessun diritto di montaggio, nessun SYS_ADMIN. Negli ambienti orchestrati mantengo il profilo in primo piano e lo verifico nelle policy. Importante: non mi affido alle funzionalità dei file presenti nell'immagine, ma assegno i diritti in fase di esecuzione nell'Orchestrator – in modo riproducibile e verificabile.
Interazione con SELinux e AppArmor
Le funzionalità determinano ciò che un processo è autorizzato a fare, mentre i profili MAC definiscono a cosa può accedere, e le due cose sono in armonia tra loro buono. Definisco le capabilities in modo restrittivo e lascio che SELinux o AppArmor limitino l'accesso a file e socket. In questo modo si crea una protezione a più livelli che pone diversi ostacoli agli exploit. Trovo un rapido confronto qui: SELinux vs. AppArmor. In questo modo, un servizio compromesso rimane isolato e può causare meno danni.
Pratica: procedere passo dopo passo
Inizierò con un'analisi della situazione attuale di tutti i servizi e delle loro Requisiti. Successivamente, rimuovo i file binari Set-UID non necessari o li sostituisco con file capabilities mirate. Configuro `sudo` in modo restrittivo e documento ogni voce. Assegno i compiti a ruoli e gruppi, mantenendo i diritti al minimo. Successivamente eseguo dei test sotto carico e controllo le voci di log alla ricerca di rifiuti imprevisti.
Una breve lista di controllo mi aiuta nella transizione:
- Definire per iscritto i requisiti per ciascun servizio (solo ciò che è realmente necessario).
- Fare un inventario dei diritti speciali esistenti (
find / -perm -4000,getcap -r /). - Sostituire in modo mirato: eliminare il Set-UID, impostare le funzionalità dei file, revocare i diritti tempestivamente.
- Chiudere le ereditazioni: ottimizzare il bounding set, ridurre al minimo Inheritable/Ambient.
- Proteggere i profili systemd/container (
CapabilityBoundingSet=,NoNewPrivileges=yes). - Eseguire test sotto carico, verificare i log e le voci di audit, documentare le eccezioni.
Monitoraggio, spazi dei nomi e audit continui
Controllo i file di log, gli allarmi e le chiamate di sistema, in modo che le azioni indesiderate vengano immediatamente distinguersi. Verifico regolarmente le modifiche apportate alle funzionalità, alle regole di sudo e ai ruoli. Laddove opportuno, isolo ulteriormente i carichi di lavoro tramite i meccanismi di isolamento del kernel. Questa panoramica offre un buon punto di partenza per Spazi dei nomi e cgroup. In questo modo riesco a individuare tempestivamente eventuali anomalie e a mantenere pulito l'ambiente circostante.
Nella vita di tutti i giorni ricorro a semplici verifiche: capsh --print mi mostra l'attuale set di abilità, getpcaps elenca i diritti processuali e in /proc//status leggo CapEff, CapPrm, CapBnd. Con auditd tengo traccia delle modifiche allo stato delle capacità (ad es. regola su capset), metto in correlazione gli eventi con le distribuzioni e imposto degli allarmi qualora dovessero comparire improvvisamente diritti di accesso estesi. Nei casi più complessi, mi aiuta strace -e capget,capset, per rendere visibili le manipolazioni dei diritti.
Esempi pratici su systemd e container
Gestisco molti servizi come unità systemd e vi incapsulo i permessi:
CapabilityBoundingSet=CAP_NET_BIND_SERVICEriduce la finestra dei diritti accessibili allo stretto necessario.AmbientCapabilities=CAP_NET_BIND_SERVICEconferisce al servizio il diritto di collegarsi alle porte 80/443 senza file capabilities.NoNewPrivileges=yesimpedisce successive estensioni dei diritti.Utente=,Gruppo=,ProtectSystem=strict,PrivateTmp=yescompletano l'isolamento.
Nei container avvio i processi in modo il più snello possibile: docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --read-only. Per i processi di breve durata utilizzo le funzionalità di durata anziché quelle relative ai file nell'immagine, in modo che le build rimangano riproducibili e i diritti siano legati all'ambiente.
Esempi concreti di migrazione tratti dalla pratica
- ping senza Set-UID: Invece
setuid rootmettosetcap 'cap_net_raw=+ep' /bin/ping. In questo modo ogni utente può aprire socket ICMP senza disporre dei pieni diritti di root. Controllo regolarmente congetcap /bin/ping, se l'attributo è stato conservato. - Servizio web sulle porte 80/443: Eseguo il mio servizio come utente senza privilegi e inserisco solo
cap_net_bind_service. Se il servizio è comunque collegato a un proxy inverso, in alternativa posso anche collegarlo alle porte 80/443 e utilizzare internamente una porta alta, senza bisogno di competenze aggiuntive. - Cambio di parti nel procedimento: Per gli strumenti che richiedono temporaneamente privilegi più elevati (ad esempio per impostare i livelli di priorità), imposto
cap_sys_nice, esegui l'azione in anticipo e poi disattiva l'abilità. Evito di mantenere i privilegi aumentati in modo permanente.
Limiti e alternative
Non tutti i casi d'uso richiedono l'uso delle funzionalità. Spesso esistono alternative sicure che comportano un rischio minore:
- Attivazione della presa: Il servizio di avvio (ad es. systemd) apre dei socket con privilegi e li passa al processo. In questo modo, il mio servizio non ha bisogno dei diritti di bind.
- Reindirizzamento delle porte: Tramite le regole del firewall reindirizzo le porte 80 e 443 su una porta alta. Il servizio rimane senza privilegi e il comportamento del sistema non subisce alcuna modifica.
- Porte low non privilegiate: Laddove opportuno, posso aumentare la soglia per le porte non privilegiate. Ciò, però, amplia il margine di manovra per tutti i processi: valuto attentamente il rapporto tra rischio e praticità.
- Piccoli aiutanti anziché tuttofare: Meglio un file binario minuscolo e verificato con una sola funzionalità che un enorme monolite con un ampio catalogo di diritti.
Riassumendo brevemente
Con Funzionalità di Linux Suddivido i poteri di root in privilegi più piccoli e facilmente gestibili. Le “file capabilities” sostituiscono i rischiosi binari Set-UID e riducono le conseguenze di un attacco. In combinazione con regole sudo rigorose, ruoli e profili MAC, si crea una protezione a più livelli con confini ben definiti. I set «Bounding» e «Inheritable» limitano l’ereditarietà e mantengono i processi in linea con le direttive. Chi adotta questo approccio riduce sensibilmente la superficie di attacco e mantiene gestibile il carico amministrativo.


