Blog

Tillidsfejltagelsen: Hvordan vores multi-tenant Docker-opsætning lækkede data (og hvordan vi fiksede det)

Lær, hvordan et teams naive Docker-opsætning førte til et kryds-lejer data læk og den lagdelte isolationsstrategi, der forhindrede det.

Resumé

Docker-containere er ikke isolerede som standard – de deler værtens kerne, og uden bevidst konfiguration kan lejere forstyrre hinanden. Denne artikel gennemgår et virkeligt scenarie, hvor en multi-tenant hostingudbyder opdagede, at klientcontainere kunne få adgang til hinandens databaser på grund af delt netværk og svage sikkerhedsstandardindstillinger. Vi viser de trinvise ændringer, der fikserede bruddet: per-tenant brugerdefinerede netværk, ikke-root-brugere, fjernede kapaciteter, skrivebeskyttede filsystemer og seccomp-profiler. En almindelig antagelse er, at containere i sig selv giver stærk isolation; vi udfordrer dette ved at forklare, hvorfor VM'er stadig tilbyder en hårdere grænse, og hvornår man bør overveje en hybrid tilgang. Konklusionen understreger, at isolation er en lagdelt øvelse, ikke en enkelt afkrydsningsboks.

Hændelsen: Når containere taler for meget

Du har sat Docker op på en enkelt vært for at køre flere klientwebsteder. Hver klient har sin egen container – et pænt, isoleret miljø, ikke? Det troede vi. Indtil en rutinemæssig sikkerhedsrevision afslørede, at Klient A's container læste MySQL-socket'en for Klient B's container på samme vært. De delte det standard bridge netværk. Værre endnu, containerne kørte som root, så en angriber, der kompromitterede én, kunne pille ved værtens Docker-socket eller en anden containers filsystem. Bruddet var ikke et sofistikeret exploit; det var en grundlæggende fejlkonfiguration. Data lækkede. Tilliden forsvandt.

Fejlscenariet er ikke ualmindeligt. Mange teams antager, at Docker's namespaces og cgroups automatisk afskærmer lejere, men de undervurderer, hvor mange flugtveje der forbliver åbne som standard. Standard bridge-netværk tilbyder ingen netværksisolation mellem containere. At køre som root giver containeren mere kraft end nødvendigt. Og uden eksplicitte ressourcegrænser kan en støjende nabo fratage andre CPU eller hukommelse.

Trin 1: Stop med at dele et enkelt netværk

Vores første fix var at give hver lejer sit eget brugerdefinerede Docker-netværk. Dette forhindrer containere i at nå hinanden, medmindre du eksplicit forbinder dem. Vi oprettede et script, der for hver lejer starter et dedikeret netværk og tilknytter deres applikationscontainer til det. Databasecontaineren lever i det samme lejernetværk, men vi tilføjede også et internt netværk til kun intra-leje kommunikation. Ikke flere kryds-leje nysgerrighed.

Vi isolerede også databaserne ved at køre dem i separate containere på det samme lejernetværk, ved hjælp af separate datavolumes. Dette sikrede, at selvom en angriber brød ind i appcontaineren, kunne de ikke sniffe databasetrafik fra en anden lejer.

For en dybere gennemgang af netværksisoleringsstrategier, se En praktisk Docker-isolationssikkerhedstjekliste til multi-tenant hosting.

Trin 2: Fjern unødvendige privilegier

Som standard kører Docker-containere med et begrænset sæt Linux-kapaciteter, men de har stadig flere end de fleste applikationer har brug for. Vores containere kørte som root, hvilket tillod processer indeni at udføre handlinger som at montere filsystemer eller ændre kerne parametre. Vi skiftede til at køre applikationen som en ikke-root-bruger inde i containeren (ved at bruge USER direktivet i Dockerfile) og fjernede alle kapaciteter undtagen dem, der absolut var nødvendige. For en typisk webapp kunne det kun være NET_BIND_SERVICE (til at binde til porte under 1024) og CHOWN (til at skrive til mapper). Vi tilføjede også --security-opt no-new-privileges for at forhindre privilegieeskalering.

Dette trin alene eliminerede mange almindelige container-escape-vektorer. En angriber, der kompromitterer webserveren, kan ikke installere pakker, ændre systembinarier eller få adgang til værtens Docker-socket, fordi processen mangler CAP_SYS_ADMIN eller CAP_DAC_OVERRIDE kapaciteterne.

Trin 3: Lås filsystemet ned

Skrivbare filsystemer er en almindelig angrebsflade. Vi gjorde rod-filsystemet skrivebeskyttet (--read-only) for alle containere, og monterede derefter midlertidige filsystemer (tmpfs) for mapper, der har brug for skriveadgang, som /tmp og applikationens cache-mappe. Dette forhindrer en angriber i at ændre applikationskode eller vedvarende ondsindede binarier.

Derudover brugte vi Docker's --mount mulighed til at bind-monte følsomme mapper som Docker-socket'en kun når det var absolut nødvendigt – og aldrig på produktionscontainere. Princippet: hvis containeren ikke har brug for at skrive til en sti, gør den skrivebeskyttet.

Trin 4: Anvend Seccomp- og AppArmor-profiler

Standard seccomp-profiler blokerer allerede mange farlige syscalls, men vi tilpassede dem yderligere til kun at whiteliste de syscalls, som vores applikation faktisk har brug for. Dette er et trade-off, fordi det kræver profilering af applikationen. En enklere tilgang er at bruge Docker's standard seccomp-profil og derefter tilføje --security-opt seccomp=path/to/profile.json hvis du har brug for strengere regler. Tilsvarende kan AppArmor-profiler begrænse containerprocesser til specifikke filstier og kapaciteter. Vi aktiverede AppArmor og brugte en tilpasset profil, der begrænsede adgang til kun applikationens datamapper.

For en omfattende guide til disse hærdningstrin, henvises til Hærdning af Docker-containere til multi-tenant hosting: En trin-for-trin isolationsguide.

Den kontrære holdning: Nogle gange har du brug for VM'er

Uanset hvor hærdet, deler containere værtens kerne. En kerne sårbarhed kan bryde al isolation på én gang. Derfor kører mange sikkerhedsbevidste platforme containere inde i letvægts VM'er – hver lejer får deres egen kerne. Dette tilføjer overhead, men giver en hardware-niveau grænse, som containere alene ikke kan. Hvis dine lejere håndterer kreditkortdata eller helbredsoptegnelser, kan en hybrid tilgang (containere inde i VM'er) være det rigtige valg. Antag ikke, at containerisolation er tilstrækkelig for din trusselsmodel; evaluer følsomheden af data og de regulatoriske krav.

For en dybere sammenligning af isolationsniveauer, læs Design af en multi-tenant Docker-arkitektur: Valg af det rigtige isolationsniveau.

Konklusion: Isolation er en stak, ikke en kontakt

Fixet var ikke en enkelt ændring – det var lagdeling: netværksisolation, begrænsede privilegier, skrivebeskyttede filsystemer og syscall-filtrering. Selv da accepterede vi, at perfekt isolation er umulig med delt kerne-containere. For vores højest sikkerhedslejere flyttede vi dem til dedikerede værter. Lektien: stol ikke på standardindstillinger. Gennemgå din Docker-opsætning, som om et brud allerede er sket. Tiden til at låse ned er før lækket, ikke efter.

Sources (5)