Blog
Zabluda povjerenja: Kako je naš višekorisnički Docker postav propuštao podatke (i kako smo to popravili)
Saznajte kako je naivni Docker postav jednog tima doveo do curenja podataka između korisnika i slojevita strategija izolacije koja je to spriječila.
Sažetak
Docker spremnici nisu izolirani prema zadanim postavkama—dijele kernel domaćina, a bez namjerne konfiguracije, korisnici mogu ometati jedni druge. Ovaj članak prolazi kroz stvarni scenarij u kojem je višekorisnički hosting pružatelj otkrio da klijentski spremnici mogu pristupiti bazama podataka jedni drugih zbog zajedničkog umrežavanja i slabih sigurnosnih zadanim postavki. Prikazujemo korak-po-korak promjene koje su popravile proboj: korisnički definirane mreže po korisniku, korisnici koji nisu root, odbačene mogućnosti, datotečni sustavi samo za čitanje i seccomp profili. Uobičajena pretpostavka je da spremnici inherentno pružaju jaku izolaciju; to osporavamo objašnjavajući zašto VM-ovi još uvijek nude čvršću granicu i kada razmotriti hibridni pristup. Zaključak naglašava da je izolacija slojevita vježba, a ne jedna potvrdna kućica.
Incident: Kad spremnici previše pričaju
Postavili ste Docker na jednom domaćinu za pokretanje više klijentskih web stranica. Svaki klijent ima svoj spremnik—uredno, izolirano okruženje, zar ne? To smo i mi mislili. Sve dok rutinska sigurnosna revizija nije otkrila da spremnik Klijenta A čita MySQL socket spremnika Klijenta B na istom domaćinu. Dijelili su zadane bridge mreže. Još gore, spremnici su radili kao root, tako da je napadač koji je kompromitirao jedan mogao petljati s Docker socketom domaćina ili datotečnim sustavom drugog spremnika. 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 Dockerova imenska prostora i cgroups automatski odvajaju korisnike, ali podcjenjuju koliko izlaza za bijeg ostaje otvoreno prema zadanim postavkama. Zadane bridge mreže ne nude mrežnu izolaciju između spremnika. Rad kao root daje spremniku 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 bio je dati svakom korisniku vlastitu korisnički definiranu Docker mrežu. To sprječava spremnike da dosegnu jedni druge osim ako ih izričito ne povežete. Stvorili smo skriptu koja za svakog korisnika pokreće namjensku mrežu i pričvršćuje njihov aplikacijski spremnik na nju. Spremnik baze podataka živi u istoj korisničkoj mreži, ali također smo dodali internu mrežu samo za komunikaciju unutar korisnika. Nema više njuškanja među korisnicima.
Također smo izolirali baze podataka pokretanjem u zasebnim spremnicima na istoj korisničkoj mreži, koristeći odvojene podatkovne volumene. To je osiguralo da čak i ako se napadač probije u aplikacijski spremnik, ne može njuškati promet baze podataka drugog korisnika.
Za dublje poniranje u strategije mrežne izolacije, pogledajte Praktični Docker sigurnosni popis izolacije za višekorisnički hosting.
Korak 2: Odbacite nepotrebne privilegije
Prema zadanim postavkama, Docker spremnici rade s ograničenim skupom Linux mogućnosti, ali još uvijek imaju više nego što većina aplikacija treba. Naši spremnici radili su kao root, što je omogućavalo procesima unutar njih da izvode radnje poput montiranja datotečnih sustava ili mijenjanja parametara jezgre. Prešli smo na pokretanje aplikacije kao korisnik koji nije root unutar spremnika (koristeći USER direktivu u Dockerfileu) i odbacili 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 spremnika. Napadač koji kompromitira web poslužitelj ne može instalirati pakete, modificirati sistemske binarne datoteke ili pristupiti Docker socketu domaćina jer procesu nedostaju mogućnosti CAP_SYS_ADMIN ili CAP_DAC_OVERRIDE.
Korak 3: Zaključajte datotečni sustav
Zapisivi datotečni sustavi uobičajena su napadna površina. Učinili smo root datotečni sustav samo za čitanje (--read-only) za sve spremnike, a zatim montirali privremene datotečne sustave (tmpfs) za direktorije koji trebaju pristup za pisanje, poput /tmp i direktorija predmemorije aplikacije. To sprječava napadača da modificira kod aplikacije ili trajno pohrani zlonamjerne binarne datoteke.
Osim toga, koristili smo Dockerovu --mount opciju za bind-mount osjetljivih direktorija poput Docker socketa samo kada je to apsolutno potrebno—i nikada na produkcijskim spremnicima. Načelo: ako spremnik ne treba pisati na putanju, učinite je samo za čitanje.
Korak 4: Primijenite Seccomp i AppArmor profile
Zadani seccomp profili već blokiraju mnoge opasne sistemske pozive, ali mi smo ih dodatno prilagodili kako bismo na bijelu listu stavili samo one sistemske pozive koje naša aplikacija stvarno treba. Ovo je kompromis jer zahtijeva profiliranje aplikacije. Jednostavniji pristup je koristiti Dockerov zadani seccomp profil i zatim dodati --security-opt seccomp=path/to/profile.json ako su potrebna stroža pravila. Slično tome, AppArmor profili mogu ograničiti procese spremnika na specifične datotečne putanje i mogućnosti. Omogućili smo AppArmor i koristili prilagođeni profil koji je ograničio pristup samo na podatkovne direktorije aplikacije.
Za sveobuhvatan vodič kroz ove korake otvrdnjavanja, pogledajte Otvrdnjavanje Docker spremnika za višekorisnički hosting: Vodič za izolaciju korak po korak.
Kontrarna perspektiva: Ponekad su potrebni VM-ovi
Bez obzira koliko su otvrdnuti, spremnici dijele kernel domaćina. Ranjivost kernela može odjednom slomiti svu izolaciju. Zbog toga mnoge sigurnosno osviještene platforme pokreću spremnike unutar laganih VM-ova—svaki korisnik dobiva vlastiti kernel. To dodaje režiju, ali pruža granicu na razini hardvera koju spremnici sami ne mogu. Ako vaši korisnici obrađuju podatke o kreditnim karticama ili zdravstvene zapise, hibridni pristup (spremnici unutar VM-ova) može biti pravi izbor. Nemojte pretpostavljati da je izolacija spremnika dovoljna za vaš model prijetnje; procijenite osjetljivost podataka i regulatorne zahtjeve.
Za dublju usporedbu razina izolacije, pročitajte Dizajniranje višekorisničke Docker arhitekture: Odabir prave razine izolacije.
Zaključak: Izolacija je stog, ne prekidač
Popravak nije bio jedinstvena promjena—bilo je to slojevito: mrežna izolacija, ograničene privilegije, datotečni sustavi samo za čitanje i filtriranje sistemskih poziva. Čak i tada, prihvatili smo da je savršena izolacija nemoguća sa spremnicima koji dijele kernel. Za naše najsigurnije korisnike premjestili smo ih na namjenske domaćine. Pouka: ne vjerujte zadanim postavkama. Revidirajte svoj Docker postav kao da se proboj već dogodio. Vrijeme za zaključavanje je prije curenja, ne poslije.


