GNU/Linux >> Znalost Linux >  >> Linux

Jak ladit problémy se svazky namontovanými na kontejnerech bez kořenů

Jedna z nejčastějších otázek, které se mě v souvislosti s Podmanem bez rootu ptají, je, jak ladit problémy se svazky připojenými do kontejneru. Tato otázka je klamně těžká. V mnoha ohledech spuštění Podmana bez rootu je téměř totožné se spuštěním jako root . Bohužel to není vždy pravda a objemy jsou jednou z oblastí s nejvýraznějšími rozdíly. Zde podrobně vysvětlím, jaké jsou tyto rozdíly, jaké druhy chyb mohou způsobit a jak je můžete obejít. Pro začátek potřebujeme nějaké základní informace o tom, jak fungují kontejnery bez root, počínaje jednou z nejzákladnějších funkcí rootless Podman:User namespaces .

Prostor jmen uživatele

Jednou ze základních bezpečnostních funkcí kontejnerů jsou jmenné prostory linuxového jádra. Jmenný prostor je způsob, jak izolovat proces (nebo skupinu procesů) od zbytku systému omezením toho, co může vidět. Existuje mnoho různých jmenných prostorů, z nichž každý má jiný efekt. Například jmenný prostor PID omezuje, jaké PID může proces zobrazit – viditelné jsou pouze PID v rámci jmenného prostoru a jsou číslovány nezávisle na hostiteli. Proces má stále PID na hostiteli také, takže PID 2000 na hostiteli může být PID 1 ve jmenném prostoru. V době psaní tohoto článku jsou jmenné prostory, které jádro poskytuje, Mount, PID, Network, IPC, UTS, User, cgroup a time, z nichž každý izoluje jiný aspekt systému; ten, na kterém nám u tohoto blogu záleží nejvíce, je uživatelský jmenný prostor.

Uživatelské jmenné prostory izolují uživatele a skupiny dostupné v kontejneru od těch, které jsou dostupné hostitelskému systému. Uživatelský jmenný prostor funguje tak, že mapuje uživatele v kontejneru na uživatele na hostiteli. Mohli bychom například namapovat uživatele 0 až 1000 v kontejneru na uživatele 100 000 až 101 000 na hostiteli (skupiny jsou také mapovány stejným způsobem, ale pro jednoduchost se zaměříme na uživatele). Toto mapování funguje velmi podobně jako jmenný prostor PID, který jsme popsali výše, ale s uživateli.

Z hostitele, všechny přístupy z root v kontejneru (UID 0) bude vypadat jako od UID 100000. Uvnitř kontejneru se jakýkoli soubor vlastněný uživatelem 100000 na hostiteli bude jevit jako vlastněný UID 0 (root ). Zajímavou otázkou je, co se stane s uživateli, kteří nejsou namapováni do kontejneru – co když připojím svazek vlastněný uživatelem 1001 na hostiteli do kontejneru pomocí jmenného prostoru uživatele, který jsem popsal? Jádro v tomto případě namapuje jakékoli UID nebo GID neplatné v jmenném prostoru na UID/GID 65534, uživatele a skupinu s názvem nobody , což není normální skupina.

Stále je možné pracovat se soubory, které nikdo nevlastní pokud to oprávnění umožňují (např. můžete číst soubor vlastněný nikým který je světově čitelný a zapisovat do tohoto souboru, pokud je světově zapisovatelný), ale nemůžete změnit jeho vlastnictví. Uživatelské jmenné prostory také poskytují omezené verze specifických funkcí, které jsou normálně dostupné pouze pro root —typickým příkladem je, že uživatelské jmenné prostory mohou připojit určité typy souborových systémů, jako je tmpfs.

Uživatelské jmenné prostory jsou mimořádně užitečné, protože nám umožňují jednat jako root v kontejneru, aniž by byl ve skutečnosti root na systému. Můžeme použít jmenné prostory uživatelů k oddělení kontejnerů od různých uživatelů v systémech s více nájemci – kontejnery jednoho uživatele by byly provozovány jako UID 10000 až 10999, další jako 11000 až 11999 a tak dále. V každém kontejneru to vypadá, jako by aplikace byla root a díky omezeným možnostem může provádět většinu běžných operací (například instalovat balíčky).

