Perché i processi nei contenitori Docker non dovrebbero essere eseguiti come root

Pubblicato: 2022-08-19

Grafica che mostra il logo Docker

I processi in un contenitore Docker non devono essere eseguiti come root. È più sicuro eseguire le tue applicazioni come utente non root che specifichi come parte del tuo Dockerfile o quando usi docker run . Ciò riduce al minimo il rischio presentando una superficie di attacco ridotta a qualsiasi minaccia nel container.

In questo articolo imparerai i pericoli dell'esecuzione di applicazioni containerizzate come root. Vedrai anche come creare un utente non root e impostare lo spazio dei nomi in situazioni in cui ciò non è possibile.

Perché correre come root è pericoloso?

I contenitori vengono eseguiti come root per impostazione predefinita. Il demone Docker viene eseguito come root sul tuo host e anche i container in esecuzione saranno root.

Sebbene possa sembrare che root all'interno del contenitore sia un utente indipendente, in realtà è lo stesso dell'account root sul tuo host. La separazione è fornita solo dai meccanismi di isolamento del contenitore di Docker. Non c'è un forte confine fisico; il tuo contenitore è un altro processo eseguito dall'utente root sul kernel del tuo host. Ciò significa che una vulnerabilità nell'applicazione, nel runtime Docker o nel kernel Linux potrebbe consentire agli aggressori di uscire dal container ed eseguire operazioni con privilegi di root sul computer.

Ci sono alcune protezioni integrate che riducono il rischio che ciò accada. Il root all'interno del contenitore non è privilegiato e ha capacità limitate. Ciò impedisce al contenitore di utilizzare i comandi di amministrazione del sistema a meno che non si aggiungano manualmente funzionalità o si utilizzi la modalità privilegiata all'avvio dei contenitori.

Nonostante questa mitigazione, consentire alle applicazioni di essere eseguite come root rimane un pericolo. Proprio come limiteresti l'uso di root in un ambiente tradizionale, non è saggio utilizzarlo inutilmente all'interno dei tuoi contenitori. Stai fornendo un ambiente con privilegi eccessivi che offre agli aggressori un punto d'appoggio maggiore nel caso in cui si verifichi una violazione.

Esecuzione di applicazioni containerizzate come utente non root

È consigliabile che le applicazioni containerizzate vengano eseguite come utente normale. La maggior parte dei software non richiede l'accesso come root, quindi la modifica dell'utente fornisce un livello di difesa immediato contro il breakout del contenitore.

Dovresti creare un nuovo account utente come una delle fasi finali nel tuo Dockerfile. È possibile ottenere ciò con l'istruzione USER :

 DA immagine di base: più recente
Esegui apt install pacchetto demo
UTENTE demo-utente: demo-gruppo
ENTRYPOINT ["demo-binario"]

I contenitori avviati da questa immagine verranno eseguiti come demo-user . L'utente sarà un membro del gruppo demo-group . Puoi omettere il nome del gruppo se non è necessario che l'utente sia in un gruppo:

 USER demo-utente

È possibile specificare un ID utente (UID) e un ID gruppo (GID) anziché i nomi:

 UTENTE 950:950

L'allocazione di un UID e GID noti è in genere il modo più sicuro per procedere. Impedisce che l'utente nel contenitore venga mappato a un account host con privilegi eccessivi.

USER è spesso specificato come penultimo stadio in un Dockerfile. Ciò significa che puoi comunque eseguire operazioni che richiedono il root in precedenza nella build dell'immagine. L'istruzione apt install nell'esempio sopra ha un legittimo bisogno di root. Se l'istruzione USER fosse stata posizionata sopra di essa, apt verrebbe eseguito come demo-user che non avrebbe i permessi necessari. Poiché le istruzioni di Dockerfile si applicano solo alle build di immagini, non ai contenitori in esecuzione, è possibile lasciare la modifica dell'utente fino a più tardi nel Dockerfile.

La modifica dell'utente eseguito dal contenitore potrebbe richiedere l'aggiornamento delle autorizzazioni sui file e sulle cartelle a cui accede. Imposta la proprietà su tutti i percorsi che verranno utilizzati dalla tua applicazione:

 COPIA initial-config.yaml /data/config.yaml

UTENTE demo-utente: demo-gruppo
ESEGUI chown demo-user: demo-group /data

In questo esempio la directory /data deve essere di proprietà demo-user modo che l'applicazione possa apportare modifiche al proprio file di configurazione. L'istruzione COPY precedente avrà copiato il file come root. Una scorciatoia è disponibile usando il flag --chown con copy :

 COPIA --chown=utente-demo:gruppo-demo initial-config.yaml /data/config.yaml

Modifica dell'utente all'avvio di un container

Sebbene tu possa facilmente modificare l'utente nei tuoi Dockerfile, molte applicazioni di terze parti continuano a essere eseguite come root. Puoi ridurre il rischio associato all'utilizzo di questi impostando il flag --user ogni volta che chiami docker run . Questo sovrascrive l'utente impostato nel Dockerfile dell'immagine.

 $ docker run -d --user demo-user: demo-group demo-image:latest
