GNU/Linux >> Znalost Linux >  >> Linux

Ruční vytváření kontejneru Linux pomocí jmenných prostorů

Není to tak dávno, co jsem napsal článek o přehledu nejběžnějších jmenných prostorů. Tyto informace je skvělé mít a jsem si jistý, že do jisté míry dokážete extrapolovat, jak byste tyto znalosti mohli dobře využít. Normálně není můj styl nechávat věci tak otevřené. Takže v příštích několika článcích věnuji nějaký čas demonstraci několika důležitějších jmenných prostorů optikou vytváření primitivního linuxového kontejneru. V určitém smyslu zapisuji své zkušenosti s technikami, které používám při odstraňování problémů s linuxovým kontejnerem na klientském webu. S ohledem na to začínám se založením jakéhokoli kontejneru, zvláště když jde o bezpečnost.

Něco o možnostech Linuxu

Zabezpečení v systému Linux může mít mnoho podob. Pro účely tohoto článku mi jde hlavně o bezpečnost, pokud jde o oprávnění k souborům. Připomínáme, že vše v systému Linux je nějaký druh souboru, a proto jsou oprávnění k souborům první linií obrany proti aplikaci, která se může chovat špatně.

Primárním způsobem, jakým Linux zpracovává oprávnění k souborům, je implementace uživatelů . Existují normální uživatelé pro které Linux používá kontrolu oprávnění a existuje superuživatel který obchází většinu (pokud ne všechny) kontroly. Stručně řečeno, původní model Linuxu byl všechno nebo nic.

Chcete-li to obejít, některé binární soubory programů mají set uid bit na nich nastaven. Toto nastavení umožňuje programu běžet jako uživatel, který vlastní binární soubor. passwd utility je toho dobrým příkladem. Tento nástroj může v systému spustit každý uživatel. K interakci s shadow potřebuje mít v systému zvýšená oprávnění soubor, který ukládá hashe pro uživatelská hesla v systému Linux. Zatímco passwd binární má vestavěné kontroly, které zajišťují, že jeden normální uživatel nemůže změnit heslo jiného uživatele, mnoho aplikací nemá stejnou úroveň kontroly, zvláště pokud správce systému zapnul set uid bit.

Možnosti Linuxu byly vytvořeny, aby poskytovaly podrobnější aplikaci bezpečnostního modelu. Namísto spouštění binárního souboru jako root můžete použít pouze specifické schopnosti, které aplikace vyžaduje, aby byla efektivní. Od jádra Linuxu 5.1 existuje 38 funkcí. Manuálové stránky pro schopnosti jsou ve skutečnosti docela dobře napsané a popisují každou schopnost.

Sada schopností je způsob, jakým lze vláknům přiřadit schopnosti. Stručně řečeno, existuje celkem pět sad schopností, ale pro tuto diskusi jsou relevantní pouze dvě z nich:Efektivní a Povolená.

Účinné :Jádro ověří každou privilegovanou akci a rozhodne, zda povolit nebo zakázat systémové volání. Pokud má vlákno nebo soubor efektivní schopnost, můžete provést akci související s efektivní schopností.

Povoleno :Povolené možnosti ještě nejsou aktivní. Pokud to však proces povolil to znamená, že proces sám se může rozhodnout eskalovat své privilegium na efektivní privilegium.

Chcete-li zjistit, jaké možnosti daný proces může mít, můžete spustit getpcaps ${PID} příkaz. Výstup tohoto příkazu bude vypadat jinak v závislosti na distribuci Linuxu. Na RHEL/CentOS získáte celý seznam schopností:

