GNU/Linux >> Linux Esercitazione >  >> Linux

Utilizzo delle funzionalità di systemd per proteggere i servizi

Tutte le versioni recenti delle distribuzioni Linux più popolari utilizzano systemd per avviare la macchina e gestire i servizi di sistema. Sistema fornisce diverse funzionalità per rendere più facile l'avvio dei servizi e più sicuro. Questa è una combinazione rara e questo articolo mostra perché è utile lasciare systemd gestire le risorse e il sandboxing di un servizio.

Giustificazione

Quindi, perché dovremmo usare systemd per il sandbox di sicurezza? In primo luogo, si potrebbe obiettare che ogni bit di questa funzionalità è già esposto attraverso strumenti esistenti e ben noti, che possono essere sottoposti a script e combinati in modi arbitrari. In secondo luogo, in particolare nel caso di programmi scritti in C/C++ e altri linguaggi di basso livello, è possibile utilizzare direttamente le chiamate di sistema appropriate, ottenendo un'implementazione snella accuratamente adattata alle esigenze di un particolare servizio.

Ci sono quattro ragioni principali:

1. La sicurezza è difficile. Un'implementazione centralizzata nel gestore dei servizi significa che un servizio che ne trae vantaggio può essere notevolmente semplificato. Senza dubbio, questa implementazione centralizzata è complessa, ma a causa del suo ampio utilizzo, è ben testata. Se consideriamo che viene riutilizzato in migliaia di servizi, il complessivo la complessità del sistema è ridotta.

2. Le primitive di sicurezza variano tra i sistemi. Sistema attenua le differenze tra architetture hardware, versioni del kernel e configurazioni di sistema.

La funzionalità che fornisce il rafforzamento dei servizi è implementata nella misura del possibile su un determinato sistema. Ad esempio, un systemd l'unità può contenere entrambe le configurazioni AppArmor e SELinux. Il primo viene utilizzato su sistemi Ubuntu/Debian, il secondo su Fedora/RHEL/CentoOS e nessuno dei due per le distribuzioni che non abilitano alcun sistema MAC. L'altro lato di questa flessibilità è che non si può fare affidamento su queste funzionalità come solo meccanismo di contenimento (o che tali servizi siano utilizzati solo su sistemi che supportano tutte le funzionalità richieste).

3. La sicurezza richiede manovre di basso livello con il sistema. Le funzionalità fornite dal gestore del servizio sono indipendenti dal linguaggio di implementazione del servizio, quindi è facile scrivere un servizio in un linguaggio di alto livello, ad esempio shell o Python o qualsiasi altra cosa sia conveniente, e comunque bloccarlo.

4. La sicurezza richiede privilegi. Questo è un paradosso, ma per togliere i privilegi sono necessari privilegi. Ad esempio, spesso abbiamo bisogno di essere root per configurare uno spazio dei nomi di montaggio personalizzato per limitare la visualizzazione del filesystem. Come altro esempio, un demone HTTP viene spesso avviato come root solo per poter aprire una porta con numero basso e le porte con numero basso sono limitate in nome della sicurezza. Il gestore dei servizi deve comunque essere eseguito con i privilegi più elevati, ma i servizi no, e l'impostazione di protezione avanzata è spesso l'unico motivo per richiedere privilegi più elevati. Eventuali bug nell'implementazione del servizio in questa fase possono essere pericolosi. Scaricando la configurazione al gestore del servizio, i servizi possono essere avviati senza questa fase iniziale di privilegi elevati.

Per contestualizzare, Fedora 32 recentemente rilasciato contiene quasi 1800 diversi file unit per l'avvio di servizi scritti in C, C++, Python, Java, Ocaml, Perl, Ruby, Lua, Tcl, Erlang e così via - e solo un sistema .

[ Hai bisogno di altro su systemd? Scarica il cheat sheet di systemd per ulteriori suggerimenti utili. ]

Alcuni modi equivalenti per avviare un servizio

