m4nifest0-tech

Cerca nel sito

Virtualizzazione

Container LXC non privilegiati su Proxmox: bind mount e mappatura degli UID

Perché un bind mount in un container LXC non privilegiato mostra file di nobody e come risolvere con il calcolo degli UID oppure con lxc.idmap, subuid e subgid.
Pubblicato il 7 min di letturaLivello: Avanzato

Un container LXC non privilegiato con un bind mount mostra spesso file e cartelle intestati a nobody:nogroup, anche quando sull'host i permessi sembrano corretti. Non è un difetto di Proxmox: dipende da come il kernel traduce gli identificatori utente tra container e host. Capire il meccanismo permette di scegliere con cognizione tra due soluzioni, il calcolo dei permessi sull'host e la mappatura personalizzata con lxc.idmap, ciascuna con compromessi di sicurezza diversi.

Container privilegiati e non privilegiati

In un container privilegiato gli identificatori coincidono con quelli dell'host: il root del container è il root dell'host e, in caso di evasione, a limitare i danni restano solo i meccanismi di isolamento (namespace, AppArmor, capabilities). In un container non privilegiato il kernel usa uno user namespace: l'intervallo di UID e GID visto dal container è traslato in un intervallo diverso sull'host, così il root del container è un utente senza privilegi sull'host.

Su Proxmox VE la traslazione predefinita parte da 100000 e copre 65536 identificatori, come dichiara la voce root:100000:65536 in /etc/subuid e /etc/subgid. La regola è UID host = 100000 + UID container.

Utente nel containerUID nel containerUID sull'host
root0100000
www-data33100033
primo utente creato1000101000
nobody65534165534

Il kernel verifica i permessi con gli ID dell'host: il container ne vede soltanto una traduzione. Un ID senza corrispondenza nel namespace compare come 65534 (nobody), ed è ciò che si osserva con un bind mount quando la cartella appartiene a un ID dell'host fuori dall'intervallo mappato, per esempio 1000. Rinunciare al container non privilegiato per aggirare il problema sposta il rischio sull'host: conviene correggere i permessi, non l'isolamento.

Il bind mount

Un bind mount espone una directory dell'host dentro il container. Per il container 101:

Sul nodo che ospita il container
pct set 101 -mp0 /tank/dati,mp=/mnt/dati

La forma generale è pct set <vmid> -mp0 /percorso/host,mp=/percorso/container. Il comando aggiunge la riga mp0: /tank/dati,mp=/mnt/dati a /etc/pve/lxc/101.conf; il punto di mount è attivo dal successivo avvio del container. Alcune regole:

  • La directory deve esistere sull'host e, in un cluster, su ogni nodo in cui il container può essere avviato o migrato (si veda Cluster Proxmox VE a tre nodi).
  • Non si montano directory di sistema come /, /var o /etc: la documentazione le indica come un rischio serio per l'host.
  • Il contenuto dei bind mount non è incluso nei backup di vzdump: i dati vanno protetti a parte, come descritto in Strategia di backup 3-2-1 con Proxmox Backup Server.

Calcolare i permessi sull'host

Con la mappatura predefinita basta intestare la directory all'ID dell'host che corrisponde all'utente del container. Per l'UID e il GID 1000:

Sull'host
chown -R 101000:101000 /tank/dati

La verifica si fa con gli ID numerici, perché i nomi dipendono dal namespace in cui vengono risolti.

Sull'host
ls -ln /tank/dati
Sul nodo, dentro il container
pct exec 101 -- ls -ln /mnt/dati
Vista dell'host
-rw-r--r-- 1 101000 101000 0 Apr  8 10:00 nota.txt
Vista del container
-rw-r--r-- 1 1000 1000 0 Apr  8 10:00 nota.txt

Il metodo non richiede modifiche alla configurazione del container. Il limite è che sull'host i dati risultano intestati a un ID insolito: è scomodo se la stessa directory è condivisa con altri servizi, per esempio Samba o NFS, o se i file esistono già con un altro proprietario. Per impostazione predefinita tutti i container non privilegiati usano lo stesso intervallo, quindi un bind mount condiviso tra più container mostra ovunque gli stessi ID.

Mappatura personalizzata con lxc.idmap