Předpokládejme však, že se aplikaci podaří uniknout z kontejneru. V takovém případě neběží jako root v systému a neběží jako stejné UID a GID jako kontejnery jakéhokoli jiného uživatele – možnost útočit na různé části systému je extrémně omezená.

Jejich přijetí však bylo omezeno technickými omezeními – především skutečností, že až donedávna neexistoval způsob, jak přemapovat UID a GID na úrovni souborového systému. Abychom měli kontejner s UID 10000 až 10999, museli jsme vytvořit kopii jeho obrázku a poté chown každé UID v daném obrázku přidáním 10 000 ke stávajícímu UID. Tento chown může být velmi pomalý a (v mnoha souborových systémech) dramaticky zvyšuje množství požadovaného prostoru.

[ Začínáte s kontejnery? Podívejte se na tento bezplatný kurz. Nasazení kontejnerových aplikací:technický přehled. ]

Kontejnery bez kořenů

Kde se uživatelské jmenné prostory staly extrémně užitečnými a oblíbenými, jsou kontejnery bez kořenů. Uživatel bez oprávnění root má v Linuxu přístup pouze k jednomu UID a GID (jejich vlastním). Kontejnery však očekávají, že budou mít přístup k více než jednomu uživateli a skupině – mnoho souborů v obrázcích kontejnerů není vlastněno rootem a aplikace budou v kontejneru často spouštěny jako uživatel bez oprávnění root, aby bylo v kontejneru vynuceno oddělení oprávnění.

Pro některá prostředí (vysoce výkonné výpočty, HPC, které je pozoruhodné), je přijatelné (dokonce žádoucí) mít v kontejneru pouze jednoho uživatele a skupinu. Pro většinu ostatních prostředí je jeden uživatel a skupina vážným omezením užitečnosti kontejneru. Můžeme použít uživatelské jmenné prostory k získání těchto dalších uživatelů a skupin, které potřebujeme, aby se chovali jako typický kontejner.

K dosažení těchto dalších uživatelů a skupin však potřebujeme zvýšená oprávnění. Toto je newuidmap a newgidmap spustitelné soubory (a /etc/subuid a /etc/subgid konfigurační soubory, které čtou) dělají – poskytují nám přístup k bloku uživatelů a skupin, které jsou pak mapovány do jmenného prostoru uživatelů pro použití v kontejnerech bez kořenů. Omezení uživatelských jmenných prostorů ohledně podpory souborového systému stále platí do určité míry, ale jsou zmírněna tím, že každý uživatel používá pouze jednu sadu UID a GID.

Za zmínku stojí také skutečnost, že jádro automaticky zpracuje chown operaci pro nás, pokud rozbalíme obrázek uvnitř uživatelského jmenného prostoru. Uživatelské posunutí jmenného prostoru zajišťuje, že při vytváření je přiřazeno správné UID, namísto toho, aby ho běhový modul kontejneru nastavoval ručně.

Přidané schopnosti uživatelského jmenného prostoru jsou také zásadní pro některé věci, které kontejnery bez rootu potřebují – bez možnosti připojit souborové systémy FUSE a tmpfs by byly kontejnery bez root mnohem omezenější (do té míry, že by byly téměř nepoužitelné).

Uživatelské jmenné prostory v Podman

Nyní, když rozumíme tomu, jak uživatelské jmenné prostory obecně fungují, pojďme diskutovat o tom, jak jsou implementovány v Podmanu bez root.

Všechny kontejnery Podman bez root jsou spuštěny v uživatelském jmenném prostoru, i když uživatel nemá k dispozici více než jedno UID a GID. Všechny kontejnery uživatele sdílejí jeden uživatelský jmenný prostor, který je otevřený procesem pozastavení bez kořenů. Jmenné prostory jsou obvykle ořezávány jádrem, když v nich nejsou žádné další procesy, takže udržování procesu, který nedělá nic jiného než spánek a nikdy se neukončí, udrží uživatelský jmenný prostor naživu.