[root@CentOS8 ~]# getpcaps $$
Capabilities for `1304': = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,38,39+ep

Pokud spustíte příkaz man 7 capabilities , najdete seznam všech těchto schopností spolu s popisem každé z nich. V některých distribucích, jako je Ubuntu nebo Arch, spuštění stejného příkazu jednoduše vede k tomuto:

[root@Arch ~]# getpcaps $$
414429: =ep

Před = je mezera podepsat. Tento prázdný znak je zaměnitelný s klíčovým slovem all . Jak jste možná uhodli, znamená to, že všechny funkce dostupné v systému jsou poskytovány jak v E efektivní a P opuštěné sady schopností.

Proč je toto důležité? Za prvé, sady schopností jsou svázány s uživatelským jmenným prostorem (o kterém pojednávám níže). Prozatím to znamená, že každý jmenný prostor bude mít svou vlastní sadu schopností, které se vztahují pouze na jeho vlastní jmenný prostor. Předpokládejme, že máte jmenný prostor nazvaný omezený . Je možné, že omezeno vypadá jako by měl všechny správné možnosti, jak je vidět u getpcaps příkaz. Pokud však omezeno byl vytvořen procesem a jmenným prostorem, který neměl nastavenou plnou kapacitu (jako je běžný uživatel), omezený nelze v systému udělit více oprávnění než proces vytváření.

Abychom to shrnuli, schopnosti, i když nejde o technologii jmenného prostoru, pracují ruku v ruce při určování toho, co a jak mohou procesy uvnitř jmenného prostoru fungovat.

[ Mohlo by se vám také líbit: Spuštění aplikace Podman bez root jako uživatel bez oprávnění root ]

Prostor jmen uživatele

Než se pustíte do vytváření uživatelského jmenného prostoru, zde je stručná rekapitulace účelu tohoto jmenného prostoru. Jak jsem popsal v dřívějším článku, uživatelská jména a v konečném důsledku i uživatelské identifikační číslo (UID) jsou jednou z vrstev zabezpečení, kterou systém použije k zajištění toho, že lidé a procesy nebudou mít přístup k věcem, které nemají povoleno.

Teorie za uživatelským jmenným prostorem

Uživatelský jmenný prostor je způsob, jak může mít kontejner (sada izolovaných procesů) jinou sadu oprávnění než samotný systém. Každý kontejner zdědí svá oprávnění od uživatele, který vytvořil nový uživatelský jmenný prostor. Například ve většině systémů Linux začínají běžná ID uživatelů na hodnotě 1 000 nebo vyšší. Ve zbytku této řady používám uživatele s názvem container-user , který má následující ID (kontexty SELinux jsou u těchto ukázek vynechány):

uid=1000(container-user) gid=1000(container-user) groups=1000(container-user)

Je důležité poznamenat, že pokud to není úmyslně omezeno správcem systému, teoreticky může každý uživatel vytvořit nový uživatelský jmenný prostor. To však neposkytuje žádné zmatky ze strany administrátorů samotného systému. Uživatelské jmenné prostory jsou hierarchie. Zvažte níže uvedený diagram:

V tomto diagramu černé čáry ukazují tok stvoření. Uživatel container-user vytvoří jmenný prostor pro uživatele s názvem app-user . Teoreticky by se jednalo o webový frontend nebo jinou aplikaci. Dále uživatel aplikace vytvoří uživatelský jmenný prostor pro java-user . V tomto jmenném prostoru java-user vytvoří jmenný prostor pro db-user .

Protože se jedná o hierarchii, kontejner-uživatel může vidět a přistupovat ke všem souborům vytvořeným kterýmkoli jmenným prostorem vytvořeným z jeho UID. Podobně, protože kořen uživatel v systému Linux může vidět všechny a pracovat s nimi soubory v systému, včetně těch, které vytvořil container-user , kořen uživatel (reprezentovaný červenou čarou) může mít úplnou autoritu nad všemi jmennými prostory.

Opak však neplatí. db-user uživatel v tomto případě nemůže vidět ani interagovat s ničím nad ním. Pokud je mapování ID zachováno stejné (výchozí zásada), uživatel aplikace , uživatel Java a db-user všechny mají stejné UID. Ačkoli však sdílejí stejné UID, db-user nemůže komunikovat s uživatelem java-user , která nemůže komunikovat s uživatelem aplikace , a tak dále.

Jakákoli oprávnění udělená v uživatelském jmenném prostoru platí pouze pro jeho vlastní jmenný prostor a případně jmenné prostory pod ním.

Ruky s uživatelskými jmennými prostory

Chcete-li vytvořit nový uživatelský jmenný prostor, jednoduše použijte unshare -U příkaz:

[container-user@localhost ~]$ PS1='\u@app-user$ ' unshare -U
nobody@app-user$

Výše uvedený příkaz obsahuje PS1 proměnná, která jednoduše změní shell, takže je snazší určit, ve kterém jmenném prostoru je shell aktivní. Je zajímavé, že si všimnete, že uživatel je nikdo :

nobody@app-user$ whoami
nobody
nobody@app-user$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)

Důvodem je, že ve výchozím nastavení neprobíhá žádné mapování ID uživatele. Pokud není definováno žádné mapování, jmenný prostor jednoduše používá pravidla vašeho systému k určení, jak zacházet s nedefinovaným uživatelem.

Pokud však vytvoříte jmenný prostor takto:

PS1='\u@app-user$ ' unshare -Ur

Mapování se vám automaticky vytvoří:

root@app-user$ cat /proc/$$/uid_map
         0       1000       1

Tento soubor představuje následující:

ID-inside-ns      ID-outside-ns      range

Rozsah hodnota představuje počet uživatelů k mapování. Pokud by to bylo například 0 1000 4 , mapování by bylo takové

0    1000
1    1001
2    1002
3    1003

A tak dále. Většinou se opravdu staráte jen o kořen uživatelské mapování, ale v případě potřeby je tato možnost k dispozici. Co se stane, když vytvoříte java-user jmenný prostor?

root@app-user$ PS1='\u@java-user$ ' unshare -Ur
root@java-user$ 

Jak se očekávalo, výzva shellu se změní a vy jste uživatel root, ale jak vypadá mapování UID?

root@java-user$ cat /proc/$$/uid_map
         0          0          1

Nyní vidíte, že máte 0 na 0 mapování. Je to proto, že uživatel vytvářející nový jmenný prostor se používá pro proces mapování ID. Protože jste byli root v předchozím jmenném prostoru má nový jmenný prostor mapování root rootovat . Nicméně, protože root v uživateli aplikace jmenný prostor nemá root v systému ani nový jmenný prostor root uživatel.

Kromě pouhé kontroly uid_map , můžete také ověřit mimo jmenný prostor, zda jsou dva procesy ve stejném jmenném prostoru. Samozřejmě musíte nejprve najít PID procesu, ale s tím, co máte v ruce, můžete spustit následující příkaz:

readlink /proc/$PID/ns/user

Abych to usnadnil, spustil jsem následující:

[container-user@localhost ~]$ PS1='\u@app-user$ ' unshare -Ur
root@app-user$ sleep 100000

V jiném terminálu jsem vykopal PID a použil readlink příkaz na tomto PID a také na aktuálním shellu:

[root@localhost ~]# readlink /proc/1307/ns/user 
user:[4026532275]

[root@localhost ~]# readlink /proc/$$/ns/user
user:[4026531837]

Jak vidíte, uživatelský odkaz je jiný. Pokud by operovali ve stejném jmenném prostoru, vypadalo by to takto:

[root@localhost ~]# readlink /proc/1424/ns/user 
user:[4026532275]

[root@localhost ~]# readlink /proc/1307/ns/user 
user:[4026532275]

Největší výhodou uživatelského jmenného prostoru je možnost spouštět kontejnery bez oprávnění root. Navíc, v závislosti na tom, jak nastavíte mapování UID, se můžete zcela vyhnout tomu, že v daném jmenném prostoru uživatele bude superuživatel. To znamená, že uvnitř tohoto typu jmenného prostoru není možné spouštět žádné privilegované procesy.

Poznámka :Uživatel jmenný prostor řídí každý jmenný prostor. To znamená, že schopnosti jmenného prostoru přímo souvisejí se schopnostmi jeho nadřazeného uživatele jmenný prostor.

Původní, úplný kořen uživatelský jmenný prostor vlastní všechny jmenné prostory v systému v níže uvedeném diagramu. Tento vztah má potenciál být obousměrný. Pokud proces běží v netu jmenný prostor běží jako root , může to ovlivnit všechny ostatní procesy vlastněné rootem uživatelský jmenný prostor. I když však vytvoření neprivilegovaného uživatelského jmenného prostoru umožňuje, aby nový uživatelský jmenný prostor měl přístup ke zdrojům v jiných jmenných prostorech, nesmí je měnit, protože je nevlastní. Tedy, zatímco proces v neprivilegovaném jmenném prostoru může ping IP (která závisí na síti jmenný prostor), nemusí změnit konfiguraci sítě hostitele.

Mnoho věcí mimo to, co si představujete jako linuxové kontejnery, využívá jmenné prostory. Linuxový balíčkový formát Flatpak používá uživatelské jmenné prostory a také některé další technologie, aby poskytl aplikační karanténu. Flatpaks sdružují všechny knihovny aplikace do stejného distribučního souboru balíčku. To umožňuje počítači se systémem Linux přijímat nejaktuálnější aplikace, aniž byste se museli starat o to, zda máte správnou verzi glibc nainstalován např. Schopnost mít je ve svém vlastním uživatelském jmenném prostoru znamená, že (teoreticky) nesprávně se chovající proces uvnitř flatpaku nemůže změnit (nebo možná ani získat přístup) k žádným souborům nebo procesům mimo jmenný prostor.

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

Koneckonců

Samotné použití uživatelských jmenných prostorů neřeší problém, který se Flatpak a další snaží zvládnout. I když jsou uživatelské jmenné prostory nedílnou součástí bezpečnostního příběhu a schopností jiných jmenných prostorů, samy o sobě mnoho neposkytují. Při vytváření nových izolovaných jmenných prostorů je třeba vzít v úvahu mnohé. V příštím článku se podívám na použití mount jmenný prostor ve spojení s uživatelským jmenným prostorem k vytvoření chroot -jako prostředí s jmennými prostory.

Pokud hledáte nějaké výzvy, které vám pomohou upevnit vaše porozumění, zkuste namapovat řadu uživatelů do nového jmenného prostoru. Co se stane, když namapujete celý rozsah do jmenného prostoru? Je možné stát se uživatelem Apache v neprivilegovaném jmenném prostoru? Jaké jsou bezpečnostní důsledky pro psaní špatné uid_map soubor? (Nápověda :Budete potřebovat dvě otevřené skořápky; jeden vytvořit a žít v novém jmenném prostoru a druhý napsat uid_map a gid_map soubory. Pokud s tím bojujete, napište mi na Twitter @linuxovens).


Linux
  1. Ladění Linuxu pomocí ProcDump

  2. Jak spravovat možnosti souborů Linux

  3. Použití příkazu ripgrep (rg) v Linuxu

  1. Linuxové jmenné prostory

  2. Mých 5 oblíbených obrázků kontejneru Linuxu

  3. Ruční vytváření kontejneru pomocí jmenných prostorů:jmenný prostor UTS

  1. Linux – Kernel:Podpora jmenných prostorů?

  2. 50 Výukové programy Sysadmin pro UNIX / Linux

  3. Kali Linux na Androidu pomocí Linux Deploy