A virtuális gépek (Virtual Machine – VM) elterjedésével lehetővé vált egy hoszt vagy gazda operációs rendszeren több virtuális gépet futtatni egy hypervisoron keresztül. Ez a hypervisor elfogja a hoszt operációs rendszerre veszélyes utasításokat, így egy virtuális gépből nehéz ártani a gazdagépnek.
A Linux konténerek lényegében folyamatok jól szeparált csoportjai, melyek a Docker megjelenésével kezdtek elterjedni. A konténerekben futó alkalmazások többek között rendelkeznek saját felhasználókkal, fájlrendszerrel, hálózati interfészekkel, ugyanakkor az egyes konténerek osztoznak a hoszt rendszer kernelén és hypervisor sem választja el őket a hardvertől, ami több biztonsági kérdést is felvet.
Az internetről számos előre telepített és konfigurált image-et lehet letölteni, melyek egy paranccsal már el is indíthatók. Érdekes biztonsági kérdéseket vet fel, hogy vajon károsíthatja-e a Dockert futtató rendszert egy kártékony konténer.
Jelen poszt keretében megpróbálok egy átfogó képet adni a Docker és így a Linux konténerek által használt biztonsági technológiákról.
A Docker technológia lehetővé teszi ,,könnyű”, hordozható alkalmazáskonténerek készítését,
melyek képesek futni bármely Dockert támogató Linux rendszeren. A hagyományos, ,,telepítőt szállítunk” megoldással ellentétben kész futtatási környezetet, konfigurációt ad az alkalmazásokhoz, és például lehetővé teszi, hogy egy Red Hat Enterprise Linux 7-re fejlesztett alkalmazás a saját környezetében tudjon futni egy Ubuntu Linux disztribúción.
A Docker több technológiára is épít, többek között a Control Groupokra, kernel namespace-ekre, LXC-re, ahogy az az első ábrán látható . Az általa indított folyamatokat különböző cgroup-okba helyezi, így izolálja őket egymástól. Az egyes folyamatok különböző kernel namespace-ekbe kerülnek, így rendelkezhetnek saját fájlrendszerrel (mount pontokkal), hálózati interfészekkel, egyedi hosztnevekkel, felhasználókkal, stb.
A Docker a futtatási környezetet image-eknek nevezi. Egy ilyen image lényegében egy hordozható chroot, ami több rétegből áll. Az egyes rétegeket az AUFS (Advanced multi layered Unification File System) kezeli, így egy fájl módosítása nem írja felül az eredeti tartalmat, hanem egy új réteget eredményez, ami elfedi az eredeti fájlt. Ezeknek a rétegeknek veszi az unióját az AUFS, ami a végleges image-et eredményezi.
Az image-ek egy minimális OS-t tartalmaznak a Linuxokon megszokott fájlrendszerrel. A Docker konténerek tulajdonképpen az image-ek futó példányai. Ezek a konténerek osztoznak a hoszt rendszer kernelén, felfoghatók jól izolált folyamatokként is. Fontos, hogy a konténerekben egy folyamatnak mindig előtérben kell futnia. Egy folyamat esetén ez triviális, több folyamat esetén egy init-et kell futtatni az előtérben, ami a háttérben elindítja a konténer folyamatait.
A Docker által használt technológiák Forrás: https://blog.docker.com/wp-content/uploads/2014/03/docker-execdriver-diagram.png
Az image-eket kézzel is felépíthetjük, de használhatunk úgynevezett Dockerfile-t is. Minden egyes sorában egy-egy lépés található, csomagok telepítése, fájlok másolása, stb. A
*docker build <a Dockerfile-t tartalmazó könyvtár elérési útja>*
paranccsal lehet az image készítést elindítani. Bővebben a Dockerfile-okról itt olvashatsz.
Control Groupok
A Control Groupok, röviden cgroupok lehetővé teszik folyamatok csoportokba rendezését, ezen csoportok erőforrás használatának monitorozását, szabályozását és egymástól való izolációját. A cgroupok a folyamatokat hierarchikus csoportokba rendezik, amelyekhez különböző alrendszereket (subsytem) rendelnek.
Alrendszerek
Az alrendszerek általában a csoportok erőforrás-használatát szabályozzák, a leggyakoribbak a következők:
- blkio: blokkeszközökhöz szabályozza a hozzáférést
- cpu: súlyozással szabályozza a hozzáférést a CPU sávszélességéhez, az ütemezőre van hatással
- cpuset: az egyes cgroupokat CPU magokhoz lehet rendelni (affinitás), illetve NUMA architektúra esetén memória node-okhoz
- cpuacct: automatikusan jelentést készít a felhasznált CPU erőforrásokról
- devices: eszközökhöz (/dev) szabályozza a hozzáférést típus (blokk, karakter), major és minor azonosító és jogosultság (létrehozás, olvasás, írás)
- freezer: a cgroup-ban futó folyamatokat megfagyasztja, vagy azokat újra elindítja
- memory: a memória használatról készít jelentést, illetve limitálja a maximálisan használható méretet
- net_cls: megjelöli a hálózati csomagokat egy osztály azonosítóval (classid)
- perf_event: lehetővé teszi a monitorozást a perf programmal
- hugetlb: lehetővé teszi nagyméretű virtuális lapok kezelését.
Biztonság
A cgroupok a következő módokon tudják növelni a rendszerek biztonságát:
- a cpu alrendszer segítségével megakadályozható, hogy az egyes konténerek kiéheztessék egymást,
- a memory alrendszerrel memórialimitet lehet beállítani az egyes konténerekre (ebbe a fájl cache is beleszámít),
- a blkio alrendszerrel beállíthatunk egy maximális sávszélességet, amellyel az egyes konténerek a blokkeszközökhöz (pl. merevlemez) hozzáférhetnek,
- a device alrendszer szabályozhatja a blokkeszközökhöz a hozzáférést.
A 1-3. pontokkal elkerülhető a konténerek túlzott erőforrás használata, ami a teljes rendszer összeomlásához vezethet. A 4. ponttal pedig folyamatszinten szabályozhatjuk a hozzáférést egyes eszközökhöz.
Namespace-ek
A Linux kernel namespace-einek lényege, hogy egyes globális erőforrásokat olyan módon lássanak az adott namespace tagjai, mintha az adott erőforrásból lenne egy saját, izolált példányuk. Ezzel a megközelítéssel elérhető, hogy folyamatok csoportja azt hihesse, hogy nem fut más folyamat a rendszeren.
Az mnt namespace
A mnt namespace lehetővé teszi, hogy folyamatok egyes csoportjai más-más mount pontokat lássanak és használhassanak, vagyis ha egy fájlrendszert egy mnt namespace-ben csatoltak fel, akkor azt csak az abban lévő folyamatok fogják látni.
Az uts namespace
Ez a namespace lehetővé teszi, hogy a benne található folyamatok saját host- és doménnévvel rendelkezzenek.
Az IPC namespace
Izolálja a SystemV és a POSIX message queue IPC (Inter Process Communication) technológiákat.