Ho impostato capacità di Linux per gestire i servizi del server secondo il principio della minimalità e assegnare così solo i diritti parziali strettamente necessari. In questo modo riduco il Superficie di attacco in modo percettibile, senza bloccare alcuna funzione.
Punti centrali
- Privilegio minimo In modo coerente: ai servizi vengono assegnate solo le competenze strettamente necessarie.
- A grana fine Al posto di Root: circa 40-50 capabilities sostituiscono l'accesso completo.
- Separazione dei processi: la separazione dei privilegi riduce i danni causati dagli exploit.
- File Funzionalità: associare i diritti direttamente ai file binari.
- Verificabile: getcap offre una visione chiara dei privilegi speciali.
Perché il root è rischioso – e come le Capabilities cambiano le cose
In passato, quasi tutti i servizi server funzionavano con Diritti di radice, il che, in caso di compromissione, avrebbe potuto portare immediatamente al controllo del sistema. Oggi distribuisco i diritti in modo mirato, utilizzando funzionalità come CAP_NET_BIND_SERVICE assegnare per le porte inferiori a 1024 e rimuovere tutti gli altri diritti avanzati. In questo modo il server web potrà effettuare il binding, ma non caricare moduli del kernel né modificare i proprietari dei file, il che Sicurezza aumenta notevolmente. Una chiara separazione dei compiti rende gli attacchi meno efficaci, poiché un processo compromesso può eseguire solo azioni limitate. Chi desidera strutturare meglio questo concetto può definire i diritti in modo molto preciso suddividere in modo granulare limitando così sistematicamente le operazioni critiche. Da un servizio root monolitico si ottiene così un insieme di servizi con autorizzazioni minime e chiaramente definite.
Ecco come funzionano i set di capacità nel kernel
Ogni processo presenta diversi Insiemi di capacità, che il kernel verifica in caso di operazioni sensibili. L’Effective-Set determina ciò che un processo è autorizzato a fare immediatamente, mentre il Permitted-Set contiene l’insieme dei diritti possibili. Tramite l’Inheritable-Set posso controllare ciò che, in caso di execve() viene trasferito ai processi figli, il che è particolarmente importante nel caso dei wrapper e degli script di avvio. Il Bounding-Set definisce un limite massimo rigido, in modo che determinate capacità non possano mai più essere acquisite, nemmeno in caso di errori nell’applicazione. Con l’Ambient-Set assegno le capacità senza SUID ai programmi normali e mantengo il Percorso di attacco piccoli. Questi set, nel loro insieme, mi consentono un controllo molto preciso, che va ben oltre il classico “tutto o niente” dell’UID 0.
| Set | Scopo | Utilizzo tipico | Rischio di errata configurazione |
|---|---|---|---|
| Efficace | Abilità ora attive | Verifica di ogni operazione privilegiata | Il processo può diventare subito troppo |
| Consentito | Insieme delle abilità consentite | Fonte dell'Effective Set | Le riserve superflue rimangono a disposizione |
| Ereditabile | Abilità ereditarie | Trasferimento controllato con execve() | I bambini ereditano diritti senza motivo |
| Bounding | Limite massimo di tutti i diritti | Definire le esclusioni permanenti | È possibile recuperare diritti importanti |
| Ambient | Condivisione senza SUID | I programmi regolari ricevono le funzionalità | Un'assegnazione dei diritti più ampia e discreta |
Nella pratica sono importanti due aspetti aggiuntivi: in primo luogo, è fondamentale Securebits se un processo, dopo una modifica dell'ID utente (ad es. tramite setuid()) mantenga le sue funzionalità. Con PR_SET_KEEPCAPS è possibile gestirlo in modo mirato – procedura tipica: avviare temporaneamente come root, creare i socket o le risorse necessarie, passare a un utente senza privilegi e mantenere solo le capacità necessarie. In secondo luogo, vale quanto segue: il Insieme di delimitazione è definitivamente integrato nell'attuale flusso di processo. Chi rimuove fin dall'inizio, nel percorso di avvio, le funzionalità superflue, non potrà più acquisire diritti „non consentiti“ in seguito, nemmeno a causa di configurazioni errate.
Gestire i diritti sui file tramite le funzionalità dei file
Invece di un servizio, permanente Diritti speciali per concederlo, preferisco collegarlo direttamente al file binario. Tramite setcap cap_net_bind_service=+eip /usr/bin/node consento i port binding senza che il processo debba essere eseguito come root. Con getcap /usr/bin/node oppure in modo ricorsivo getcap -r / 2>/dev/null verifico l'assegnazione e mantengo il controllo. La rimozione avviene tramite setcap -r /percorso/del/binario, per cui revoco i diritti temporanei al termine dell'operazione. Durante la copia, spesso le capabilities vanno perse, pertanto le salvo esplicitamente durante la distribuzione per Regressioni da evitare. In questo modo le build rimangono riproducibili e i diritti sono sempre documentati in modo tracciabile.
Le funzionalità dei file sono implementate come attributi estesi (security.capability) sul file system. Ciò richiede un file system compatibile e opzioni di montaggio adeguate. Strumenti come catrame e rsync è necessario includere esplicitamente gli XAttr (ad es. tar --xattrs, rsync -XA), altrimenti i diritti scompaiono silenziosamente. I gestori di pacchetti possono impostare le capacità nelle fasi successive all’installazione; preferisco definirle nel processo di compilazione/rilascio, per evitare sorprese durante gli aggiornamenti. Un altro aspetto critico è che gli script dell’interprete (ad es. con shebang) non ereditano le capacità dei file come i binari ELF. Capacità potenti su interprete È comunque rischioso – preferisco disaccoppiare il tutto e lavorare con piccoli file binari di supporto dedicati.
Il principio di minimalità applicato ai servizi server nella pratica
Avvio il server web come utente senza privilegi e concedo esclusivamente CAP_NET_BIND_SERVICE, affinché il processo possa collegarsi alla porta 80/443 e non ne vengano aperte altre Privilegi offre. Continuo a gestire file e directory tramite i permessi POSIX e, facoltativamente, i profili MAC, garantendo così che la configurazione e i contenuti rimangano protetti separatamente. Agli agenti di monitoraggio o di registrazione vengono assegnati diritti di rete mirati e diritti di lettura sui log, ma nessuna autorizzazione ad apportare modifiche al sistema. Negli ambienti containerizzati riduco ulteriormente l’insieme delle capability e lo combino con filtri delle chiamate di sistema per limitare il comportamento. Questa combinazione riduce l’impatto degli exploit riusciti e aumenta la Trasparenza dei poteri effettivi. I servizi continuano a funzionare, ma il margine di manovra rimane limitato.
Anziché assegnare delle “capabilities”, a volte le elimino del tutto: l’attivazione del socket mette a disposizione listener privilegiati (ad es. 443/tcp) tramite il processo Init e passa al servizio solo il descrittore di file aperto. Il processo dell’applicazione non necessita quindi di alcun CAP_NET_BIND_SERVICE altro. Allo stesso modo, è possibile eseguire in anticipo operazioni da root una tantum (ad esempio, creare la directory PID) e poi rinunciare sistematicamente ai privilegi. Meno privilegi in generale sono coinvolti, tanto più il sistema risulta resistente agli errori a catena.
Attuare correttamente la separazione dei privilegi
Suddivido i servizi di ampia portata in più Sottoprocessi, ciascuno dei quali dispone solo delle funzionalità necessarie. Un processo front-end gestisce il protocollo TLS e si collega alle porte, ma non dispone dei diritti sul file system necessari per apportare modifiche critiche. Un processo di backend elabora i dati internamente, dispone di diritti di lettura minimi sulla configurazione e comunica con i database senza capacità di rete proprie. Le attività amministrative, come la rotazione dei log o la manutenzione, vengono eseguite tramite strumenti dedicati con capacità limitate nel tempo. Se un aggressore attacca una parte del sistema, il resto rimane indenne perché il Autorizzazioni sono definiti in modo rigoroso. In questo modo, la sicurezza si adatta alla struttura dell'applicazione anziché basarsi su diritti di sistema onnipotenti.
Per questa suddivisione è opportuno ricorrere a una chiara orchestrazione iniziale. Nelle configurazioni classiche questo compito è affidato a un supervisor; sui sistemi odierni preferisco utilizzare systemd, poiché integra direttamente capabilities, cgroups e namespaces. In questo modo posso avviare il frontend di rete, i worker e gli strumenti di amministrazione ciascuno con la propria sandbox, limitare le risorse e, in caso di errore, farli riavviare automaticamente – senza mai concedere diritti di root in modo indiscriminato.
Combinare i controlli di sicurezza: POSIX, MAC e Capabilities
Le funzionalità danno il meglio di sé quando le abbino ai classici Diritti sui file e sui sistemi MAC. SELinux o AppArmor possono limitare ulteriormente le azioni nonostante le capacità assegnate, creando così una protezione multipla. Ad esempio, un processo può sì connettersi a una porta, ma una policy gli impedisce di leggere file sensibili. Chi desidera approfondire la differenza tra questi approcci troverà un chiaro confronto in SELinux vs. AppArmor e può quindi scegliere una strategia di policy adeguata. Nel complesso, si crea un sistema di difesa integrato in grado di bloccare gli attacchi su più livelli e di Superficie di attacco ulteriormente ridotto. L'assegnazione dei diritti rimane così verificabile, ripetibile e coerente.
La situazione diventa particolarmente restrittiva quando, in aggiunta, NoNewPrivileges Attiva: in questo modo i processi e i processi figlio non potranno acquisire nuovi privilegi (ad esempio tramite SUID o nuove capacità dei file impostate). In combinazione con una lista rigida di limitazione delle capacità, si crea una barriera di sicurezza che impedisce l'estensione dei privilegi anche in caso di configurazione errata.
Distribuire e verificare in modo sicuro le funzionalità
Ritengo che il Set di abilità il più piccolo possibile ed evita tutto ciò che suona come un „secondo root“, ad esempio CAP_SYS_ADMIN. Interpreti come Python, Perl o le shell non dispongono di funzionalità avanzate, poiché le loro funzionalità possono essere facilmente utilizzate in modo improprio. Grazie a verifiche periodiche tramite getcap -r / 2>/dev/null individuo eventuali anomalie e le risolvo. I file binari con capabilities sono in sola lettura, appartengono a root e non si trovano in percorsi che gli utenti normali possono modificare. Inoltre, controllo i miei binari prima di ogni rilascio e documento le modifiche, in modo che Recensione e la riproduzione avvengano in modo affidabile. In questo modo la concessione dei diritti rimane gestibile e le modifiche restano tracciabili.
Durante l'esecuzione, controllo i processi tramite /proc//status (Campi CapEff, CapPrm, CapInh). Questo fornisce i valori esadecimali dei set attivi e mostra immediatamente se un'applicazione è in grado di fare più di quanto previsto. Strumenti come capsh --print oppure getpcaps facilitano il debug. Con il sottosistema di audit di Linux registro inoltre le modifiche alle capacità o a security.capability-Gli attributi dei file, per tracciare eventuali manipolazioni. Chi considera le capacità come oggetti di configurazione e sottopone le modifiche a un rigoroso controllo rende gli audit riproducibili e semplifica la dimostrazione della conformità.
Ostacoli frequenti e come evitarli
Una trappola tipica: quando si copia Attributi perdute, con la conseguenza che i servizi improvvisamente non si avviano più o, al contrario, non sono sufficientemente limitati. Pertanto, salvo esplicitamente le capabilities nella build oppure le assegno in modo automatizzato nella fase post-installazione. Un altro errore è l’uso eccessivo di capabilities generiche, che aprono più di quanto sia necessario. È preferibile utilizzare capabilities specifiche come CAP_NET_RAW oppure CAP_CHOWN da utilizzare solo dove svolgono una funzione concreta. Anche l’Ambient-Set lo uso con parsimonia, in modo da evitare che si verifichino effetti indesiderati Passaggio di testimone diffusi. Chi riduce in modo mirato e verifica regolarmente previene le falle di sicurezza dovute a errori di utilizzo.
Altrettanto importante: eliminare sistematicamente i file binari SUID. Nei casi in cui in passato era necessario il SUID (ad esempio per inviare pacchetti ICMP), spesso è possibile ricorrere a CAP_NET_RAW lavorare – o, meglio ancora, esternalizzare la funzione in un processo ausiliario il più piccolo possibile con specifiche molto rigorose. Inoltre, evito di collocare le capabilities in percorsi temporanei o scrivibili dagli utenti. Un rigoroso regime di proprietà e distribuzione (Root:root, 0755/0555, percorsi immutabili) impedisce la „perdita“ dei diritti a causa della sostituzione dei file binari.
Funzionalità nei container e DevSecOps
Negli ambienti containerizzati riduco il Capacità in modo aggressivo ed elimino tutto ciò che non è strettamente necessario per il carico di lavoro. Inoltre, creo un Profilo Seccomp che blocca le chiamate di sistema rischiose, creando così un ulteriore ostacolo. Nelle pipeline di build definisco le capacità in modo dichiarativo, le testo nell’ambiente di staging e le registro con il controllo delle versioni. Ciò va a vantaggio della conformità, poiché mi permette di dimostrare il rispetto del principio del privilegio minimo e di documentare in modo completo le modifiche ai diritti. In questo modo i container rimangono strettamente controllati, senza ostacolarne le funzioni, e la Superficie di attacco rimane ridotto. In combinazione con immagini che contengono solo lo stretto necessario, la sicurezza aumenta ulteriormente.
Importante nel contesto dei container: le capabilities si trovano nei namespace Relativo. All’interno di uno spazio dei nomi utente, un processo può sì essere „root“, ma i suoi privilegi si applicano solo agli spazi dei nomi associati – il che riduce notevolmente il raggio d’azione. D’altra parte, „--privilegiato“ È praticamente sempre da evitare: disattiva il limite rigido di bounding e apre molto più del necessario. Per questo motivo avvio i container di default con l’impostazione „rimuovi tutto, aggiungi in modo mirato“ e aggiungo NoNewPrivileges, limiti cgroup e mount in sola lettura. Per i servizi che devono solo essere in ascolto, utilizzo l'attivazione tramite socket o i sidecar per fare a meno di qualsiasi capability aggiuntiva.
Esempio di systemd: limitare le capacità in modo dichiarativo
Nelle unità di servizio definisco i limiti massimi consentiti a un processo: in modo chiaro, ripetibile e controllabile tramite il controllo di versione. Ecco un esempio sintetico di un servizio web che può connettersi solo alla porta 443 e che per il resto è fortemente limitato:
[Unit]
Description=Servizio web minimale senza root
[Service]
User=web
Group=web
ExecStart=/usr/bin/my-web
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/my-web
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallFilter=@basic-io @network-io
LockPersonality=yes
MemoryDenyWriteExecute=yes
[Install]
WantedBy=multi-user.target
La combinazione di Funzionalità ambientali e una dura CapabilityBoundingSet garantisce che al servizio venga assegnata solo la competenza necessaria e nient’altro. NoNewPrivileges impedisce un successivo aumento del livello di Privilega, ProtectSystem e ReadWritePaths regolano l'accesso in scrittura, mentre un filtro rigoroso delle chiamate di sistema impedisce punti di ingresso superflui nel kernel.
Funzionalità di uso frequente – e alternative sicure
- CAP_NET_BIND_SERVICE: Assegnazione alle porte <1024. Alternativa: attivazione dei socket, inserimento di un proxy inverso a monte.
- CAP_NET_RAW: Rohsockets (Ping, DHCP). Alternativa: un piccolo processo di supporto anziché ampi privilegi dell'interprete.
- CAP_CHOWN/CAP_FOWNER: Modifiche relative ai proprietari/ACL. Alternativa: directory preconfigurate, strumenti di manutenzione dedicati.
- CAP_SYS_PTRACE: Debugging/Tracing – solo nell'ambiente di staging, mai su larga scala in produzione.
- CAP_SYS_ADMIN: „Secondo root“ – da evitare; specificare ciò che serve davvero.
Scelgo sempre la quantità minima necessaria per abilitare esattamente la funzione richiesta. Se una capability apre diverse vie di attacco (ad esempio i socket RAW), incapsulo la funzione in un processo separato e di breve durata e, una volta completato il lavoro, revoco i diritti.
Lista di controllo pratica per capacità solide
- Il servizio si avvia senza root? Se no: perché no? E si può risolvere il problema attivando i socket o utilizzando piccoli file binari di supporto?
- Sono tutti Le Capabilities assegnate sono effettivamente necessarie (prova di funzionalità, casi di test)?
- L'insieme di delimitazione è stato definito nel modo più ristretto possibile e il prima possibile?
- Gli XAttr vengono mantenuti in modo coerente durante la compilazione, le distribuzioni e i backup (flag rsync/tar, script dei pacchetti)?
- Sto evitando sistematicamente l'uso delle "capabilities" negli interpreti e nei file binari SUID?
- I diritti di proprietà e sui file (Root:root, 0755/0555) e i percorsi sono protetti contro la sostituzione?
- I controlli aggiuntivi (NoNewPrivileges, Seccomp, profili MAC) sono efficaci?
- Le capacità di processo vengono verificate durante l'esecuzione (
/proc//status, getpcaps) e le modifiche sono state documentate? - I container sono configurati di default con „drop all, add minimal“ e senza „privileged“?
Riassumendo brevemente
Linux Le funzionalità (Capabilities) suddividono i classici diritti di root in piccole unità gestibili, applicando così il principio di minimalità in modo tecnicamente corretto. Assegno ai servizi solo le funzionalità di cui hanno realmente bisogno, combinandole con i diritti POSIX e le politiche MAC. Le funzionalità dei file garantiscono che i diritti siano direttamente associati ai file binari e che gli audit mostrino chiaramente chi è autorizzato a fare cosa. Grazie alla separazione dei privilegi, ai diritti ridotti dei container e ai filtri delle chiamate di sistema, limito i danni nel caso in cui venga sfruttata una vulnerabilità. Controlli regolari, rigidi diritti di proprietà e di scrittura e un processo di rilascio documentato mantengono snella l’assegnazione dei privilegi. In questo modo il servizio server rimane funzionante, ma il Spazio di manovra per gli aggressori viene mantenuto costantemente basso.