První věc, kterou proces Podman bez root udělá, je připojit se k bezkořenovému uživatelskému jmennému prostoru (nebo vytvořit nový jmenný prostor a pozastavit proces, pokud ještě neexistují). Jako součást vytváření uživatelského jmenného prostoru spustí Podman newuidmap a newgidmap spustitelné soubory k udělení jakýchkoli dalších UID a GID, která byla uživateli přidělena v /etc/subuid a /etc/subgid (výchozí částka udělená při vytvoření uživatele je 65536 od každého). Mapování uživatelů dostupné v podman info si můžete prohlédnout v idMappings pole:

mheon@podman-rhel8-test $ podman info idmappings:gidmap:- kontejner_id:0 host_id:1000 Velikost:1- kontejner_id:1 Host_ID:100000 Velikost:6536 UIDMap:- kontejner_id:0 host_id:1000 Velikost:1000 velikost:1000. :1    - container_id:1      host_id:100000      velikost:65536

Vezměte prosím na vědomí, že uživatelský jmenný prostor bez root se ve výchozím nastavení znovu nevytvoří, pokud /etc/subuid a /etc/subgid soubory jsou změněny; to se provádí spuštěním migrace systému podman příkaz. Pokud jste tyto soubory upravili a zdá se, že Podman vaše změny nerozpoznal, spusťte tento příkaz.

Všechny příkazy Podman bez root se spouštějí ve jmenném prostoru uživatele bez root, který je vytvořen, abychom zajistili, že máme správná uživatelská mapování a oprávnění. Dokonce i jednoduché informační příkazy, jako je podman info , vyžadují uživatelský jmenný prostor bez root. Jako takový je nefunkční uživatelský jmenný prostor stopkou pro Podman bez root. Naštěstí se to nestává často. Když se tak stane, obvykle zjistíme, že je to způsobeno nedostatečnými oprávněními k souboru na newuidmap a/nebo newgidmap binární soubory v systému (obvykle chybí možnost souboru). Přeinstalujte balíček, který je obsahuje (nazývaný shadow-utils na RHEL, CentOS a Fedora) to obvykle vyřeší.

Jakmile je vytvořen uživatelský jmenný prostor bez root, můžeme začít spouštět kontejnery. Použití svazků s těmito kontejnery je prvním bodem, ve kterém se většina uživatelů setká s praktickými rozdíly mezi root a rootless Podman. Uživatel spustí kontejner s připojeným svazkem a okamžitě zjistí, že kontejner nemá přístup k souborům ve svazku, přestože vše je zdánlivě nastaveno správně.

Podívejme se například na jednoduchý příkaz Podman:

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

Tento příkaz spouští můj uživatel mheon s UID a GID nastaveným na 1000 (stejný uživatel, který měl kontejner používat). Mému uživateli bylo přiděleno 65536 UID a GID počínaje UID/GID 100000 přes /etc/subuid a /etc/subgid . Kontext SELinux byl nastaven tak, aby kontejner mohl přistupovat k adresáři přes :Z možnost na připojení svazku.

Nicméně, přístup k /data složka v kontejneru bude odepřena a jediná chybová zpráva, kterou systém vrátí, je obecné oprávnění odepřeno z jádra. Každý uživatel, který neznal, co je uživatelský jmenný prostor, nebude mít ponětí, co je špatně.

Nyní, když to víme, by však měla být příčina zřejmá:Mapování uživatele znamená, že UID 1000 v kontejneru ve skutečnosti není UID 1000 na hostiteli. Uživatele v kontejneru a skutečného uživatele na hostiteli můžete zobrazit pomocí podman top příkaz:

mheon@podman-rhel8-test $ podman top -l uživatel,huserUSER   HUSER1000   100999 

Zde, USER je uživatel v kontejneru, zatímco HUSER je uživatel na hostiteli.

Ale vědět, proč se něco děje, ještě neznamená, že víme, jak to napravit. Stále chceme provozovat náš bezkořenový kontejner Podman se specifickým objemem namontovaným do něj. jak to uděláme? Naštěstí existuje mnoho způsobů, jak to napravit, kterým se budu věnovat níže.

