Blog
Zabluda povjerenja: Kako je naša multi-tenant Docker postavka procurila podatke (i kako smo to popravili)
Saznajte kako je naivna Docker postavka jednog tima dovela do curenja podataka između tenanta i slojevita strategija izolacije koja ga je spriječila.
Sažetak
Docker kontejneri nisu izolirani po defaultu—dijele host kernel, a bez namjerne konfiguracije, tenanti mogu ometati jedni druge. Ovaj članak prolazi kroz stvarni scenarij gdje je multi-tenant hosting provajder otkrio da klijentski kontejneri mogu pristupiti bazama podataka jedni drugih zbog zajedničkog umrežavanja i slabih sigurnosnih postavki. Pokazujemo korak-po-korak promjene koje su popravile proboj: mreže definirane po tenantima, ne-root korisnici, uklonjene mogućnosti, samo-čitljivi fajl sistemi i seccomp profili. Uobičajena pretpostavka je da kontejneri inherentno pružaju jaku izolaciju; mi to osporavamo objašnjavajući zašto VM-ovi i dalje nude čvršću granicu i kada razmotriti hibridni pristup. Zaključak naglašava da je izolacija slojevita vježba, a ne jedan checkbox.
Incident: Kad kontejneri previše pričaju
Postavili ste Docker na jednom hostu za pokretanje više klijentskih web stranica. Svaki klijent ima svoj kontejner—uredno, izolirano okruženje, zar ne? To smo i mi mislili. Dok rutinska sigurnosna revizija nije otkrila da kontejner Klijenta A čita MySQL socket kontejnera Klijenta B na istom hostu. Dijelili su defaultnu bridge mrežu. Što je gore, kontejneri su radili kao root, pa je napadač koji je kompromitirao jedan mogao dirati Docker socket hosta ili fajl sistem drugog kontejnera. Proboj nije bio sofisticirani exploit; bila je to osnovna pogrešna konfiguracija. Podaci su procurili. Povjerenje je nestalo.
Scenarij neuspjeha nije rijedak. Mnogi timovi pretpostavljaju da Docker-ovi namespace-ovi i cgroup-ovi automatski ograđuju tenante, ali potcjenjuju koliko izlaza za bijeg ostaje otvoreno po defaultu. Defaultne bridge mreže ne nude mrežnu izolaciju između kontejnera. Rad kao root daje kontejneru više moći nego što je potrebno. A bez eksplicitnih ograničenja resursa, jedan bučni susjed može uskratiti CPU ili memoriju drugima.
Korak 1: Prestanite dijeliti jednu mrežu
Naš prvi popravak je bio dati svakom tenantu svoju korisnički definiranu Docker mrežu. To sprječava kontejnere da dopru jedni do drugih osim ako ih eksplicitno ne povežete. Kreirali smo skriptu koja za svakog tenanta pokreće namjensku mrežu i pričvršćuje njihov aplikacijski kontejner na nju. Kontejner baze podataka živi u istoj mreži tenanta, ali smo također dodali internu mrežu samo za intra-tenant komunikaciju. Nema više njuškanja između tenanta.
Također smo izolirali baze podataka pokrećući ih u zasebnim kontejnerima na istoj mreži tenanta, koristeći zasebne volumene podataka. To je osiguralo da čak i ako napadač uđe u aplikacijski kontejner, ne može prisluškivati promet baze podataka drugog tenanta.
Za dublje istraživanje strategija mrežne izolacije, pogledajte Praktična Docker sigurnosna lista za izolaciju multi-tenant hostinga.
Korak 2: Uklonite nepotrebne privilegije
Po defaultu, Docker kontejneri rade s ograničenim skupom Linux mogućnosti, ali i dalje imaju više nego što većina aplikacija treba. Naši kontejneri su radili kao root, što je omogućavalo procesima unutar da izvrše radnje poput montiranja fajl sistema ili mijenjanja kernel parametara. Prebacili smo se na pokretanje aplikacije kao ne-root korisnik unutar kontejnera (koristeći USER direktivu u Dockerfile-u) i uklonili sve mogućnosti osim onih koje su apsolutno potrebne. Za tipičnu web aplikaciju, to mogu biti samo NET_BIND_SERVICE (za vezivanje na portove ispod 1024) i CHOWN (za pisanje u direktorije). Također smo dodali --security-opt no-new-privileges kako bismo spriječili eskalaciju privilegija. Sam ovaj korak eliminirao je mnoge uobičajene vektore bijega iz kontejnera.
Napadač koji kompromituje web server ne može instalirati pakete, mijenjati sistemske binarne datoteke ili pristupiti Docker socket-u hosta jer procesu nedostaju mogućnosti CAP_SYS_ADMIN ili CAP_DAC_OVERRIDE.
Korak 3: Zaključajte fajl sistem
Zapisivi fajl sistemi su česta napadačka površina. Učinili smo root fajl sistem samo-čitljivim (--read-only) za sve kontejnere, a zatim montirali privremene fajl sisteme (tmpfs) za direktorije koji trebaju pravo pisanja, poput /tmp i direktorija za keš aplikacije. To sprječava napadača da modificira aplikacijski kod ili trajno postavi zlonamjerne binarne datoteke. Dodatno, koristili smo Docker opciju --mount za bind-mount osjetljivih direktorija poput Docker socket-a samo kada je apsolutno neophodno—i nikada na produkcijskim kontejnerima. Princip: ako kontejner ne treba pisati na putanju, učinite je samo-čitljivom.
Korak 4: Primijenite Seccomp i AppArmor profile
Defaultni seccomp profili već blokiraju mnoge opasne sistemske pozive, ali smo ih dodatno prilagodili da dozvolimo samo one sistemske pozive koje naša aplikacija stvarno treba. Ovo je kompromis jer zahtijeva profiliranje aplikacije. Jednostavniji pristup je koristiti Docker-ov defaultni seccomp profil, a zatim dodati --security-opt seccomp=path/to/profile.json ako su potrebna stroža pravila. Slično tome, AppArmor profili mogu ograničiti kontejnerske procese na specifične putanje i mogućnosti. Omogućili smo AppArmor i koristili prilagođeni profil koji je ograničio pristup samo na aplikacijske direktorije sa podacima.
Za sveobuhvatan vodič o ovim koracima očvršćavanja, pogledajte Očvršćavanje Docker kontejnera za multi-tenant hosting: Vodič za izolaciju korak po korak.
Suprotni pogled: Ponekad trebate VM-ove
Bez obzira koliko su očvršćeni, kontejneri dijele kernel hosta. Ranjivost kernela može odjednom probiti svu izolaciju. Zbog toga mnoge sigurnosno svjesne platforme pokreću kontejnere unutar laganih VM-ova—svaki tenant dobiva svoj kernel. To dodaje režijske troškove, ali pruža granicu na nivou hardvera koju sami kontejneri ne mogu. Ako vaši tenanti rukuju podacima o kreditnim karticama ili zdravstvenim kartonima, hibridni pristup (kontejneri unutar VM-ova) može biti pravi izbor. Ne pretpostavljajte da je izolacija kontejnera dovoljna za vaš model prijetnji; procijenite osjetljivost podataka i regulatorne zahtjeve.
Za dublje poređenje nivoa izolacije, pročitajte Dizajniranje multi-tenant Docker arhitekture: Odabir pravog nivoa izolacije.
Zaključak: Izolacija je stog, a ne prekidač
Popravak nije bio jedna promjena—bio je slojevit: mrežna izolacija, ograničene privilegije, samo-čitljivi fajl sistemi i filtriranje sistemskih poziva. Čak i tada, prihvatili smo da je savršena izolacija nemoguća s kontejnerima koji dijele kernel. Za naše najsigurnije tenante, prebacili smo ih na namjenske hostove. Lekcija: ne vjerujte defaultnim postavkama. Revidirajte svoju Docker postavku kao da se proboj već dogodio. Vrijeme za zaključavanje je prije curenja, a ne poslije.


