GNU/Linux >> Linux Esercitazione >  >> Linux

Come eseguire il debug dei problemi con i volumi montati su contenitori senza root

Una delle domande più frequenti che mi vengono poste su Podman senza root è come eseguire il debug dei problemi con i volumi montati nel contenitore. Questa domanda è ingannevolmente difficile. In molti modi, eseguire Podman senza root è quasi identico a eseguirlo come root . Sfortunatamente, questo non è sempre vero e i volumi sono una delle aree con le differenze più significative. Qui, spiegherò in dettaglio quali sono queste differenze, quali tipi di errori possono causare e come aggirarli. Per iniziare, abbiamo bisogno di alcune informazioni di base su come funzionano i container rootless, a cominciare da una delle caratteristiche più fondamentali di Podman rootless:Spazi dei nomi utente .

Lo spazio dei nomi utente

Una delle caratteristiche di sicurezza fondamentali dei container sono gli spazi dei nomi del kernel Linux. Uno spazio dei nomi è un modo per isolare un processo (o un gruppo di processi) dal resto del sistema limitando ciò che può vedere. Esistono molti spazi dei nomi diversi, ognuno con un effetto diverso. Ad esempio, lo spazio dei nomi PID limita i PID che un processo può visualizzare:solo i PID all'interno dello spazio dei nomi sono visibili e sono numerati indipendentemente dall'host. Il processo ha ancora un PID anche sull'host, quindi il PID 2000 sull'host potrebbe essere PID 1 nello spazio dei nomi. Al momento della stesura di questo articolo, gli spazi dei nomi forniti dal kernel sono Mount, PID, Network, IPC, UTS, User, cgroup e time, ognuno dei quali isola un aspetto diverso del sistema; quello a cui teniamo di più per questo blog è lo spazio dei nomi utente.

Gli spazi dei nomi utente isolano gli utenti ei gruppi disponibili nel contenitore da quelli disponibili nel sistema host. Uno spazio dei nomi utente funziona mappando gli utenti nel contenitore agli utenti sull'host. Ad esempio, potremmo mappare gli utenti da 0 a 1000 nel contenitore agli utenti da 100000 a 101000 sull'host (anche i gruppi sono mappati in modo identico, ma ci concentreremo sugli utenti per semplicità). Questa mappatura agisce in modo molto simile allo spazio dei nomi PID descritto sopra, ma con gli utenti.

Dall'host, tutti gli accessi da root nel contenitore (UID 0) sembrerà essere dall'UID 100000. All'interno del contenitore, qualsiasi file di proprietà dell'utente 100000 sull'host apparirà come di proprietà dell'UID 0 (root ). Una domanda interessante è cosa succede agli utenti non mappati nel contenitore:cosa succede se monto un volume di proprietà dell'utente 1001 sull'host in un contenitore utilizzando lo spazio dei nomi utente che ho descritto? Il kernel, in questo caso, mapperà qualsiasi UID o GID non valido nello spazio dei nomi a UID/GID 65534, un utente e un gruppo chiamato nessuno , che non è un gruppo normale.

È ancora possibile interagire con file di proprietà di nessuno se le autorizzazioni lo consentono (ad esempio, puoi leggere un file di proprietà di nessuno che è leggibile da tutto il mondo e scrivi su quel file se è scrivibile da tutto il mondo), ma non puoi cambiarne la proprietà. Gli spazi dei nomi utente garantiscono anche versioni limitate di funzionalità specifiche che normalmente sono disponibili solo per root —l'esempio tipico è che gli spazi dei nomi utente possono montare determinati tipi di filesystem, come tmpfs.

Gli spazi dei nomi utente sono estremamente utili perché ci consentono di agire come root all'interno del contenitore senza essere effettivamente root sul sistema. Possiamo utilizzare gli spazi dei nomi utente per separare i container da utenti diversi su sistemi multi-tenant:i container di un utente verrebbero eseguiti come UID da 10000 a 10999, un altro da 11000 a 11999 e così via. All'interno di ogni contenitore, sembra che l'applicazione sia root e, grazie alle limitate capacità concesse, può eseguire le operazioni più comuni (installazione di pacchetti, ad esempio).