Più comunemente, systemd i servizi sono definiti tramite un file unit :un file di testo in formato ini che dichiara i comandi da eseguire e le varie impostazioni. Dopo che questo file dell'unità è stato modificato, systemctl daemon-reload dovrebbe essere chiamato per chiedere al manager di caricare le nuove impostazioni. L'output del demone arriva nel diario e viene utilizzato un comando separato per visualizzarlo. Quando si eseguono comandi in modo interattivo, tutto ciò non è molto conveniente. Il sistema eseguito command dice al manager di avviare un comando per conto dell'utente ed è un'ottima alternativa per l'uso interattivo. Il comando da eseguire è specificato in modo simile a sudo . Il primo argomento posizionale e tutto ciò che segue è il comando effettivo e tutte le opzioni precedenti vengono interpretate da systemd-run si. Il sistema eseguito comando ha opzioni per specificare impostazioni specifiche come --uid e --gid per l'utente e il gruppo. Il -E opzione imposta una variabile di ambiente, mentre un'opzione "catch-all" -p accetta coppie chiave=valore arbitrarie simili al file unit.

$ systemd-run whoamiRunning as unit:run-rbd26afbc67d74371a6d625db78e33acc.service$ journalctl -u run-rbd26afbc67d74371a6d625db78e33acc.servicejournalctl -u run-rbd26afbc67d74371a6d625db78e33acc.service-- Logs begin at Thu 2020-04-23 19:31:49 CEST, termine lun 2020-04-27 13:22:35 CEST. --Apr 27 13:22:18 fedora systemd[1]:avviato run-rbd26afbc67d74371a6d625db78e33acc.service.Apr 27 13:22:18 fedora whoami[520662]:rootApr 27 13:22:18 fedora systemd[1]:run- rbd26afbc67d74371a6d625db78e33acc.service:riuscito. 

systemd-run -t collega i flussi di input, output ed errore standard del comando al terminale di richiamo. Questo è ottimo per eseguire comandi in modo interattivo (nota che il processo di servizio è ancora un figlio del manager).

$ systemd-run -t whoamiRunning as unit:run-u53517.servicePress ^] tre volte entro 1 secondo per disconnettere TTY.root 

Ambiente coerente

Un'unità inizia sempre in un ambiente accuratamente definito. Quando avviamo un'unità utilizzando systemctl o systemd-run , il comando viene sempre invocato come figlio del gestore. L'ambiente della shell non influisce sull'ambiente in cui vengono eseguiti i comandi del servizio. Non tutte le impostazioni che possono essere specificate in un file unit sono supportate da systemd-run , ma la maggior parte sono, e fintanto che ci atteniamo a quel sottoinsieme, invocazione tramite un file unit e systemd-run sono equivalenti. In effetti, systemd-run crea al volo un file di unità temporaneo.

Ad esempio:

$ sudo systemd-run -M rawhide -t /usr/bin/grep PRETTY_NAME=/etc/os-release 

Qui, sudo parla con PAM per consentire l'escalation dei privilegi, quindi esegue systemd-run come radice. Quindi, systemd-run effettua una connessione a una macchina denominata rawhide , dove comunica con il gestore di sistema (PID 1 nel contenitore) su dbus. Il gestore invoca grep , che fa il suo lavoro. Il grep il comando stampa su stdout, che è connesso allo pseudo-terminale da cui sudo è stato invocato.

Impostazioni di sicurezza

Utenti e utenti dinamici

Senza ulteriori indugi, parliamo di alcune impostazioni specifiche, a cominciare dalle primitive più semplici e potenti.

In primo luogo, il meccanismo di separazione dei privilegi più vecchio, più semplice e forse più utile:gli utenti. Puoi definire gli utenti con User=foobar nel [Servizio] sezione di un file unit o systemd-run -p User=foobar o systemd-run --uid=foobar . Potrebbe sembrare ovvio, e su Android ogni applicazione ha il proprio utente, ma nel mondo Linux abbiamo ancora troppi servizi che vengono eseguiti inutilmente come root.

Sistema fornisce un meccanismo per creare utenti su richiesta. Quando viene invocato con DynamicUser=yes , per il servizio viene assegnato un numero utente univoco. Questo numero si risolve in un nome utente temporaneo. Questa assegnazione non è memorizzata in /etc/passwd , ma viene invece generato al volo da un modulo NSS ogni volta che viene richiesto il numero o il nome corrispondente. Dopo l'arresto del servizio, il numero potrebbe essere riutilizzato in seguito per un altro servizio.

