Blog
Skal hver lejer have sin egen VM?
Vælg mellem containere pr. lejer, VM'er og hybride opsætninger med en risikobaseret beslutningsramme og de hærdningstrin, der gør hver mulighed forsvarlig.
Resumé
Multi-tenant-hosting tvinger dig til at vælge, hvor langt lejere kan række ind i hinanden. Containere bruger Linux namespaces og cgroups til at isolere processer og ressourcer, men de deler værtens kerne. Virtuelle maskiner tilføjer en grænse på hardwareniveau, men på bekostning af hastighed og operationel belastning. En hybrid tilgang – containere i VM'er – kan give dig begge dele, men det fordobler den overflade, du skal patche. Denne artikel fører dig gennem en risikobaseret beslutning, en sammenligning side om side og de Docker-hærdningstrin, der betyder noget, selv inde i en VM. Når du er færdig, ved du, hvilken isolationsmodel der passer til dine lejere, og hvad du skal konfigurere før lancering.
Din multi-tenant-app er næsten klar. Du har en Docker Compose-fil, der starter en stack op pr. kunde, og det er hurtigt. Så spørger en ven, der driver et hostingfirma: 'Giver du hver lejer sin egen VM?' Du fryser. Du havde ikke planlagt for det spørgsmål. Denne artikel giver dig en måde at besvare det i dag, uden et sikkerhedsteam. Du gør det alene, så beslutningen skal være enkel nok at forsvare kl. 2 om natten.
Hold op med at lede efter den 'bedste' model. Start med at skrive ned, hvad der sker, hvis en lejers kode overtager din vært. Definér eksplosionsradiusen, før du vælger noget værktøj. Den øvelse fortæller dig mere end nogen benchmark nogensinde vil.
Kernen er den værelseskammerat, du ikke kan smide ud
Containere er effektive, fordi de deler værtens kerne. Den deling er hele tricket, og hele risikoen. Linux namespaces giver hver container sin egen visning af processer, netværk og filsystem. Control groups (cgroups) lader dig loffe CPU, hukommelse og disk I/O, så én lejer ikke kan sulte de andre. Men ingen af dem skaber en hardwaremur.
Tænk på en container som en proces med et rigtig godt falsk ID. Den tror, den er på sin egen maskine. Kernen er dog én kopi af Linux, der kører på din vært. Hvis en lejer udnytter en sårbarhed i kernen, bliver namespaces til metadata og intet mere. En angriber, der kan kalde kernelfunktioner, kan nå andre namespaces på samme kerne. Det er den container-escape, du hele tiden hører om.
Sig, du hoster et lille B2B-værktøj med én container pr. klient. En klient installerer et tvivlsomt plugin med en fejl, der tillader ekstern kodeudførelse. Med standard Docker-indstillinger kører den proces som root inde i containeren. Root i en container er stadig UID 0, og kernen skelner ikke den UID fra værtens root, medmindre du eksplicit mapper brugere. Angriberen kan forsøge at bryde ud, og den delte kerne er deres mål.
Fejlen behøver ikke være dramatisk. En enkelt lejer, der lækker hukommelse, kan skubbe værten til at bruge swap, hvilket sænker alle andre lejere. Uden cgroup-grænser er en enkelt løkke, der opfører sig dårligt, et tilgængelighedsangreb. Med dem er det en blokeret proces og en alarm.
Betyder det, at containere er usikre? Nej. Det betyder, at du skal behandle kernen som en delt tillidszone. Før du vælger, skriv en risikoudtalelse på ét afsnit: 'Hvis en lejers container kompromitteres, kan angriberen få adgang til: [list]. Den økonomiske omkostning vil være: [amount or impact].' Hvis det afsnit skræmmer dig, er du ikke paranoid. Du er ærlig.
For en dybere forståelse af isolationsspektret, fra delte containere til fuldt adskilte stacks, se vores guide til at designe en multi-tenant Docker-arkitektur.
Tre måder at skære det (Vælg én, før du går i drift)
Der findes egentlig tre arkitekturer til multi-tenant-isolering. Enhver 'bedste praksis' er en kombination af disse.
| Tilgang | Isoleringsbarriere | Bedst når | Sværeste forbehold |
|---|---|---|---|
| Containere pr. lejer | Kernel namespaces + cgroups | Mange små lejere, lav risiko pr. lejer, behov for tæthed | Én kerneudnyttelse kan bryde alle lejere på den vært |
| Én VM pr. lejer | Hypervisor/hardwarevirtualisering | Regulerede data, fjendtlige lejere, høj værdi pr. lejer | Tungere, langsommere at provisionere, du patcher et OS pr. lejer |
| Containere i VM'er | VM-grænse omkring containeriserede arbejdsbelastninger | Tæthed plus en hård skal mellem grupper | Omkostninger og operationel overhead næsten fordoblet |
Containere pr. lejer. Dette er standarden for de fleste SaaS-grundlæggere. Hver lejer får sin egen container eller lille Compose-stack. Provisionering er øjeblikkelig, images er små, CI/CD er ligetil. Ressourcegrænser forhindrer støjende naboer i at æde serveren. Kompromiset er den delte kerne. Hvis du kan holde workloads ikke-privilegerede og patche værten regelmæssigt, er dette ofte det rigtige første træk.
Læg ikke to lejere i samme container. Det er en delt kerne plus en delt runtime plus et delt filsystem. Hvis én lejer uploader en fil, der opretter en proces, er den anden lejer allerede i den samme processtabel. En container er din isolationsenhed; gør den til én lejer pr. container.
Hvad med databasen? Hvis hver lejer forbinder til én MongoDB- eller PostgreSQL-instans med de samme legitimationsoplysninger, har du allerede tilføjet en stor delt komponent. Giv hver lejer separate legitimationsoplysninger og ideelt set en separat database eller et separat schema. Containere isolerer appen; databasen er ofte den første lækage, en angriber vil teste.
Én VM pr. lejer. Giv hver lejer en fuld virtuel maskine. Hypervisoren tilføjer en grænse på hardwareniveau, hvilket er præcis, hvad et kerneudnyttelsesangreb skal krydse for at nå værten. Dette betyder noget for regulerede miljøer eller når lejere er upålidelige. Omkostningen er tæthed og tid. Du administrerer nu en flåde af operativsystemer, ikke kun containere. Hver VM har brug for opdateringer, sikkerhedsagenter og overvågning. For en solo-grundlægger er det rigtigt arbejde.
Mønstre, der virker på dette niveau: brug infrastructure-as-code til at oprette en VM fra det samme basisimage, bag opdateringer ind i nye images i stedet for at patche levende systemer, og afslut workloads, du ikke genkender. Hold VM'ens administrationsport lukket for internettet.
Containere i VM'er. Denne hybrid bliver sjældent diskuteret i begynder-tutorials. Du lægger en lille VM omkring hver lejer (eller lille gruppe af lejere) og kører derefter containere inde i den VM. VM'en er en eksplosionsradius-beholder; containerne er bare implementerbare enheder. Dette giver dig den hårde kant af virtualisering og reproducerbarheden af images. Det koster mere, fordi du betaler for virtualiseringsomkostninger og containerfleksibilitet, men det kan være den fornuftigste langsigtede model, når du ikke fuldt ud kan stole på lejere.
Et almindeligt mikro-eksempel: en lejer kører en Node-API og en baggrundsarbejder. I stedet for én stor container med begge processer, brug én VM, derefter to containere med forskellige ressourcegrænser, et delt netværk og ingen direkte interneteksponering for arbejderen. VM'en giver den hårde kant; containerne giver struktur.
Hvilken skal du vælge? Tabellen er din shortliste. De næste afsnit gør beslutningen konkret.
Hvis du vælger containere, så gør disse seks ting – ellers lad være
Containere pr. lejer er fine, hvis du behandler hver container som en potentiel angriber. Det starter med konfiguration, ikke ønsketænkning.
0. Begræns ressourcer, før du stoler på nogen. Cgroups er en fairnesmekanisme og et tilgængelighedsforsvar. Sæt --memory og --cpus pr. container. En lejer, der lækker hukommelse, bør ramme sin egen grænse, ikke din servers. Dette er ikke en sikkerhedsgrænse, men en støjende nabo er et angreb uden en eneste linje kode. En praktisk start: --memory 512m --cpus 0.5. For en arbejdsproces, start lavere og skaler op.
1. Kør som en ikke-root-bruger. Lad aldrig containerprocessen bruge UID 0, medmindre du absolut har brug for det. Sæt en bruger i Dockerfile'en og brug --user som en ekstra beskyttelse. Et udnyttelsesangreb, der kører som en uprivilegeret bruger, har langt færre veje til kernen. I din Dockerfile skal du oprette en bruger: RUN useradd -u 10001 app og USER app. Spring ikke dette over for at spare tid.
2. Fjern alle capabilities, du ikke har brug for. Linux-capabilities deler root'ens magt op i små stykker. De fleste webapps har brug for næsten ingen. Start med --cap-drop=ALL og tilføj kun det, du ved, du har brug for. En container uden CAP_SYS_ADMIN er meget sværere at bruge til namespace-tricks. Hvis din app forsøger at binde en privilegeret port, så kør den på en høj port og sæt en proxy foran i stedet for at give NET_BIND_SERVICE.
3. Gør filsystemet skrivebeskyttet. Din app bør ikke skrive til sit eget containerlag. Mount en tmpfs til tilstand. En angriber, der ikke kan skrive til disk, har meget sværere ved at plante persistens. En kompromitteret PHP-app, der forsøger at skrive en webshell, vil fejle, når rod-filsystemet er skrivebeskyttet. Du kan mount et navngivet volumen til en skrivbar mappe, som din app virkelig har brug for.
4. Anvend seccomp og AppArmor eller SELinux. Disse sender risikable syscalls til bunken med afviste. Docker leverer en standard seccomp-profil; brug den. Tilføj en AppArmor-profil for et ekstra lag. Du behøver ikke mestre enhver syscall. Du skal nægte, hvad en normal webworker aldrig kræver. Kør aldrig med --privileged. Det flag deaktiverer næsten alle de forsvarsværker, du lige har sat op.
5. Segmentér netværket. Giv ikke hver container en rute til alle andre containere. Afvis som standard, og åbn kun de porte, du har brug for. En kompromitteret databasecontainer bør ikke kunne scanne dit adminpanel. Hvis lejere er i separate netværk, kan et brud i ét netværk ikke sprede sig lateralt.
En praktisk start:
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
Læg de samme flag i en Compose-fil, og anvend dem på hver lejer. Dette er ikke komplet, men det er en langt stærkere standard end det, docker run giver dig ud af boksen.
For en mere dybdegående gennemgang, brug vores trin-for-trin-hærdningsguide til Docker-containere i multi-tenant-hosting.
Dockers Enhanced Container Isolation er den undtagelse, du bør kende til
Hvis du kører i et administreret Docker-miljø, så se efter Dockers Enhanced Container Isolation (ECI). Den bruger user namespace-isolering og en sikker container-runtime under motorhjelmen. Root inde i en container mappes til en uprivilegeret bruger på værten, så selv en container, der kører som root, får ikke værts-root-rettigheder. Den blokerer også farlige capabilities og syscalls som standard. Dette er ikke noget, du kan genskabe med et par flag på almindelig Docker. Hvis din platform understøtter det, så slå det til. Det fjerner ikke behovet for ikke-root-brugere og ressourcegrænser, men det ændrer risikomatematikken.
Du kan tilnærme en del af dette med user namespace-remapping (userns-remap) i Docker-dæmonen. Det er ikke så komplet som en sikker runtime, men det er bedre end ingenting. Hvis du bruger det, så verificér, at UID-mappingen virker, før du stoler på det.
VM-fejlslutningen: At flytte til virtuelle maskiner er ikke hærdning
Her er den kontrære del, og det er den del, de fleste springer over. Hvis du flytter til én VM pr. lejer og derefter implementerer dine normale containere inde i den, har du ikke fjernet dit containersikkerhedsproblem. Du har tilføjet et bredt bur. Container-escapen virker stadig; angriberen lander bare i VM'en i stedet for på værten. Det er en reel forbedring, men du har stadig brug for de seks trin.
Den anden fælde er at antage, at selve VM'en er sikker. Et standardimage med en svag SSH-adgangskode, upatchede basispakker eller en åben administrationsport er en gave. Hypervisorgrænsen betyder kun noget, hvis gæsten er hærdet og opdateret. Ellers er din 'sikre VM' en hurtigere vej til kompromittering, fordi du føler dig sikker og holder op med at tjekke.
Det, en VM giver dig, er en reducerbar eksplosionsradius. Én lejers katastrofe forbliver i én VM. Det, det koster dig, er din tid. Du bliver systemadministrator for lige så mange operativsystemer, som du har lejere. Hvis du er en solo-grundlægger, der sender et produkt, så spørg dig selv, om du har timerne til at patche og overvåge en flåde. Hvis ja, kan én VM pr. lejer være den rigtige beslutning. Hvis nej, kan containere med stærk hærdning være mere ærlige.
Husk også, at din hypervisor-vært er et kritisk mål. En kompromitteret hypervisor kan se alle gæster. Patch værten, ikke kun gæsterne. VM'en fritager dig ikke fra at patche værten; det hæver indsatsen for at springe det over.
En advarsel om hybriden: antag ikke, at containere i en VM giver dig 'to lag sikkerhed' gratis. VM'en tilføjer en grænse; containeren har stadig brug for non-root, capabilities og seccomp. Ellers er det første lag kun lige så stærkt som den svageste container.
Fire spørgsmål, der afgør debatten på ti minutter
Optimér ikke i det abstrakte. Stil dig selv disse fire spørgsmål i rækkefølge. Skriv svarene ned.
1. Hvad har min lejer adgang til? Hvis en lejer kun kan nå sin egen webapp og database, er containere pr. lejer med strenge netværksregler forsvarlige. Hvis en lejers data er reguleret eller økonomisk følsomme, så bevæg dig mod VM'er.
2. Hvor meget ville én lejers kompromittering koste mig? Læg tabte kunder, juridisk eksponering og tillid sammen. Hvis tallet er større end omkostningen ved at køre VM'er, så brug pengene. Hvis ikke, er containere et rationelt valg.
3. Hvor mange lejere har jeg, og hvor meget betaler de? Mange små abonnenter: containertæthed betyder noget. En håndfuld store konti: giv hver en VM og fakturér derefter. Lejere, der betaler dig mindre end en kop kaffe, bør ikke hver kræve et OS at administrere.
4. Kan jeg patche ting efter en tidsplan? Containere deler én værtkerne, så at patche værten beskytter alle. VM'er multiplicerer dine patch-mål. Hvis du ved, at du vil springe opdateringer over, så vælg arkitekturen med færre bevægelige dele og hårdere standardindstillinger.
Dine svar vil gruppere sig. To eller flere VM-fokuserede svar betyder, at du ikke bør som standard vælge containere pr. lejer. Tre eller flere container-fokuserede svar betyder, at VM'er er for tidlige. Et kontraintuitivt resultat: en lejer med lave indtægter og adgang til følsomme data har stadig brug for VM'en, fordi de regulatoriske omkostninger ikke har noget at gøre med, hvor meget de betaler.
Lever det mindste, du kan stole på, og opnå mere isolering
Din første arkitektur behøver ikke at være din endelige. Start med den strammeste opsætning, du rent faktisk kan vedligeholde, og tilføj derefter isolering, når din lejerbase retfærdiggør det. For de fleste solo-operatører betyder det containere pr. lejer med non-root, begrænsede capabilities, skrivebeskyttede filsystemer, seccomp og netværkssegmentering. For regulerede eller højværdi-lejere skal du springe direkte til én VM pr. lejer, med containere kun som et pakkelag indeni.
Uanset hvad du vælger, så skriv beslutningen ned, og genbesøg den kvartalsvis. Når du får dit første spørgsmål 'skal vi flytte denne lejer til en VM?', har du et svar, og du har tjeklisten til at bakke det op. Det er, hvad isolering faktisk betyder: et kompromis, du styrer, ikke en teknologi, du køber.
Før lancering skal du gennemgå vores praktiske Docker-isoleringssikkerhedstjekliste – den forvandler disse beslutninger til en liste, du kan verificere, før du viser en side til en kunde.