Tuttavia, supponiamo che un'applicazione riesca a uscire dal contenitore. In tal caso, non viene eseguito come root sul sistema e non in esecuzione con lo stesso UID e GID dei contenitori di qualsiasi altro utente:la capacità di attaccare diverse parti del sistema è estremamente limitata.

Tuttavia, la loro adozione era limitata da limitazioni tecniche, in particolare dal fatto che fino a tempi molto recenti non c'era modo di rimappare UID e GID a livello di filesystem. Per avere un contenitore che utilizzava gli UID da 10000 a 10999, dovevamo fare una copia della sua immagine e quindi chown ogni UID in detta immagine aggiungendo 10000 all'UID esistente. Questo chown può essere molto lento e (in molti filesystem) aumenta notevolmente la quantità di spazio richiesta.

[ Iniziare con i container? Dai un'occhiata a questo corso gratuito. Distribuzione di applicazioni containerizzate:una panoramica tecnica. ]

Contenitori senza radice

Laddove gli spazi dei nomi utente sono diventati estremamente utili e popolari sono i contenitori senza radice. Un utente non root in Linux ha accesso a un solo UID e GID (il proprio). Tuttavia, i container prevedono di accedere a più utenti e gruppi:molti file nelle immagini dei container non sono di proprietà di root e le applicazioni verranno spesso eseguite come utente non root nel contenitore per imporre la separazione dei privilegi all'interno del contenitore.

Per alcuni ambienti (calcolo ad alte prestazioni, HPC, essendo uno di quelli notevoli), avere un solo utente e gruppo nel contenitore è accettabile (anche desiderabile). Per la maggior parte degli altri ambienti, un utente e un gruppo rappresentano una grave limitazione all'utilità del contenitore. Possiamo utilizzare gli spazi dei nomi utente per ottenere questi utenti e gruppi aggiuntivi di cui abbiamo bisogno per agire come un tipico contenitore.

Tuttavia, abbiamo bisogno di privilegi elevati per ottenere questi utenti e gruppi aggiuntivi. Questo è ciò che la newuidmap e newgidmap eseguibili (e /etc/subid e /etc/subgid config che leggono) fanno:ci concedono l'accesso a un blocco di utenti e gruppi, che vengono quindi mappati in uno spazio dei nomi utente per essere utilizzati dai contenitori rootless. Le limitazioni degli spazi dei nomi utente per quanto riguarda il supporto del filesystem si applicano ancora in una certa misura, ma sono mitigate dalla disponibilità di un solo set di UID e GID da utilizzare per ciascun utente.

Da notare anche il fatto che il kernel gestirà automaticamente il chown operazione per noi se decomprimiamo l'immagine all'interno di uno spazio dei nomi utente. Lo spostamento dell'utente dello spazio dei nomi garantisce che l'UID corretto venga assegnato al momento della creazione invece di richiedere al runtime del contenitore di impostarlo manualmente.

Le funzionalità aggiuntive di uno spazio dei nomi utente sono essenziali anche per alcune delle cose di cui i container senza root hanno bisogno:senza la possibilità di montare filesystem FUSE e tmpfs, i container senza root sarebbero molto più limitati (al punto da essere quasi inutilizzabili).

Spazi dei nomi utente in Podman

Ora che comprendiamo come funzionano gli spazi dei nomi utente in generale, discutiamo di come vengono implementati in Podman senza root.

Tutti i contenitori Podman senza root vengono eseguiti in uno spazio dei nomi utente, anche se l'utente non ha più di un UID e GID disponibile. Tutti i contenitori di un utente condividono un unico spazio dei nomi utente, tenuto aperto dal processo di pausa senza root. Gli spazi dei nomi vengono solitamente eliminati dal kernel quando non ci sono più processi al loro interno, quindi mantenere un processo in giro che non fa altro che dormire e non esce mai manterrà vivo lo spazio dei nomi dell'utente.

La prima cosa che fa un processo Podman senza radice è unirsi allo spazio dei nomi utente senza radice (o creare un nuovo spazio dei nomi e mettere in pausa il processo se non esistono ancora). Come parte della creazione dello spazio dei nomi utente, Podman eseguirà la newuidmap e newgidmap eseguibili per concedere eventuali UID e GID aggiuntivi assegnati all'utente in /etc/subuid e /etc/subgid (l'importo predefinito concesso alla creazione dell'utente è 65536 di ciascuno). Puoi visualizzare le mappature utente disponibili nelle info podman comando, in idMappings campo:

; :1    - container_id:1      host_id:100000      size:65536

Si noti che lo spazio dei nomi utente rootless non viene ricreato per impostazione predefinita se /etc/subid e /etc/subgid i file vengono modificati; questo viene fatto eseguendo la migrazione del sistema podman comando. Se hai modificato questi file e Podman non sembra riconoscere le tue modifiche, esegui questo comando.

Tutti i comandi Podman senza radice vengono eseguiti all'interno dello spazio dei nomi utente senza radice creato per garantire di disporre delle mappature e dei privilegi utente corretti. Anche semplici comandi informativi, come podman info , richiedono lo spazio dei nomi utente senza root. In quanto tale, uno spazio dei nomi utente non funzionale è un ostacolo per Podman senza root. Fortunatamente, questo non accade spesso. Quando lo fa, di solito scopriamo che è causato da autorizzazioni file insufficienti su newuidmap e/o newgidmap binari sul sistema (di solito manca una capacità di file). Reinstallare il pacchetto che li contiene (chiamato shadow-utils su RHEL, CentOS e Fedora) di solito lo risolveranno.

Una volta creato lo spazio dei nomi utente senza root, possiamo iniziare a eseguire i container. L'uso dei volumi con questi contenitori è il primo punto in cui la maggior parte degli utenti incontrerà le differenze pratiche tra Podman root e rootless. L'utente eseguirà un container con un volume montato e scoprirà immediatamente che il container non può accedere ai file nel volume, nonostante tutto sia apparentemente impostato correttamente.

Ad esempio, esaminiamo un semplice comando Podman:

podman run --user 1000:1000 -v /home/mheon/data:/data:Z ubi8 sh 

Questo comando è stato eseguito dal mio utente, mheon , con UID e GID impostati su 1000 (lo stesso utente che il contenitore è stato incaricato di utilizzare). Al mio utente sono stati assegnati 65536 UID e GID a partire da UID/GID 100000 tramite /etc/subuid e /etc/subgid . Il contesto SELinux è stato impostato in modo che il contenitore possa accedere alla directory tramite il :Z opzione sul montaggio del volume.

Tuttavia, accedi a /data cartella nel contenitore verrà negata e l'unico messaggio di errore che il sistema restituirà è un generico permesso negato dal nocciolo. Qualsiasi utente che non sapesse cosa sia uno spazio dei nomi utente non avrebbe idea di cosa c'è che non va.

Tuttavia, ora che sappiamo, la causa dovrebbe essere ovvia:la mappatura dell'utente significa che l'UID 1000 nel contenitore non è effettivamente l'UID 1000 sull'host. Puoi visualizzare l'utente nel contenitore e l'utente effettivo sull'host tramite il podman top comando:

mheon@podman-rhel8-test $ podman top -l user,huserUSER   HUSER1000   100999 

Qui, UTENTE è l'utente nel contenitore, mentre HUSER è l'utente sull'host.

Tuttavia, sapere perché qualcosa sta accadendo non significa che sappiamo come risolverlo. Vogliamo ancora eseguire il nostro contenitore Podman senza root con un volume specifico montato al suo interno. Come lo facciamo? Fortunatamente, ci sono molti modi per risolvere questo problema, che tratterò di seguito.

La prima soluzione

Il primo è semplice:il --user l'opzione può essere omessa dal contenitore, eseguendo il comando contenitore come root . Come notato sopra, per impostazione predefinita, Podman associa l'utente che esegue il contenitore a root nel contenitore, quindi ora accederemo al volume come UID/GID 1000 sull'host, nonostante sia root nel contenitore. In esecuzione come root in un contenitore rootato è un potenziale problema di sicurezza poiché stai eseguendo come root del sistema utente:se un utente malintenzionato rompesse il contenitore, sarebbe in grado di agire come root sul sistema. Il punto centrale di un container senza root è che questo non è mai vero:i problemi di sicurezza sono principalmente un fattore irrilevante.

Sfortunatamente, la soluzione di eseguire il contenitore come root cade piatta quando l'immagine viene scritta specificamente per utilizzare un utente non root. Alcune immagini del contenitore presentano script di punti di ingresso complessi per eliminare autorizzazioni che non possono essere modificate facilmente. Questi richiederanno una soluzione alternativa.

La seconda soluzione

La seconda opzione consiste nel concedere all'utente in esecuzione nel contenitore l'autorizzazione a leggere e scrivere la cartella montata dall'host. A partire da Podman v3.1.0, questo può essere fatto automaticamente tramite il :U opzione del volume su -v flag (ad es. -v /home/mheon/data:/data:Z,U ).

Quindi inserisci podman unshare chown 1000:1000 /home/mheon/data . Questa opzione del volume regolerà automaticamente la proprietà della directory, in modo che l'utente in esecuzione nel contenitore, qualunque sia l'utente UID 1000 nel contenitore a cui è mappato sull'host, sarà il proprietario della directory. Nelle versioni senza questo flag, podman unshare il comando può essere utilizzato per accedere allo spazio dei nomi utente senza root e quindi chown la directory di proprietà dell'utente che esegue il contenitore.

In questo caso, podman unshare chown 1000:1000 /home/mheon/data cambierebbe la proprietà della directory sull'host per l'utente e il gruppo associati all'UID/GID 1000 nello spazio dei nomi utente. Tieni presente che, se la proprietà viene modificata, tutte le directory principali sull'host richiederanno anche l'autorizzazione di esecuzione per tutti gli utenti (chmod a+x… ) autorizzazione per garantire l'accesso alla directory in questione.

Sfortunatamente, il chown approccio ha una propria serie di svantaggi. Il /home/mheon/data directory è nella directory home del mio utente, ma non è più di proprietà del mio utente (in questo caso, è di proprietà dell'utente e del gruppo 100999 ). Nello spazio dei nomi utente senza root, il mheon l'utente può agire come root e leggere, scrivere e modificare i file di proprietà di quell'utente; ma non può fare nessuna di queste cose al di fuori di essa. Podman fornisce un comando per inserire una shell all'interno dello spazio dei nomi utente senza root (podman unshare ) che possono essere utilizzati per modificare o rimuovere tali file, ma l'impossibilità di gestire questi file in altro modo è scomoda.

La terza soluzione

La terza opzione è usare il --userns=keep-id opzione per esecuzione Podman . Questo flag dice a Podman di fare due cose:in primo luogo, per impostare l'utente che il contenitore viene eseguito in base all'UID e al GID dell'utente che ha eseguito Podman (a meno che non sia esplicitamente sovrascritto da --user flag) e in secondo luogo, per riordinare gli utenti mappati nel contenitore in modo tale che l'utente che ha eseguito Podman sia mappato al proprio UID e GID, anziché a root (questo viene fatto tramite un secondo spazio dei nomi utente, annidato all'interno dello spazio dei nomi utente senza root, creato solo per questo contenitore). L'utente nello spazio dei nomi utente su cui è in esecuzione il contenitore non è root ma si associa ancora all'utente che esegue Podman (mheon ) sull'host e può quindi accedere alla directory montata nell'esempio /home/mheon/data .

Tuttavia, ciò non risolverà tutti gli errori di accesso. Un altro problema comune è il tentativo di montare in un file o dispositivo a cui l'utente che esegue Podman ha accesso, ma solo da un gruppo supplementare. Ad esempio, supponiamo che il mio utente, mheon , fa parte del kvm gruppo che possiede /dev/kvm e scelgo di montare /dev/kvm in un contenitore usando podman run -t -i -v /dev/kvm:/dev/kvm fedora bash . Il contenitore non sarà in grado di accedere a /dev/kvm , nonostante sia in esecuzione come utente mio sull'host (che dovrebbe avere accesso).

Il motivo è che, per motivi di sicurezza, i contenitori rilasceranno (per impostazione predefinita) tutti i gruppi aggiuntivi come parte della loro creazione. Questo comportamento può essere disabilitato, ma solo utilizzando il crun Runtime OCI (questo dovrebbe essere predefinito a partire da Podman 3.0 su tutte le distribuzioni tranne RHEL) passando un'annotazione speciale (--annotation run.oci.keep_original_groups=1 ).

Nel prossimo Podman v3.2.0, questo sarà disponibile tramite un argomento speciale per il group-add flag (--group-add keep-groups ). Tieni presente che, sebbene possiamo mantenere l'accesso a questi gruppi, non abbiamo l'autorizzazione per aggiungerli allo spazio dei nomi utente senza root:possiamo farlo solo agli utenti e ai gruppi a noi assegnati in /etc/subuid e /etc/subgid . Un ls -al su /dev/kvm nel contenitore troverà che è di proprietà di nobody:nobody (come il suo effettivo proprietario e gruppo, root:kvm , non sono mappati nel contenitore).

Tuttavia, è possibile accedervi perché, nonostante il kvm gruppo non fa parte dello spazio dei nomi utente, il processo contenitore è ancora parte del gruppo agli occhi del kernel. Questo è alquanto limitante in quanto non è possibile creare esplicitamente file come uno di questi gruppi supplementari (poiché nessuno non è un gruppo reale con cui possiamo interagire), ma è sufficiente per dare al contenitore l'accesso al contenuto sull'host che altrimenti non sarebbe in grado di raggiungere e le directory con il bit SUID di proprietà di un gruppo supplementare imposteranno comunque il proprietario corretto .

Un altro tipo di errore comune si verifica durante il pull di immagini con file o cartelle di proprietà di utenti con UID elevato. Qualsiasi file o cartella di proprietà di un UID o GID troppo grande per essere incluso nello spazio dei nomi utente produrrà un errore. In precedenza ho scritto un blog su questo e sulle potenziali soluzioni, che possono essere trovate qui.

Conclusione

Una delle caratteristiche più forti di Podman è il nostro forte supporto per i container senza radici e non è difficile capire perché le persone siano entusiaste. I container rootless sono facili da configurare, più sicuri di root container e può fare quasi tutto ciò che un container viene eseguito come root può fare. Naturalmente, la parola chiave è quasi —poiché l'esperienza complessiva con root e rootless è così simile, le differenze possono creare confusione e spesso non sono facili da spiegare. Dopo aver letto questo blog, dovresti avere una buona conoscenza di una delle più grandi di queste differenze e di come lavorare con Podman per far funzionare i tuoi container nel modo desiderato.


Linux
  1. Come installare Nextcloud con ISPConfig 3.1

  2. Aggiungi utente al gruppo in Linux, come farlo (con esempi)

  3. Esecuzione di Podman senza root come utente non root

  4. Come creare un nuovo utente con accesso Ssh?

  5. Come elencare i contenitori Docker

Come eseguire container come servizio Systemd con Podman

In che modo Cirrus CLI utilizza Podman per ottenere build senza root

Come modificare il codice nei contenitori Docker con il codice di Visual Studio

Come eseguire i contenitori Docker

Procedura:Introduzione a Windows Containers e Docker

Come gestire i container Docker