Blog
Má mít každý tenant vlastní VM?
Vyberte si mezi kontejnery pro jednotlivé tenanty, VM a hybridními nastaveními s rozhodovacím rámcem založeným na rizicích a kroky k posílení zabezpečení, díky nimž bude každá možnost obhajitelná.
Shrnutí
Multi-tenant hosting vás nutí rozhodnout, jak daleko k sobě mohou tenanti navzájem dosáhnout. Kontejnery používají linuxové namespaces a cgroups k izolaci procesů a zdrojů, ale sdílejí jádro hostitele. Virtuální stroje přidávají hranici na úrovni hardwaru, za cenu rychlosti a provozní zátěže. Hybridní přístup – kontejnery uvnitř VM – vám může dát obojí, ale zdvojnásobuje plochu, kterou musíte opravovat. Tento článek vás provede rozhodováním založeným na rizicích, srovnáním vedle sebe a kroky k posílení zabezpečení Dockeru, které jsou důležité i uvnitř VM. Na konci budete vědět, který izolační model odpovídá vašim tenantům a co nastavit před spuštěním.
Vaše multi-tenant aplikace je téměř hotová. Máte soubor Docker Compose, který spustí stack pro každého zákazníka, a je rychlý. Pak se vás přítel, který provozuje hostingovou společnost, zeptá: „Dáváte každému tenantovi vlastní VM?" Ztuhnete. Na tuto otázku jste nebyli připraveni. Tento článek vám dnes nabízí způsob, jak na ni odpovědět, bez bezpečnostního týmu. Děláte to sami, takže rozhodnutí musí být dostatečně jednoduché na obhájení ve dvě ráno.
Přestaňte hledat „nejlepší“ model. Začněte tím, že si zapíšete, co se stane, když kód tenanta ovládne váš hostitel. Definujte blast radius, než si vyberete jakýkoli nástroj. Toto cvičení vám řekne víc než jakýkoli benchmark.
Jádro je spolubydlící, kterého nelze vystěhovat
Kontejnery jsou efektivní, protože sdílejí jádro hostitele. Toto sdílení je celý trik a také celé riziko. Linuxové namespaces dávají každému kontejneru vlastní pohled na procesy, síť a souborový systém. Control groups (cgroups) umožňují omezit CPU, paměť a diskové I/O, aby jeden tenant nemohl vyhladovět ostatní. Ale ani jedno nevytváří hardwarovou zeď.
Představte si kontejner jako proces s opravdu dobrým falešným průkazem. Věří, že je na vlastním počítači. Jádro je však jedna kopie Linuxu běžící na vašem hostiteli. Pokud tenant zneužije zranitelnost jádra, namespaces se stanou pouze metadaty. Útočník, který může volat funkce jádra, se může dostat k dalším namespaces na stejném jádře. To je ten únik z kontejneru, o kterém stále slyšíte.
Řekněme, že hostujete malý B2B nástroj s jedním kontejnerem na klienta. Klient si nainstaluje podezřelý plugin s chybou umožňující vzdálené spuštění kódu. Při výchozím nastavení Dockeru tento proces běží jako root uvnitř kontejneru. Root v kontejneru je stále UID 0 a jádro toto UID nerozlišuje od roota hostitele, pokud explicitně nemapujete uživatele. Útočník se může pokusit prorazit ven a sdílené jádro je jeho cílem.
Selhání nemusí být dramatické. Jediný tenant s únikem paměti může dostat hostitele do swapu, což zpomalí všechny ostatní tenanty. Bez limitů cgroups je jeden nesprávně fungující cyklus útokem na dostupnost. S nimi je to zablokovaný proces a upozornění.
Znamená to, že jsou kontejnery nebezpečné? Ne. Znamená to, že musíte jádro považovat za sdílenou důvěryhodnou zónu. Než se rozhodnete, napište prohlášení o riziku v rozsahu jednoho odstavce: „Pokud je kontejner tenanta napaden, útočník má přístup k: [list]. Obchodní náklady by byly: [amount or impact].“ Pokud vás tento odstavec děsí, nejste paranoidní. Jste upřímní.
Pro hlubší pohled na spektrum izolace, od sdílených kontejnerů po plně oddělené stacky, viz našeho průvodce navrhováním multi-tenant Docker architektury.
Tři způsoby, jak to rozdělit (vyberte jeden před nasazením)
Ve skutečnosti existují tři architektury pro multi-tenant izolaci. Každá „nejlepší praxe“ je kombinací těchto.
| Přístup | Izolační bariéra | Nejlepší, když | Nejtvrdší úskalí |
|---|---|---|---|
| Kontejnery pro jednotlivé tenanty | Kernel namespaces + cgroups | Mnoho malých tenantů, nízké riziko na tenanta, potřeba hustoty | Jeden exploit jádra může prolomit všechny tenanty na daném hostiteli |
| Jedno VM na tenanta | Hypervisor/hardwarová virtualizace | Regulovaná data, nepřátelští tenanti, vysoká hodnota na tenanta | Těžší, pomalejší zřizování, opravujete OS pro každého tenanta |
| Kontejnery uvnitř VM | Hranice VM kolem kontejnerizovaných workloadů | Hustota plus tvrdá skořápka mezi skupinami | Náklady a provozní režie se téměř zdvojnásobí |
Kontejnery pro jednotlivé tenanty. Toto je výchozí volba pro většinu zakladatelů SaaS. Každý tenant dostane vlastní kontejner nebo malý Compose stack. Zřizování je okamžité, obrazy jsou malé, CI/CD je přímočaré. Limity zdrojů brání hlučným sousedům, aby server zahltili. Kompromisem je sdílené jádro. Pokud dokážete udržet workloady neprivilegované a pravidelně opravovat hostitele, je to často správný první krok.
Nedávejte dva tenanty do stejného kontejneru. To je sdílené jádro plus sdílený runtime plus sdílený souborový systém. Pokud jeden tenant nahraje soubor, který vytvoří proces, druhý tenant je již ve stejné tabulce procesů. Kontejner je vaše jednotka izolace; zajistěte jednoho tenanta na kontejner.
A co databáze? Pokud se každý tenant připojuje k jedné instanci MongoDB nebo PostgreSQL se stejnými přihlašovacími údaji, přidali jste obrovský sdílený komponent. Dejte každému tenantovi samostatné přihlašovací údaje a ideálně samostatnou databázi nebo schéma. Kontejnery izolují aplikaci; databáze je často první netěsnost, kterou útočník vyzkouší.
Jedno VM na tenanta. Dejte každému tenantovi plný virtuální stroj. Hypervisor přidává hranici na úrovni hardwaru, což je přesně to, co musí exploit jádra překonat, aby dosáhl hostitele. To je důležité pro regulovaná prostředí nebo když jsou tenanti nedůvěryhodní. Cenou je hustota a čas. Nyní spravujete flotilu operačních systémů, ne jen kontejnery. Každé VM potřebuje aktualizace, bezpečnostní agenty a monitorování. Pro sólového zakladatele je to pořádná práce.
Vzory, které na této úrovni fungují: použijte infrastrukturu jako kód k vytvoření VM ze stejného základního obrazu, zapracujte aktualizace do nových obrazů místo oprav živých systémů a ukončete workloady, které nepoznáváte. Udržujte management port VM uzavřený před internetem.
Kontejnery uvnitř VM. Tento hybrid se v začátečnických tutoriálech jen málokdy probírá. Kolem každého tenanta (nebo malé skupiny tenantů) umístíte malé VM a uvnitř pak spustíte kontejnery. VM je nádoba ohraničující blast radius; kontejnery jsou jen nasaditelné jednotky. To vám dává tvrdou hranu virtualizace a reprodukovatelnost obrazů. Stojí to více, protože platíte za režii virtualizace a flexibilitu kontejnerů, ale může to být nejrozumnější dlouhodobý model, pokud tenantům nemůžete plně důvěřovat.
Jeden běžný mikropříklad: tenant provozuje Node API a background worker. Místo jednoho obrovského kontejneru s oběma procesy použijte jedno VM, pak dva kontejnery s různými limity zdrojů, sdílenou sítí a bez přímého vystavení internetu pro worker. VM poskytuje tvrdou hranu; kontejnery poskytují strukturu.
Který byste si měli vybrat? Tabulka je váš užší výběr. Následující části rozhodnutí upřesní.
Pokud si vyberete kontejnery, udělejte těchto šest věcí, nebo se do toho nepouštějte
Kontejnery pro jednotlivé tenanty jsou v pořádku, pokud každý kontejner považujete za potenciálního útočníka. To začíná konfigurací, ne přáním.
0. Omezte zdroje, než někomu začnete věřit. Cgroups jsou mechanismus spravedlnosti a obrana dostupnosti. Nastavte --memory a --cpus pro každý kontejner. Tenant, který ztrácí paměť, by měl narazit na svůj vlastní limit, ne na limit vašeho serveru. Toto není bezpečnostní hranice, ale hlučný soused je útok bez jediného řádku kódu. Praktický začátek: --memory 512m --cpus 0.5. U worker procesu začněte níž a postupně zvyšujte.
1. Běžte jako neprivilegovaný uživatel. Nikdy nedovolte procesu v kontejneru používat UID 0, pokud to nezbytně nepotřebujete. Nastavte uživatele v Dockerfile a přidejte --user jako další pojistku. Exploit běžící jako neprivilegovaný uživatel má mnohem méně cest k jádru. V Dockerfile vytvořte uživatele: RUN useradd -u 10001 app a USER app. Nevynechávejte to kvůli úspoře času.
2. Odstraňte všechny capability, které nepotřebujete. Linuxové capability rozdělují moc roota na malé kousky. Většina webových aplikací nepotřebuje téměř žádné. Začněte s --cap-drop=ALL a přidejte zpět pouze to, co víte, že potřebujete. Kontejner bez CAP_SYS_ADMIN se mnohem hůře používá pro triky s namespaces. Pokud se vaše aplikace snaží navázat na privilegovaný port, spusťte ji na vysokém portu a před ni umístěte proxy, místo abyste udělovali NET_BIND_SERVICE.
3. Nastavte souborový systém jako pouze pro čtení. Vaše aplikace by neměla zapisovat do své vlastní vrstvy kontejneru. Pro stav připojte tmpfs. Útočník, který nemůže zapisovat na disk, má mnohem těžší čas s trvalou přítomností. Napadená PHP aplikace, která se snaží zapsat webshell, selže, když je root souborový systém pouze pro čtení. Pro zapisovatelný adresář, který aplikace skutečně potřebuje, můžete připojit pojmenovaný svazek.
4. Použijte seccomp a AppArmor nebo SELinux. Tyto posílají riziková systémová volání do koše. Docker obsahuje výchozí seccomp profil; použijte jej. Přidejte profil AppArmor pro další vrstvu. Nemusíte ovládat každé syscall. Musíte odepřít to, co normální webový worker nikdy nevyžaduje. Nikdy nespouštějte s --privileged. Tento přepínač deaktivuje téměř všechny obrany, které jste právě nastavili.
5. Segmentujte síť. Nedávejte každému kontejneru trasu ke všem ostatním kontejnerům. Výchozí odmítnutí, poté otevřete pouze porty, které potřebujete. Napadený kontejner s databází by neměl být schopen skenovat váš administrační panel. Pokud jsou tenanti v oddělených sítích, narušení v jedné síti se nemůže šířit laterálně.
Praktický začátek:
docker run --user 10001 --cap-drop=ALL --security-opt no-new-privileges --read-only --tmpfs /tmp:rw,size=64M --security-opt seccomp=default.json --memory 512m --cpus 0.5 myimage
Vložte stejné přepínače do souboru Compose a aplikujte je na každého tenanta. To není kompletní, ale je to mnohem silnější výchozí stav, než jaký vám poskytne docker run po vybalení.
Pro podrobnější průchod použijte našeho průvodce krok za krokem pro posílení zabezpečení Docker kontejnerů v multi-tenant hostingu.
Vylepšená izolace kontejnerů Dockeru je výjimka, o které byste měli vědět
Pokud běžíte ve spravovaném prostředí Dockeru, hledejte Docker's Enhanced Container Isolation (ECI). Pod kapotou používá izolaci uživatelských namespaces a zabezpečené běhové prostředí kontejnerů. Root uvnitř kontejneru je mapován na neprivilegovaného uživatele na hostiteli, takže i kontejner běžící jako root nezíská práva roota hostitele. Také ve výchozím nastavení blokuje nebezpečné capability a systémová volání. To není něco, co můžete znovu vytvořit pomocí několika přepínačů na vanilkovém Dockeru. Pokud vaše platforma tento režim podporuje, zapněte jej. Neodstraňuje potřebu neprivilegovaných uživatelů a limitů zdrojů, ale mění matematiku rizik.
Část toho můžete přiblížit pomocí user namespace remapping (userns-remap) v Docker daemonu. To není tak úplné jako zabezpečené běhové prostředí, ale je to lepší než nic. Pokud to použijete, ověřte, že mapování UID funguje, než tomu začnete věřit.
Klam VM: Přechod na virtuální stroje není posílení zabezpečení
Zde je kontrariánská část, a je to část, kterou většina lidí přeskočí. Pokud přejdete na jedno VM na tenanta a pak do něj nasadíte své běžné kontejnery, neodstranili jste svůj problém se zabezpečením kontejnerů. Přidali jste prostornou klec. Únik z kontejneru stále funguje; útočník se pouze ocitne ve VM místo na hostiteli. To je skutečné zlepšení, ale stále potřebujete šest kroků.
Další pastí je předpoklad, že VM je samo o sobě bezpečné. Výchozí obraz se slabým heslem SSH, neopravenými základními balíčky nebo otevřeným management portem je dar. Hranice hypervisoru záleží jen tehdy, pokud je host posílen a aktualizován. Jinak je vaše „bezpečné VM“ rychlejší cestou k napadení, protože se cítíte bezpečně a přestanete kontrolovat.
Co vám VM přináší, je zmenšitelný blast radius. Pohroma jednoho tenanta zůstane v jednom VM. Co vás to stojí, je váš čas. Stáváte se správcem systému pro tolik operačních systémů, kolik máte tenantů. Pokud jste sólový zakladatel, který dodává produkt, zeptejte se, zda máte hodiny na opravy a monitorování flotily. Pokud ano, VM na tenanta může být správná volba. Pokud ne, kontejnery se silným zabezpečením mohou být čestnější volbou.
Také nezapomeňte, že hostitel hypervisoru je kritickým cílem. Napadený hypervisor vidí všechny hosty. Opravujte hostitele, ne jen hosty. VM vás neomlouvá z oprav hostitele; zvyšuje sázky pro jejich vynechání.
Upozornění k hybridu: nepředpokládejte, že kontejnery uvnitř VM vám zdarma poskytnou „dvě vrstvy zabezpečení“. VM přidává hranici; kontejner stále potřebuje neprivilegovaného uživatele, capability a seccomp. Jinak je první vrstva jen tak silná, jako nejslabší kontejner.
Čtyři otázky, které vyřeší debatu za deset minut
Neoptimalizujte v abstraktní rovině. Položte si tyto čtyři otázky v pořadí. Zapište si odpovědi.
1. K čemu má můj tenant přístup? Pokud se tenant může dostat pouze ke své vlastní webové aplikaci a databázi, jsou kontejnery pro jednotlivé tenanty s přísnými síťovými pravidly obhajitelné. Pokud jsou data tenanta regulovaná nebo finančně citlivá, přejděte k VM.
2. Kolik by mě stálo napadení jednoho tenanta? Sečtěte ztracené zákazníky, právní riziko a důvěru. Pokud je číslo větší než náklady na provoz VM, utraťte peníze. Pokud ne, kontejnery jsou racionální volbou.
3. Kolik mám tenantů a kolik platí? Mnoho malých předplatitelů: záleží na hustotě kontejnerů. Hrstka velkých účtů: dejte každému VM a fakturujte odpovídajícím způsobem. Tenanti, kteří vám platí méně než kávu, by neměli vyžadovat každý samostatný OS ke správě.
4. Mohu provádět opravy podle plánu? Kontejnery sdílejí jedno jádro hostitele, takže oprava hostitele chrání všechny. VM znásobují vaše cíle pro opravy. Pokud víte, že aktualizace budete vynechávat, vyberte architekturu s méně pohyblivými částmi a tvrdšími výchozími hodnotami.
Vaše odpovědi se budou shlukovat. Dvě nebo více odpovědí zaměřených na VM znamená, že byste neměli automaticky používat kontejnery pro jednotlivé tenanty. Tři nebo více odpovědí zaměřených na kontejnery znamená, že jsou VM předčasné. Jeden kontraintuitivní výsledek: tenant s nízkými příjmy a přístupem k citlivým datům stále potřebuje VM, protože regulační náklady nemají nic společného s tím, kolik platí.
Nasazujte minimum, kterému můžete věřit, a poté si zaslužte více izolace
Vaše první architektura nemusí být konečná. Začněte s nejpřísnějším nastavením, které skutečně dokážete udržovat, a poté přidávejte izolaci, jak to vaše základna tenantů ospravedlní. Pro většinu sólových provozovatelů to znamená kontejnery pro jednotlivé tenanty s neprivilegovaným uživatelem, omezenými capability, souborovým systémem pouze pro čtení, seccomp a segmentací sítě. Pro regulované nebo vysoce hodnotné tenanty přejděte rovnou na jedno VM na tenanta, s kontejnery pouze jako balicí vrstvou uvnitř.
Ať si vyberete cokoli, zapište si rozhodnutí a čtvrtletně se k němu vracejte. Když dostanete první otázku „měl bychom přesunout tohoto tenanta na VM?“, budete mít odpověď a také kontrolní seznam, který ji podpoří. To je to, co izolace skutečně znamená: kompromis, který řídíte, ne technologie, kterou si koupíte.
Před spuštěním si projděte náš praktický kontrolní seznam zabezpečení izolace Dockeru – přemění tato rozhodnutí na seznam, který můžete ověřit dříve, než zákazníkovi ukážete stránku.