Quando dovrebbe essere utilizzato un utente statico regolare per un servizio e quando è preferibile uno dinamico? Gli utenti dinamici sono ottimi quando l'identità dell'utente è effimera e non è necessaria alcuna integrazione con altri servizi nel sistema. Ma quando abbiamo una politica nel database per consentire l'accesso di utenti specifici, directory condivise con un particolare gruppo o qualsiasi altra configurazione in cui vogliamo fare riferimento al nome utente, gli utenti dinamici probabilmente non sono l'opzione migliore.

Monta gli spazi dei nomi

In generale, va notato che systemd spesso è solo la funzionalità di wrapping fornita dal kernel. Ad esempio, varie impostazioni che limitano l'accesso all'albero del file system, rendendone parti di sola lettura o inaccessibili, vengono eseguite organizzando i file system appropriati in uno spazio dei nomi di montaggio non condiviso.

Diverse impostazioni utili sono implementate in questo modo. I due più utili e generali sono ProtectHome= e ProtectSystem= . Il primo usa uno spazio dei nomi di montaggio non condiviso per creare /home di sola lettura o del tutto inaccessibile. Il secondo riguarda la protezione di /usr , /avvio e /ecc .

Anche una terza impostazione utile ma molto specifica è PrivateTmp= . Utilizza gli spazi dei nomi di montaggio per rendere visibile una directory privata come /tmp e /var/tmp per il servizio. I file temporanei del servizio vengono nascosti agli altri utenti per evitare problemi dovuti a conflitti di nomi di file o autorizzazioni errate.

La vista del file system può essere gestita a livello di singole directory tramite InaccessiblePaths= , ReadOnlyPaths= , ReadWritePaths= , BindPaths= e ReadOnlyBindPaths= . Le prime due impostazioni forniscono tutto o solo l'accesso in scrittura a parti di una gerarchia di file system. Il terzo riguarda il ripristino dell'accesso, utile quando si desidera concedere l'accesso completo solo a una directory specifica all'interno della gerarchia. Gli ultimi due consentono di spostare le directory o, più precisamente, di montarle in modo privato in una posizione diversa.

Tornando all'argomento di DynamicUser=yes , tali utenti temporanei sono possibili solo quando il servizio non è autorizzato a creare file permanenti su disco. Se tali file fossero visibili ad altri utenti, verrebbero mostrati come privi di proprietario o, peggio, potrebbero essere accessibili dal nuovo utente temporaneo con lo stesso numero, causando una fuga di informazioni o un'escalation involontaria dei privilegi. Sistema utilizza gli spazi dei nomi di montaggio per rendere la maggior parte dell'albero del file system non scrivibile al servizio. Per consentire l'archiviazione permanente, viene montata una directory privata nell'albero del file system visibile al servizio.

Si noti che tali protezioni sono indipendenti dal meccanismo di controllo dell'accesso ai file di base che utilizza la proprietà del file e la maschera di autorizzazione. Se un file system è montato in sola lettura, anche gli utenti che potrebbero modificare file specifici in base alle autorizzazioni standard non possono farlo finché il file system non viene rimontato in lettura e scrittura. Ciò fornisce una protezione contro gli errori nella gestione dei file (dopotutto, non è raro che gli utenti impostino occasionalmente la maschera di autorizzazione sbagliata) ed è uno strato di una strategia di difesa approfondita.

Le risorse implementate utilizzando gli spazi dei nomi di montaggio sono generalmente molto efficienti perché l'implementazione del kernel è efficiente. Anche l'overhead durante la loro configurazione è generalmente trascurabile.

Creazione automatica di directory per un servizio

Una funzionalità relativamente nuova che systemd prevede servizi è la gestione automatica delle directory. Percorsi diversi del filesystem hanno caratteristiche di archiviazione e usi previsti diversi, ma rientrano in alcune categorie standard. L'FHS specifica che /etc è per i file di configurazione, /var/cache è per l'archiviazione non permanente, /var/lib/ è per l'archiviazione semipermanente, /var/log per i log e /run per i file volatili. Un servizio spesso necessita di una sottodirectory in ciascuna di queste posizioni. Sistema lo imposta automaticamente, come controllato da ConfigurationDirectory= , CacheDirectory= , StateDirectory= , LogsDirectory=RuntimeDirectory= impostazioni. L'utente possiede tali directory. La directory di runtime viene rimossa dal manager quando il servizio viene interrotto. L'idea generale è di legare l'esistenza di tali risorse del filesystem alla durata del servizio. Non devono essere creati in anticipo e vengono ripuliti in modo appropriato dopo l'interruzione dell'esecuzione del servizio.