Quando i file hanno già un proprietario reale sull'host, si può lasciare che un singolo ID attraversi il namespace senza traslazione: l'UID 1000 del container diventa l'UID 1000 dell'host, mentre tutti gli altri restano traslati. Per ogni tipo (u per gli UID, g per i GID) servono tre intervalli, che devono coprire da 0 a 65535 senza lacune né sovrapposizioni.

/etc/pve/lxc/101.conf (righe da aggiungere)
lxc.idmap = u 0 100000 1000
lxc.idmap = g 0 100000 1000
lxc.idmap = u 1000 1000 1
lxc.idmap = g 1000 1000 1
lxc.idmap = u 1001 101001 64535
lxc.idmap = g 1001 101001 64535

Ogni riga ha la forma lxc.idmap = <tipo> <primo ID nel container> <primo ID sull'host> <quantità>. Qui gli ID da 0 a 999 vanno a 100000-100999, l'ID 1000 resta 1000 e gli ID da 1001 a 65535 vanno a 101001-165535: 1000 + 1 + 64535 fa 65536.

LXC avvia il container come root e applica la mappatura con newuidmap e newgidmap, che accettano solo gli ID delegati all'utente in /etc/subuid e /etc/subgid. La voce predefinita copre 100000-165535, quindi a root va delegato anche l'ID 1000.

Sull'host
echo 'root:1000:1' >> /etc/subuid
echo 'root:1000:1' >> /etc/subgid

Il formato è utente:primo-id:quantità. Senza queste voci il container non si avvia e l'errore cita newuidmap o newgidmap; per diagnosticare conviene avviarlo in primo piano con lxc-start -F -n 101. La mappatura viene letta a ogni avvio, quindi il container va riavviato.

Sul nodo
pct shutdown 101
pct start 101

Attenzione

Mappare un ID senza traslazione riduce l'isolamento: un processo che gira come UID 1000 nel container agisce sull'host come UID 1000, con accesso a tutto ciò che quell'utente può leggere o scrivere, non solo alla directory condivisa. Conviene mappare un ID riservato allo scopo, per esempio un account di servizio dell'host senza shell di login, e non quello di un utente reale. L'ID dell'host non deve coincidere con quello del container: basta sostituire il secondo numero delle righe u 1000 1000 1 e g 1000 1000 1 (e le voci in subuid e subgid) con l'ID scelto, purché fuori dall'intervallo 100000-165535.

Importante

In un container già in uso, i file del suo filesystem che appartengono all'ID rimappato (per esempio la home dell'utente 1000, salvata su disco come 101000) risultano nobody dopo la modifica: Proxmox non li riallinea. Conviene applicare la mappatura a un container nuovo oppure correggere la proprietà dall'host a container fermo.

Suggerimento

Se più container devono solo condividere una directory, spesso basta mappare un GID e lavorare con i permessi di gruppo (directory con bit setgid e g+rwX): gli UID restano traslati e nessun utente dell'host viene esposto.

Nota

La documentazione delle versioni più recenti di Proxmox VE (serie 9.x) descrive anche un'opzione idmap per il singolo punto di mount, che evita di rimappare l'intero container. Conviene controllare che la versione in uso la supporti; lxc.idmap resta valido su Proxmox VE 8 e 9.

Verificare la mappatura

Sul nodo
pct exec 101 -- cat /proc/self/uid_map
Output atteso (la seconda colonna riporta gli ID dell'host)
         0     100000       1000
      1000       1000          1
      1001     101001      64535

Il controllo definitivo consiste nel creare un file dal container e leggerne il proprietario numerico sull'host.

Sul nodo
pct exec 101 -- su -s /bin/sh -c 'id && touch /mnt/dati/prova' utente-esempio
ls -ln /tank/dati/prova
Output atteso
uid=1000(utente-esempio) gid=1000(utente-esempio) groups=1000(utente-esempio)
-rw-r--r-- 1 1000 1000 0 Apr  8 10:05 prova

Con la mappatura personalizzata l'UID è 1000 su entrambi i lati; con quella predefinita sull'host sarebbe 101000. Per una diagnosi rapida:

  • Proprietario nobody nel container: l'ID dell'host non rientra nell'intervallo mappato.
  • Container che non parte dopo la modifica: voce mancante in subuid o subgid, oppure intervalli che non sommano 65536.
  • Permesso negato con gli ID corretti: controllare i permessi delle directory superiori; con i bind mount nei container non privilegiati le ACL non sono utilizzabili.

Articoli correlati