$ docker run -d --user demo-user demo-image:latest
$ docker run -d --user 950:950 demo-image:latest

Il flag --user esegue il processo del contenitore come utente specificato. È meno sicuro dell'istruzione Dockerfile USER perché devi applicarla individualmente a ogni comando di docker run . Un'opzione migliore per le immagini utilizzate regolarmente è creare la tua immagine derivata che può impostare un nuovo account utente:

 DA immagine-che-corre-come-root:latest
USER demo-utente
 $ build della finestra mobile. -t immagine-che-ora-corre-come-non-root:latest

La modifica dell'utente di un'immagine di terze parti può causare problemi: se il contenitore prevede di essere eseguito come root o deve accedere ai percorsi del filesystem di proprietà di root, verranno visualizzati errori durante l'utilizzo dell'applicazione. Potresti provare a modificare manualmente i permessi sui percorsi che causano problemi. In alternativa, verifica se il fornitore dispone di un metodo supportato per eseguire l'applicazione con un account utente senza privilegi.

Gestione delle applicazioni che devono essere eseguite come root

Lo spazio dei nomi utente è una tecnica per gestire le applicazioni che richiedono alcuni privilegi di root. Ti consente di mappare la radice all'interno di un contenitore a un utente non root sul tuo host. La radice simulata all'interno del contenitore dispone dei privilegi necessari, ma un'interruzione non fornirà l'accesso root all'host.

La rimappatura dello spazio dei nomi viene attivata aggiungendo un campo userns-remap al file /etc/docker/daemon.json :

 {
    "userns-remap": "predefinito"
}

L'utilizzo di default come valore per userns-remap indica a Docker di creare automaticamente un nuovo utente sul tuo host chiamato dockremap . Il root all'interno dei contenitori verrà mappato di nuovo su dockremap sul tuo host. È possibile invece specificare un utente e un gruppo esistenti, utilizzando una combinazione UID/GID o nome utente/nome gruppo:

 {
    "userns-remap": "demo-user"
}

Riavvia il demone Docker dopo aver applicato la modifica:

 $ riavvio della finestra mobile del servizio sudo

Se stai usando nsuser-remap: default , l'utente dockremap dovrebbe ora esistere sul tuo host:

 $ id dockremap

uid=140(dockremap) gid=119(dockremap) gruppi=119(dockremap)

L'utente dovrebbe apparire anche nei /etc/subuid e /etc/subgid ID subordinato:

 $ dockremap:231500:65535

All'utente è stato assegnato un intervallo di 65.535 ID subordinati a partire da 231500. All'interno dello spazio dei nomi utente, l'ID 231500 è mappato su 0 , rendendolo l'utente root nei contenitori. Essendo un UID con un numero elevato, 231500 non ha privilegi sull'host, quindi gli attacchi di breakout del contenitore non saranno in grado di infliggere così tanti danni.

Tutti i contenitori che avvii verranno eseguiti utilizzando lo spazio dei nomi utente rimappato a meno che tu non decida di uscire con docker run --userns=host . Il meccanismo funziona creando directory namespace all'interno di /var/lib/docker che sono di proprietà dell'UID e GID subordinato dell'utente namespace:

 $ sudo ls -l /var/lib/docker/231500.231500

totale 14
drwx------ 5 231500 231500 13 lug 22 19:00 aufs
drwx------ 3 231500 231500 13 lug 22 19:00 contenitori
...

Lo spazio dei nomi utente è un modo efficace per aumentare l'isolamento del contenitore, evitare interruzioni e preservare la compatibilità con le applicazioni che richiedono privilegi di root. Tuttavia, ci sono alcuni compromessi: la funzione funziona meglio su una nuova istanza Docker, i volumi montati dall'host devono avere le autorizzazioni regolate e alcuni driver di archiviazione esterni non supportano affatto la mappatura degli utenti. È necessario rivedere la documentazione prima di adottare questa opzione.

Riepilogo

L'esecuzione di applicazioni containerizzate come root è un rischio per la sicurezza. Sebbene sia facile da trascurare, l'isolamento fornito dai container non è abbastanza forte da separare completamente gli utenti del kernel dagli utenti dei container. Il root nel contenitore è lo stesso del root sul tuo host, quindi un compromesso riuscito potrebbe fornire il controllo del tuo computer.

Come autore di immagini, dovresti includere l'istruzione USER nel tuo Dockerfile in modo che l'applicazione venga eseguita senza root. Gli utenti di immagini possono sovrascriverlo con docker run --user per assegnare un UID e un GID specifici. Questo aiuta a mitigare i casi in cui l'immagine usa normalmente root.

Puoi rafforzare ulteriormente la sicurezza eliminando tutte le funzionalità dal contenitore utilizzando --cap-drop=ALL , quindi inserendo nella whitelist quelle necessarie con --cap-add flag. La combinazione di queste tecniche eseguirà la tua applicazione come utente non root con il set minimo di privilegi di cui ha bisogno, migliorando la tua posizione di sicurezza.