První řešení

První je jednoduchý:--user možnost lze z kontejneru vynechat a spustit příkaz kontejner jako root . Jak je uvedeno výše, Podman ve výchozím nastavení mapuje uživatele spouštějícího kontejner na root v kontejneru – takže nyní budeme přistupovat ke svazku jako UID/GID 1000 na hostiteli, přestože jsme root v kontejneru. Spuštěn jako root v kořenovém kontejneru je potenciální bezpečnostní problém, protože běžíte jako root systému uživatel – pokud by se útočník dostal z kontejneru, mohl by fungovat jako root na systému. Celý smysl kontejneru bez kořenů je v tom, že to nikdy není pravda – problémy se zabezpečením jsou primárně nefaktorem.

Bohužel řešení spouštění kontejneru jako root spadne, když je obraz specificky napsán pro použití uživatele bez oprávnění root. Některé obrázky kontejnerů obsahují složité skripty vstupních bodů k odstranění oprávnění, která nelze snadno upravit. Ty budou vyžadovat alternativní řešení.

Druhé řešení

Druhou možností je udělit uživateli běžícímu v kontejneru oprávnění ke čtení a zápisu do složky připojené z hostitele. Od verze Podman v3.1.0 to lze provést automaticky pomocí :U volume na -v příznak (např. -v /home/mheon/data:/data:Z,U ).

Dále zadejte podman unshare chown 1000:1000 /home/mheon/data . Tato volba svazku automaticky upraví vlastnictví adresáře, takže uživatel běžící v kontejneru – bez ohledu na to, na který uživatel UID 1000 v kontejneru je namapován na hostiteli – bude vlastníkem adresáře. Ve verzích bez tohoto příznaku podman unshare příkaz lze použít ke vstupu do jmenného prostoru uživatele bez root a poté chown adresář, který bude vlastnit uživatel spouštějící kontejner.

V tomto případě podman unshare chown 1000:1000 /home/mheon/data by změnilo vlastnictví adresáře na hostiteli na uživatele a skupinu, které se mapují na UID/GID 1000 v uživatelském jmenném prostoru. Upozorňujeme, že pokud dojde ke změně vlastnictví, všechny nadřazené adresáře na hostiteli budou také vyžadovat oprávnění ke spuštění pro všechny uživatele (chmod a+x… ) oprávnění k zajištění přístupu k příslušnému adresáři.

Bohužel, chown přístup má své vlastní nevýhody. /home/mheon/data adresář je v domovském adresáři mého uživatele, ale již jej nevlastní můj uživatel (v tomto případě jej vlastní uživatel a skupina 100999 ). V uživatelském jmenném prostoru bez root, mheon uživatel může vystupovat jako root a číst, zapisovat a upravovat soubory vlastněné tímto uživatelem; ale nemůže dělat žádnou z těchto věcí mimo něj. Podman poskytuje příkaz pro zadání shellu uvnitř jmenného prostoru uživatele bez root (podman unshare ), které lze použít k úpravě nebo odstranění takových souborů, ale nemožnost tyto soubory spravovat jinak je nepohodlná.

Třetí řešení

Třetí možností je použít --userns=keep-id možnost spustit podman . Tento příznak říká Podmanovi, aby udělal dvě věci:Za prvé, nastavit uživatele, který kontejner spouští, jako UID a GID uživatele, který spustil Podman (pokud to není výslovně přepsáno --user příznak) a za druhé, změnit pořadí uživatelů namapovaných do kontejneru tak, aby uživatel, který spustil Podman, byl mapován na jejich vlastní UID a GID, nikoli na root (to se provádí prostřednictvím druhého uživatelského jmenného prostoru, vnořeného do uživatelského jmenného prostoru bez root, vytvořeného pouze pro tento kontejner). Uživatel v uživatelském jmenném prostoru, pod kterým kontejner běží, není root ale stále se mapuje na uživatele, který spouští Podman (mheon ) na hostiteli a může tak přistupovat k adresáři připojenému v příkladu /home/mheon/data .

