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”).

No alt text provided for this image

Şekil 1. “sudo apt update”.

İkinci adım olarak, “Docker”ı yüklüyorum (“sudo apt install docker.io”).

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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”).

No alt text provided for this image

Şekil 4. “sudo docker images”.

“Docker repository”den bir imajı çekelim (“sudo docker pull alpine”).

No alt text provided for this image

Şekil 5. “sudo docker pull alpine”.

Artık bir “docker” imajımız mevcut.

No alt text provided for this image

Ş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”).

No alt text provided for this image

Ş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.

No alt text provided for this image

Şekil 8. Dockerfile.

Basit bir “index.html” dosyası oluşturdum.

No alt text provided for this image

Şekil 9. index.html

Ardından da “build” işlemini başlattım.

No alt text provided for this image

Şekil 10. “sudo docker build –t webserver:latest .”.

Şekil 11.’de imajlarımızın listesini görüyoruz.

No alt text provided for this image

Ş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.).

No alt text provided for this image
No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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.).

No alt text provided for this image

Ş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.

No alt text provided for this image

Şekil 20. /tmp’yi dahil ettiğimi konteynerin oluşturulması.

No alt text provided for this image

Şekil 21. Konteynerin görüntülenmesi

No alt text provided for this image

Ş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.).

No alt text provided for this image

Ş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”).

No alt text provided for this image

Ş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.).

No alt text provided for this image

Şekil 25. Docker engine host üzerinde /tmp/file.txt dosyasının oluşturulması.

Şekil 26.’da alpine konteyner tekrar oluşturuldu.

No alt text provided for this image

Ş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.

No alt text provided for this image

Ş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ü.