$ sudo systemd-run -t -p Utente=utente -p CacheDirectory=pippo -p StateDirectory=pippo -p RuntimeDirectory=pippo -p PrivateTmp=yes ls -ld /run/foo /var/cache/ foo /var/lib/foo /etc/foo /tmp/Running as unit:run-u45882.servicePress ^] tre volte entro 1s per disconnettere TTY.drwxr-xr-x  2 user user   40 Apr 26 08:21 /run/ foo           ← creato e rimosso automaticamentedrwxr-xr-x  2 utente utente 4096 Apr 26 08:20 /var/cache/foo     ← creato automaticamentedrwxr-xr-x. 2 utente utente 4096 13 novembre 21:50 /var/lib/foo       ← creato automaticamentedrwxr-xr-x. 2 root    root    4096 Nov 13 21:50 /etc/foo           ← creato automaticamente, ma non di proprietà dell'utente, poiché il servizio (di solito) non modifica la propria configurazionedrwxrwxrwt  2 root    root      40 Apr 26 08:21 /tmp/              ← " sticky bit" è impostato, ma questa directory non è quella che vedono tutti gli altri 

Naturalmente, quelle sette posizioni (contando PrivateTmp= come due) non coprono le esigenze di ogni servizio, ma dovrebbero essere sufficienti per la maggior parte delle situazioni. Per gli altri casi, configurazione manuale o una configurazione appropriata in tmpfiles.d è sempre un'opzione.

La gestione automatica delle directory si adatta perfettamente a DynamicUser= impostazione e utenti creati automaticamente, fornendo un servizio che viene eseguito come utente separato e non è autorizzato a modificare la maggior parte dell'albero del file system (anche se le autorizzazioni di accesso ai file lo consentirebbero). Il servizio può comunque accedere a directory selezionate e archiviarvi dati, senza alcuna configurazione diversa dalla configurazione del file dell'unità.

Ad esempio, un servizio Web Python potrebbe essere eseguito come:

$ systemd-run -p DynamicUser=yes -p ProtectHome=yes -p StateDirectory=webserver --working-directory=/srv/www/content python3 -m http.server 8000 

o tramite il file unitario equivalente:

[Service]DynamicUser=yesProtectHome=yesStateDirectory=webserverWorkingDirectory=/srv/www/contentExecStart=python3 -m http.server 8000 

Ci assicuriamo che il servizio venga eseguito come utente temporaneo senza la possibilità di modificare il file system o avere accesso ai dati dell'utente.

Le impostazioni qui descritte possono essere considerate "di alto livello". Anche se l'implementazione potrebbe essere complicata, i concetti stessi sono facilmente comprensibili e l'effetto sul servizio è chiaro. Esistono un gran numero di altre impostazioni per eliminare varie autorizzazioni e capacità, bloccare i protocolli di rete e le regolazioni del kernel e persino disabilitare le singole chiamate di sistema. Questi sono al di fuori dello scopo di questo breve articolo. Fare riferimento all'ampia documentazione di riferimento.

Mettendo tutto questo da usare

Quando abbiamo una buona comprensione di ciò che il servizio fa e di cui ha bisogno, possiamo considerare quali privilegi sono richiesti e cosa possiamo portare via. I candidati più ovvi si candidano come utente non privilegiato e limitano l'accesso ai dati degli utenti in /home . Più consentiamo systemd per impostare le cose per noi (ad esempio, utilizzando StateDirectory= e amici), più è probabile che il servizio possa essere eseguito correttamente come utente senza privilegi. Spesso il servizio ha bisogno di accedere a una sottodirectory specifica e possiamo ottenerlo usando ReadWritePaths= e impostazioni simili.

L'aggiunta di misure di sicurezza in qualsiasi modo automatico è impossibile. Senza una buona comprensione di ciò di cui il servizio ha bisogno in diversi scenari di configurazione e per diverse operazioni, non possiamo definire una sandbox utile. Ciò significa che il sandboxing dei servizi viene eseguito al meglio dai loro autori o manutentori.

Valutazione e status quo

