Merhaba;
Konteyner (“container”) teknolojisi artık hayatımızın bir parçası ve yoğun olarak kullanıyoruz. Peki, gerektiği şekilde güvenlik önlemlerimizi acaba alabiliyor muyuz? Bugün yazımda, konteyner oluştururken eğer dikkatli olmazsak ne tür güvenlik açıklarına maruz kalabileceğimizi anlatmak istiyorum. Açıkcası ben testleri yaparken, aman Allah’ım demekten kendimi alamadım ve gerçekten tedirgin oldum. Bu nedenle kesinlikle, konteyner yapısı kullanırken gerekli güvenlik kurallarını uyguladığımızdan emin olmamız son derece kritik bir öneme sahip.
Test ortamın, “Oracle Cloud Infrastructure” bulut yapısında oluşturduğum Ubuntu 18.04. Çalışmama, Ubuntu 18.04 üzerinde paketleri güncelleyerek başlıyorum (“sudo apt update”).
Şekil 1. “sudo apt update”.
İkinci adım olarak, “Docker”ı yüklüyorum (“sudo apt install docker.io”).
Şekil 2. “Sudo apt install docker.io”.
Üçüncü adım olarak, “docker”ı başlatıp, kullanılabilir (“sudo systemctl start docker”, “sudo systemctl enable docker”, “docker –version”) hale getirdim.
Şekil 3. “sudo systemctl start docker”, “sudo systemctl enable docker”, “docker –version”.
“Docker” ile ilgili daha önce yazdığım çok detaylı yazılarım mevcut. Linkedin profilimden ulaşabilirsiniz. Bu yazımda, güvenliğe odaklandığım için basit temel işlemler yaparak ilerliyor olacağım. Docker’ın temel işlemlerini hatırlatmak istiyorum.
Kurulumu yeni yaptığımız için şu anda herhangi bir imajımız mevcut değil (“sudo docker images”).
Şekil 4. “sudo docker images”.
“Docker repository”den bir imajı çekelim (“sudo docker pull alpine”).
Şekil 5. “sudo docker pull alpine”.
Artık bir “docker” imajımız mevcut.
Şekil 6. Alpine imaj.
Alpine imajdan bir konteyner oluşturalım (“sudo docker run -itd alpine”) ve ardından konteynere bağlanalım (“sudo docker ps –aq”, “sudo docker exec -it cf908d8b6a2c sh”).
Şekil 7. “sudo docker run -itd alpine”, “sudo docker ps –aq”, “sudo docker exec -it cf908d8b6a2c sh”.
Hızlıca bir web uygulaması oluşturalım. Kendime, “workspace” isimli bir çalışma alanı oluşturdum ve aşağıda olduğu gibi içine bir Dockerfile ekledim.
Şekil 8. Dockerfile.
Basit bir “index.html” dosyası oluşturdum.
Şekil 9. index.html
Ardından da “build” işlemini başlattım.
Şekil 10. “sudo docker build –t webserver:latest .”.
Şekil 11.’de imajlarımızın listesini görüyoruz.
Şekil 11. İmajların listesi.
“webserver:latest” imajdan konteyner oluşturduk (“sudo docker run -itd -p 8080:80 webserver:latest”) ve oluşan konteynerleri “docker ps” komutuyla gördük (Şekil 12.).
Şekil 12. Webserver:latest konteyner ve konteyner listesi.
Elbette, 8080 portuna bağlanmak için “Oracle Cloud Infrastructure”’da tanımlı VCN’de “default security list” içinde, 8080 portu için “ingress” kuralı ekledim. Ardından, Oracle’ın bana verdiği “public IP” üzerinden web sunucuma bağlandım (Şekil 13). Not: “Oracle Cloud Infrastructure” ile ilgili iki makalem Linkedin’de mevcut. Arzu ederseniz okuyabilirsiniz.
Şekil 13. Konteyner içinde çalıştırdığımız basit bir web sunucu örneği.
Evet, buraya kadar temel docker işlemlerini yaptık. “Cgroups” kavramından bahsetmem gerekiyor. “Cgroups” Linux kernel’in bir özelliği ve bizim konteynerin kaynaklarını kısıtlamamıza olanak sağlıyor. Gelin basit bir örnek ile bu özelliği görelim.
Bu amaç ile, alpine imajımdan, pids sayısı 20 olacak şekilde yeni bir konteyner oluşturdum.
Şekil 14. Alpine imajında pids sayısını 20 ile kısıtladım.
Alpine konteynere bağlanalım. Diğer taraftanda başka bir “shell”den, “docker engine” host üzerinden “docker stats” komutunu çalıştırdım. Proses sayısını hızlıca artırmak için “sleep 60” komutunu kullandım. Gördüğünüz gibi, prosess sayısı, verdiğim limit değere ulaştıktan sonra, “sh: can’t fork: Resource temporarily unavailable” mesajını alıyoruz.
Şekil 15. Alpine konteynere bağlandık ve prosess sayısını artırdık. Ardından, prosesi çalıştırmak için ihtiyaç duyulan kaynağın yetersizliğini işaret eden bir mesaj aldık.
Şekil 16. “Docker engine” host üzerinde “docker stats” komutu çıktısı, proses sayısını alpine konteyner için göstermektedir.
Diğer önemli bir özellik ise “namespaces” kavramıdır. Konteyneri, çalıştığı host’dan izole etmeyi sağlayan bir özelliktir. Aşağıdaki örnekte bakın nasıl bir tehlikeli durum ile karşı karşıyayız?
Öncelikle Ubuntu host üzerinde root yetkisi aldım.
Şekil 17. Ubuntu docker engine host üzerinde root yetkisi aldım.
Root yetkisi ile /tmp altına file.txt isimli dosya oluşturdum. Şekil 18’de gördüğünüz gibi beklediğimiz gibi dosyanın sahibi root.
Şekil 18. file.txt dosyasının oluşturulması.
Root kullanıcısından çıkarak, ubuntu kullanıcısına geçtiğimizde, file.txt dosyasında doğal olarak değişiklik yapamıyoruz (Şekil 19.).
Şekil 19. Ubuntu kullanıcıs ile /tmp/file.txt altında değişiklik yapamıyoruz.
Şimdi /tmp dosyasını da içerecek şekilde yeni bir konteyner oluşturalım. Aşağıda olan komutları lütfen dikkatle takip edin. Konteyner içinde, root’un yazdığı bir dosyanın içeriğini nasıl ezdiğimi görün.
Şekil 20. /tmp’yi dahil ettiğimi konteynerin oluşturulması.
Şekil 21. Konteynerin görüntülenmesi
Şekil 22. Konteyner shell üzerinden, root’un host üzerinde oluşturduğu dosyanın içeriğini ezilmesi.
“Default” olarak davranış bu şekilde. Sizin host üzerinde olan root’un, konteyner içinde olan root ile aynı olmadığını konfigüre etmeniz gerekiyor. Aksi halde bu şekilde daha acı kayıplarla karşılaşabilirsiniz. Konteyner içindeki root’u farklı bir bağlamda (“context”) çalıştırmalısınız. Bu konfigürasyonu yaparsak nasıl bir durum ile karşılaşıyoruz, bir görelim. Öncelikle mecvut docker konteynerleri silelim (şekil 23.).
Şekil 23. Docker konteynerlerin silinmesi.
Ardından, “docker” daemon’ı durduralım ve “namespace” özelliğini kullanacak şekilde tekrar başlatalım (“sudo dockerd –userns-remap=default”).
Şekil 24. “–userns-remap” özelliği ile docker’ın başlatılması.
“Docker engine” host üzerinde, root kullanıcısı olarak tekrar /tmp/file.txt dosyasını oluşturdum (şekil 25.).
Şekil 25. Docker engine host üzerinde /tmp/file.txt dosyasının oluşturulması.
Şekil 26.’da alpine konteyner tekrar oluşturuldu.
Şekil 26. Alpine konteynerin oluşturulması.
“userns-remap” özelliği sonrası “docker engine” host üzerinde root kullanıcısının oluşturduğu /tmp/file.txt dosyasını konteyner üzerinden ezemediğimizi görüyoruz.
Şekil 27. Konteyner üzerinden /tmp/file.txt dosyası ezilemedi.
Konteyner güvenliğinde dikkat etmemiz gereken diğer bir nokta, Docker hub’dan aldığımız imajların bilinen zaafiyetlerinin olup olmadığından emin olmamız. Shellshock, dirty cow gibi. İkinci bölümde böyle bir imajı kullandığımızda, konteyner üzerinden “docker engine” host üzerinde ne kadar kolay komut çalıştırılabileceğini göstereceğim.
Yazım çok fazla uzadığı için okuma kolaylığı olması açısından kalan konuları ikinci bölümde ve daha sonraki bölümlerde anlatacağım. Docker imajları kullanarak, tabiri caiz ise arka kapıdan nasıl “docker engine” host üzerine giriş yapabileceğimizi göreceksiniz. Güvenilir olmayan kaynaklardan gelen imajlarda bu arka kapılar olabilir. Yine, konteyner üzerinde kalıcı volüm “mount”larını kullanarak, ne kadar kolay root kullanıcısı (“privilege escalation”) olabileceğimizi göreceğiz. /var/run/docker.sock dosyasının ne derece önemli olduğunu göreceğiz. /var/run/docker.sock dosyasına ulaşmak root olmak ile eşdeğerdir. Eğer konteyner, “–privileged flag” kullanılarak oluşturulmuş ise, konteynere pek çok yetenek vermiş olabilirsiniz. Bu durumda, saldırgan bu özellikleri kullanarak konteynerden, host üzerine geçebilir. Bu noktada CAP_SYS_MODULE çok önem arzediyor. Konteynere giren bir saldırgan, kernel modülü yükleyerek, host üzerine geçebilir. Yine, konteynere eklediğiniz kritik veri içeren kalıcı (“persistent”) bir volüm, konteyner silindikten sonra silinmeyeceği için, içinde bilgi hala kaldığı için, saldırgan tarafından bu duyarlı veriye kolayca erişilebilir. Docker remote API kullanılabilir haldeyse, yine açık noktalardan saldırgan fayda sağlayabilir. “Docker secret” yönetimi yine ziyadesiyle dikkat etmemiz gereken bir konu. Burada ifade ettiğim tüm durumları, sistem üzerinde size gösterebilirim. Ikinci bölümde bu açıkları kullanarak konteyner üzerinden host üzerine ne kolay atlayabileceğimizi göreceğiz.
Ama sakın endişe etmeyin tüm bu açıkları kontrol edebileceğimiz, önlem alabileceğimiz elbette farklı alternatiflerimiz mevcut. “Apparmor” linux kernel modülü bunlardan birisi. Yine “seccomp” profillerini kullanabiliriz. Konteynerlerin içerdiği zaafiyetlerin statik analizini “clair” isimli açık kaynak kodla yapabileceğimiz gibi ticari olarakta AquaSecurity, Twistlock gibi çözümleri kullanabiliriz. Gantek olarak AquaSecurity ile partner olduk. Tiwstlock, Paloalto üzerinden artık satılıyor. Aynı şekilde Paloalto ile de partner’ız. Aqua Security https://www.aquasec.com/ bu yeni teknolojilerdeki güvenlik çözümlerine odaklanmış bir firma. “O’Reilly Container Security” kitabı da çok güzel bir kaynak. Aqua Security üzerinden indirebilirsiniz.
İkinci bölümde görüşmek üzere. Udemy’de “Hacking and Securing Docker Containers“ isimli eğitimi mutlaka almanızı öneriyorum. “CloudSecTraining” tarafından hazırlanmış. Başından kalkmadan eğitimi bir çırpıda bitirmek istiyorsunuz. Benimde burada anlattıklarımın çoğu bu eğitimden esinlendiğim şeylerdir.
Bir konuyu daha eklemek istiyorum. Bulut üzerinden “compute instance” almanın kolaylığını yaşadım. Eğitimi tamamlar tamamlamaz hemen bir sistem üzerinde bunları denemeliyim dedim. 3 dakika içinde “Oracle Cloud Infrastructure” üzerinden Ubuntu “instance”mı oluşturup ana hedefim olan docker işlemlerimi yapmaya başladım.
Asiye Yiğit – 24 Mayıs 2020 – Ramazan Bayramının birinci günü.
Makale için teşekkürler.
Yalnız ekran alıntıları çıkmadığı için anlaşılması zor oluyor.
Merhaba, yorumunuz için teşekkürler. Ekran alıntıları düzeltildi.