Blogg

Förtroendefallgropen: Hur vår fleranvändar-Docker-installation läckte data (och hur vi åtgärdade det)

Lär dig hur en naiv Docker-installation ledde till en dataläcka mellan klienter och den skiktade isoleringsstrategi som förhindrade den.

Sammanfattning

Docker-containrar är inte isolerade som standard – de delar värdens kärna, och utan medveten konfiguration kan klienter störa varandra. Denna artikel går igenom ett verkligt scenario där en fleranvändarvärd upptäckte att klientcontainrar kunde komma åt varandras databaser på grund av delat nätverk och svaga säkerhetsstandardinställningar. Vi visar steg-för-steg-ändringarna som åtgärdade läckan: per-klient användardefinierade nätverk, icke-root-användare, borttagna kapaciteter, skrivskyddade filsystem och seccomp-profiler. En vanlig uppfattning är att containrar ger stark isolering; vi utmanar detta genom att förklara varför virtuella maskiner fortfarande erbjuder en hårdare gräns och när man bör överväga en hybridansats. Slutsatsen betonar att isolering är en skiktad övning, inte en enda kryssruta.

Incidenten: När containrar pratar för mycket

Du har satt upp Docker på en enda värd för att köra flera klientwebbplatser. Varje klient har sin egen container – en snygg, isolerad miljö, eller hur? Det trodde vi. Tills en rutinmässig säkerhetsrevision visade att Klient A:s container läste MySQL-socketen från Klient B:s container på samma värd. De delade det interna bridge-nätverket. Värre var att containrarna kördes som root, så en angripare som komprometterade en kunde manipulera värdens Docker-socket eller en annan containers filsystem. Läckan var inte ett sofistikerat utnyttjande; det var grundläggande felkonfiguration. Data läckte. Förtroendet försvann.

Misslyckandescenariot är inte ovanligt. Många team antar att Dockers namnområden och cgroups automatiskt avskärmar klienter, men de underskattar hur många flyktvägar som fortfarande är öppna som standard. Standard bridge-nätverk erbjuder ingen nätverksisolering mellan containrar. Att köra som root ger containern mer kraft än nödvändigt. Och utan explicita resursbegränsningar kan en bullrig granne svälta ut andra på CPU eller minne.

Steg 1: Sluta dela ett enda nätverk

Vår första åtgärd var att ge varje klient sitt eget användardefinierade Docker-nätverk. Detta förhindrar att containrar når varandra om du inte uttryckligen ansluter dem. Vi skapade ett skript som för varje klient startar ett dedikerat nätverk och kopplar deras applikationscontainer till det. Databascontainern lever i samma klientnätverk, men vi lade också till ett internt nätverk endast för intra-klientkommunikation. Inget mer tjuvtittande mellan klienter.

Vi isolerade även databaserna genom att köra dem i separata containrar på samma klientnätverk, med separata datavolymer. Detta säkerställde att även om en angripare bröt sig in i appcontainern, kunde de inte avlyssna databastrafik från en annan klient.

För en djupare inblick i nätverksisoleringsstrategier, se En praktisk Docker-isoleringssäkerhetschecklista för fleranvändarhosting.

Steg 2: Ta bort onödiga privilegier

Som standard körs Docker-containrar med en begränsad uppsättning Linux-kapaciteter, men de har fortfarande mer än de flesta applikationer behöver. Våra containrar kördes som root, vilket tillät processer inuti att utföra åtgärder som att montera filsystem eller ändra kernelparametrar. Vi bytte till att köra applikationen som en icke-root-användare inuti containern (med USER-direktivet i Dockerfilen) och tog bort alla kapaciteter utom de som absolut krävs. För en typisk webbapp kan det vara bara NET_BIND_SERVICE (för att binda till portar under 1024) och CHOWN (för att skriva till kataloger). Vi lade också till --security-opt no-new-privileges för att förhindra privilegieeskalering.

Detta steg ensam eliminerade många vanliga containerflyktvägar. En angripare som komprometterar webbservern kan inte installera paket, modifiera systembinärer eller komma åt värdens Docker-socket eftersom processen saknar CAP_SYS_ADMIN- eller CAP_DAC_OVERRIDE-kapaciteterna.

Steg 3: Lås filsystemet

Skrivbara filsystem är en vanlig angreppsyta. Vi gjorde rotfilsystemet skrivskyddat (--read-only) för alla containrar och monterade sedan temporära filsystem (tmpfs) för kataloger som behöver skrivåtkomst, som /tmp och applikationens cache-katalog. Detta förhindrar en angripare från att modifiera applikationskod eller spara skadliga binärer.

Dessutom använde vi Dockers --mount-alternativ för att bind-mounta känsliga kataloger som Docker-socketen endast när det är absolut nödvändigt – och aldrig på produktionscontainrar. Principen: om containern inte behöver skriva till en sökväg, gör den skrivskyddad.

Steg 4: Tillämpa Seccomp- och AppArmor-profiler

Standard seccomp-profiler blockerar redan många farliga systemanrop, men vi anpassade dem ytterligare för att vitlista endast de systemanrop som vår applikation faktiskt behöver. Detta är en avvägning eftersom det kräver profilering av applikationen. En enklare metod är att använda Dockers standard seccomp-profil och sedan lägga till --security-opt seccomp=sökväg/till/profil.json om du behöver striktare regler. På samma sätt kan AppArmor-profiler begränsa containerprocesser till specifika filsökvägar och kapaciteter. Vi aktiverade AppArmor och använde en anpassad profil som begränsade åtkomst till endast applikationens datakataloger.

För en omfattande guide om dessa härdningssteg, se Härdning av Docker-containrar för fleranvändarhosting: En steg-för-steg-isoleringsguide.

Den konträra åsikten: Ibland behöver du virtuella maskiner

Oavsett hur härdade de är, delar containrar värdens kärna. En kärnsårbarhet kan bryta all isolering på en gång. Därför kör många säkerhetsmedvetna plattformar containrar inuti lätta virtuella maskiner – varje klient får sin egen kärna. Detta tillför overhead men ger en hårdvarugräns som containrar ensamma inte kan. Om dina klienter hanterar kreditkortsdata eller hälsouppgifter kan en hybridansats (containrar inuti virtuella maskiner) vara rätt väg. Anta inte att containerisolering är tillräcklig för din hotmodell; utvärdera datas känslighet och regulatoriska krav.

För en djupare jämförelse av isoleringsnivåer, läs Designa en fleranvändar-Docker-arkitektur: Välja rätt isoleringsnivå.

Slutsats: Isolering är en stack, inte en strömbrytare

Åtgärden var inte en enda förändring – det var skiktning: nätverksisolering, begränsade privilegier, skrivskyddade filsystem och systemanropsfiltrering. Ändå accepterade vi att perfekt isolering är omöjlig med delade-kärne-containrar. För våra mest säkerhetskänsliga klienter flyttade vi dem till dedikerade värdar. Lärdom: lita inte på några standardinställningar. Granska din Docker-installation som om en läcka redan har inträffat. Tiden att låsa är innan läckan, inte efter.

Sources (5)