To však nevyřeší všechny chyby přístupu. Dalším běžným problémem je pokus o připojení k souboru nebo zařízení, ke kterému má přístup uživatel s Podmanem, ale pouze prostřednictvím doplňkové skupiny. Řekněme například, že můj uživatel, mheon , je součástí kvm skupina, která vlastní /dev/kvm a rozhodl jsem se připojit /dev/kvm do kontejneru pomocí podman spustit -t -i -v /dev/kvm:/dev/kvm fedora bash . Kontejner nebude mít přístup k /dev/kvm , navzdory skutečnosti, že běží jako můj uživatel na hostiteli (který by měl mít přístup).

Důvodem je to, že z bezpečnostních důvodů kontejnery (ve výchozím nastavení) při vytváření zruší všechny další skupiny. Toto chování lze zakázat, ale pouze pomocí crun Runtime OCI (toto by mělo být výchozí od Podman 3.0 na všech distribucích kromě RHEL) předáním speciální anotace (--annotation run.oci.keep_original_groups=1 ).

V nadcházející verzi Podman v3.2.0 to bude dostupné prostřednictvím speciálního argumentu pro group-add příznak (--group-add keep-groups ). Vezměte prosím na vědomí, že i když si můžeme ponechat přístup k těmto skupinám, nemáme oprávnění je přidávat do jmenného prostoru uživatelů bez root – to můžeme udělat pouze uživatelům a skupinám, které jsou nám přiděleny v /etc/subuid a /etc/subgid . ls -al na /dev/kvm v kontejneru zjistí, že je vlastněn nobody:nobody (jako jeho skutečný vlastník a skupina, root:kvm , nejsou mapovány do kontejneru).

Lze k němu však přistupovat, protože navzdory skutečnosti, že kvm skupina není součástí jmenného prostoru uživatele, proces kontejneru je v očích jádra stále součástí skupiny. To je poněkud omezující v tom, že není možné explicitně vytvářet soubory jako jednu z těchto doplňkových skupin (jako nikdo není skutečná skupina, se kterou bychom mohli interagovat), ale stačí dát kontejneru přístup k obsahu na hostiteli, ke kterému by se jinak nemohl dostat, a adresáře s bitem SUID vlastněným doplňkovou skupinou budou stále nastavovat správného vlastníka .

Další typ běžné chyby se vyskytuje při stahování obrázků se soubory nebo složkami vlastněnými uživateli s vysokým UID. Jakýkoli soubor nebo složka vlastněná UID nebo GID, které jsou příliš velké na to, aby mohly být zahrnuty do prostoru jmen uživatele, způsobí chybu. Dříve jsem o tom a potenciálních řešeních napsal blog, který lze nalézt zde.

Závěr

Jednou z nejsilnějších vlastností společnosti Podman je naše silná podpora kontejnerů bez kořenů a není těžké pochopit, proč jsou lidé nadšení. Kontejnery bez root se snadno nastavují a jsou bezpečnější než root kontejnery a může dělat téměř vše, co kontejner spouští jako root může udělat. Samozřejmě, že klíčové slovo je téměř —protože celková zkušenost s root a rootless je tak podobná, rozdíly mohou být matoucí a často je není snadné vysvětlit. Po přečtení tohoto blogu byste měli dobře chápat jeden z největších z těchto rozdílů a jak pracovat s Podmanem, aby vaše kontejnery fungovaly tak, jak chcete.


Linux
  1. Jak nainstalovat Nextcloud s ISPConfig 3.1

  2. Přidat uživatele do skupiny v Linuxu, jak na to (s příklady)

  3. Jak přidat uživatele do kontejneru Docker?

  1. Jak odstranit uživatelské účty pomocí domovského adresáře v systému Linux

  2. Jak vytvořit nového uživatele s přístupem Ssh?

  3. Jak vypsat Docker kontejnery

  1. Jak přidat podporu jádra PPP do kontejnerů OpenVZ

  2. Spuštění rootless Podman jako uživatel bez root

  3. Jak vytvořit a spustit kontejnery LXC Linux pomocí příkazů LXC