Il numero di impostazioni possibili è elevato e ne vengono aggiunte di nuove con ogni versione di systemd . Stare al passo con quello è difficile. Sistema fornisce uno strumento per valutare l'uso delle direttive sandboxing nel file unit. I risultati dovrebbero essere considerati suggerimenti:dopotutto, come accennato in precedenza, la creazione automatica di una politica di sicurezza è difficile e qualsiasi valutazione consiste semplicemente nel contare ciò che viene utilizzato e ciò che non lo è, senza una comprensione profonda di ciò che conta per un determinato servizio.

; 0.1armi PrivateDevices =Servizio non ha accesso ai dispositivi hardware ✓ PrivateMounts =Service Impossibile installare i supporti del sistema PrivateTMP =Servizio Esegui in fase di avvio speciale, l'opzione non si applica ✗ PrivateUser =Servizio ha accesso ad altri utenti 0.2 ProteCthome =Servizio esecuzioni in fase di avvio speciale , l'opzione non si applica                ✓ ProtectKernelLogs=     Il servizio non può leggere o scrivere nel buffer ad anello del log del kernel          ✓ ProtectKernelModules=  Il servizio non può caricare o ri Moduli ad kernel Ad ✓ ProtectKerNeltUnables =Servizio non può alterare le sintonizzabili del kernel (/proc/sys, ...) ProtectSystem =Service corse in una fase di avvio speciale, l'opzione non si applica ✓ Gruppi supplementari =il servizio non ha gruppi supplementari ... → Livello di esposizione complessivo per SystemD- Risold.service:2.1 OK 🙂 $ SystemD-Analyze Security Httpd.Service Nome Descrizione ESPOSIZIONE ... ✗ Utente =/DynamicUser =Service funziona come utente root 0.4✗ DeviceALlow =Service non ha un dispositivo ACL 0.2✗ PrivateDevices =Service ha potenzialmente accesso a dispositivi hardware                 0.2✓ PrivateMounts=         Il servizio non può installare montaggi di sistema                                  ✓ PrivateTmp=            Il servizio non ha accesso ad altri software File temporanei di Ware's ✗ PrivateUser =Service ha accesso ad altri utenti 0.2✗ ProteCthome =Service ha pieno accesso alle directory home 0.2✗ Protectkernellogs =Servizio può leggere o scrivere al buffer anello di tronchi del kernel 0.2✗ ProtectkernelModules =Service può caricare o leggere i moduli del kernel 0.2✗ ProtectKerNeltUnables =Servizio può alterare le sintonizzabili del kernel 0.2✗ ProtectSystem =Service ha pieno accesso alla gerarchia del file OS 0.2 SupplementaryGroups =Service funziona come root, l'opzione non ha importanza ... → Livello di esposizione complessivo per httpd.service:9.2 UNSAFE 😨

Ancora una volta, questo non significa che il servizio non sia sicuro, ma che non stia usando il systemd primitive di sicurezza.

Guardando il livello dell'intera distribuzione:

$ systemd-analyze security '*' 

Vediamo che la maggior parte dei servizi ha un punteggio molto alto (vale a dire, cattivo). Non possiamo raccogliere tali statistiche sui vari servizi interni, ma sembra ragionevole presumere che siano simili. C'è sicuramente un sacco di frutta bassa e l'applicazione di un sandboxing relativamente semplice renderebbe i nostri sistemi più sicuri.

Concludi

Lasciando systemd gestire i servizi e il sandboxing può essere un ottimo modo per aggiungere un livello di sicurezza ai tuoi server Linux. Prendi in considerazione la possibilità di testare le configurazioni precedenti per vedere quali potrebbero essere i vantaggi per la tua organizzazione.

In questo articolo, abbiamo accuratamente evitato qualsiasi accenno al networking. Questo perché la seconda puntata parlerà dell'attivazione del socket e del sandboxing dei servizi che utilizzano la rete.

[ Non dimenticare di controllare il cheat sheet di systemd per ulteriori suggerimenti utili. ]


Linux
  1. Come gestire i servizi Systemd con Systemctl su Linux

  2. 10 pratici comandi di sistema:un riferimento

  3. Come utilizzare il comando Systemctl per gestire i servizi Systemd

  4. Come elencare i servizi Systemd in Linux

  5. Usa Systemctl per gestire i servizi

Comandi Systemctl per gestire il servizio Systemd

Gestire cgroup con systemd

Come configurare l'esecuzione automatica di uno script Python utilizzando Systemd

Servizi di rete

Modo corretto di usare Ubuntu systemctl per controllare Systemd

systemd - Dando al mio servizio più argomenti