Anda bisa menjalankan Docker di production selama berbulan-bulan tanpa masalah yang terlihat. Container berjalan, aplikasi merespons, tidak ada yang rusak. Lalu satu port yang terbuka atau satu permission yang salah konfigurasi memberikan celah bagi penyerang tanpa perlu usaha lebih. Sebagian besar kesalahan keamanan Docker tidak tampak seperti kesalahan sampai sesuatu akhirnya bermasalah.
Artikel ini membahas konfigurasi-konfigurasi spesifik yang membuat lingkungan container berisiko, apa yang bisa dieksploitasi penyerang dari masing-masing konfigurasi tersebut, dan diakhiri dengan checklist yang bisa Anda jalankan pada setup Anda sendiri sekarang.
Mengapa Keamanan Docker Lebih Rumit dari yang Terlihat
Container terasa terisolasi. Anda menjalankan satu container, ia berjalan di process space-nya sendiri, dan dari dalam container tersebut, container lain seolah tidak ada. Isolasi memang ada, tetapi sifatnya hanya sebagian. Container berbagi kernel host, yang berarti dalam kondisi tertentu, sebuah proses di dalam container bisa mengakses sistem host sepenuhnya.
Docker dikonfigurasi untuk kemudahan developer, bukan untuk keamanan production. Akses root aktif. Semua port bisa di-bind ke semua interface. Tidak ada monitoring runtime. Kebanyakan developer menerima pengaturan tersebut, men-deploy container, lalu melanjutkan pekerjaan. Pendekatan itu wajar untuk tahap awal, tetapi bukan postur keamanan yang matang.
Menurut Laporan Red Hat 2024 State of Kubernetes Security, 67% organisasi menunda atau memperlambat deployment aplikasi karena kekhawatiran keamanan container atau Kubernetes. Hambatan itu biasanya bukan berasal dari serangan. Melainkan dari tim yang menyadari bahwa setup container mereka membutuhkan penguatan keamanan yang belum pernah dibangun sebelumnya.
Kami sering melihat container berjalan di production dengan konfigurasi yang sama seperti di mesin lokal developer. Di situlah kesalahan keamanan Docker cenderung menumpuk diam-diam, tanpa gejala yang terlihat sampai ada audit atau kegagalan terjadi.
Kesalahan-kesalahan yang menciptakan celah tersebut bersifat spesifik, dapat diprediksi, dan sebagian besar bisa dihindari, dimulai dari level konfigurasi.
Kesalahan Konfigurasi Docker yang Umum
Sebagian besar pelanggaran keamanan container tidak dimulai dari eksploitasi zero-day. Semuanya bermula dari konfigurasi yang ditetapkan sejak awal, tanpa terlalu mempertimbangkan eksposur jaringan atau cakupan privilese.
Pengaturan default Docker dirancang agar bisa langsung berjalan. Celah antara fungsional dan aman adalah tempat risiko keamanan container Docker menumpuk, terutama pada setup self-hosted yang di-deploy lalu tidak pernah ditinjau kembali.
Kami sering melihat pola ini: container di server dengan IP publik yang memiliki binding port, pengaturan user, dan konfigurasi jaringan persis seperti saat pertama kali di-deploy.
Menjalankan Container sebagai Root
Ketika Anda menjalankan container Docker tanpa menentukan user, container tersebut berjalan sebagai root. Artinya, setiap proses di dalam container, termasuk aplikasi Anda, memiliki hak akses tingkat root dalam namespace container tersebut.

Root di dalam container tidak sama dengan root di host, tetapi pemisahannya tidak absolut. Eksploitasi privilege escalation yang menargetkan runtime, seperti runc CVE-2019-5736 yang sudah terdokumentasi dan kelemahan runtime serupa, seringkali membutuhkan proses container yang berjalan sebagai root untuk berhasil.
Container non-root menghilangkan kebutuhan proses root yang menjadi syarat eksploitasi semacam itu, sehingga secara signifikan mempersempit permukaan serangan untuk kelas kerentanan tersebut, meskipun tidak sepenuhnya menghilangkan risiko container escape.
Menambahkan direktif USER ke Dockerfile Anda menangani hal ini. Beberapa image resmi sudah menyertakan pengguna tanpa hak istimewa yang bisa diaktifkan dengan direktif USER, tetapi banyak yang masih menggunakan root secara default tanpa app user yang siap pakai. Dalam kasus seperti itu, buat pengguna di Dockerfile sebelum berpindah ke pengguna tersebut. Untuk sebagian besar setup yang dihosting sendiri, perubahan tunggal ini menghilangkan satu kategori risiko eskalasi secara keseluruhan.
Membuka Terlalu Banyak Port ke Internet Publik
Saat Anda mempublikasikan port dengan Docker, Docker menulis aturan iptables-nya sendiri secara langsung. Aturan tersebut berjalan sebelum aturan firewall di level host. Ini adalah perilaku yang sudah diketahui dan dilaporkan oleh komunitas dan yang didokumentasikan dalam panduan packet filtering Docker, bukan kesalahan konfigurasi, dan artinya UFW serta alat serupa tidak memblokir apa yang sudah dibuka oleh Docker.

Docker menulis langsung ke iptables, melewati pengaturan default UFW dan firewalld di banyak host Linux. Artinya, port yang terikat ke 0.0.0.0 bisa dapat diakses secara publik meskipun firewall Anda tampak sudah dikonfigurasi. Security group cloud dan aturan chain DOCKER-USER masih bisa memblokir lalu lintas tersebut, sehingga eksposur aktualnya bergantung pada konfigurasi jaringan spesifik Anda.
Ikat layanan ke 127.0.0.1 jika memungkinkan, arahkan lalu lintas yang menghadap publik melalui reverse proxy, dan publikasikan hanya yang benar-benar memerlukan akses eksternal. Reverse proxy adalah cara paling andal untuk mengontrol apa yang terekspos dari luar host.
Mengabaikan Isolasi Jaringan Antar Container
Setiap container dalam jaringan tersebut dapat menjangkau container lain tanpa batasan apa pun. Default bridge tidak menerapkan filtering lalu lintas antar container yang berbagi jaringan yang sama, dan sebagian besar setup tidak pernah mengubah konfigurasi tersebut.

Jika satu container berhasil disusupi, komunikasi yang terbuka itu menjadi jalur pergerakan lateral. Container frontend dapat menjangkau database, API internal, atau apa pun yang ada di jaringan default bridge yang sama, bahkan ketika akses tersebut tidak pernah dimaksudkan.
Jaringan yang didefinisikan pengguna memberi Anda kontrol eksplisit atas container mana yang dapat berkomunikasi, tetapi satu jaringan kustom yang digunakan bersama oleh semua layanan Anda tetap memungkinkan lalu lintas bebas antar container. Isolasi nyata mengharuskan layanan yang tidak seharusnya saling berkomunikasi ditempatkan di jaringan yang terpisah. Menonaktifkan default bridge adalah titik awal, bukan garis akhir.
Mengabaikan Socket Docker
Socket Docker di /var/run/docker.sock adalah antarmuka kontrol untuk seluruh engine Docker. Memasangnya ke dalam container memberi container tersebut akses API langsung ke daemon yang berjalan di host.

Dengan akses tersebut, sebuah container dapat menjalankan container baru, memasang direktori host, memeriksa dan memodifikasi container yang sedang berjalan, dan secara efektif mengendalikan mesin host. Permukaan serangan ini setara dengan akses root di host, itulah mengapa setiap alat yang memerlukan akses socket perlu dievaluasi dengan cermat.
Untuk sebagian besar kasus penggunaan, ada alternatif yang lebih aman: API dengan cakupan terbatas atau alat manajemen Docker yang tidak memerlukan akses socket. Docker-in-Docker memiliki trade-off keamanan dan operasional tersendiri dan bukan pengganti yang mudah.
Kesalahan konfigurasi menciptakan eksposur awal. Pilihan image dan dependensi menentukan seberapa jauh eksposur itu bertambah seiring waktu.
Kesalahan Image dan Secrets yang Bertahan Melampaui Container
Saat Anda menghentikan container, kesalahan konfigurasi di dalamnya ikut berhenti. Saat Anda membangun ulang dari image yang membawa kerentanan atau kredensial yang dikodekan langsung, masalah tersebut ikut aktif kembali bersama container. Kesalahan di level image tidak direset di antara setiap deploy.
Image tersebut ikut terbawa ke setiap environment yang menariknya, setiap registry yang menyimpannya, dan setiap anggota tim yang menjalankannya. Sifat yang menetap ini menjadikan manajemen image dan secrets sebagai kategori risiko tersendiri, yang perlu diaudit secara terpisah dari konfigurasi.
Pola ini sering kami temui: sebuah image yang dipilih dengan cermat saat awal proyek dan tidak pernah di-rebuild sejak saat itu, perlahan-lahan menyimpang dari baseline keamanan yang awalnya diwakilinya.
Menggunakan Image yang Tidak Terpercaya atau Sudah Usang
Registry publik terbuka untuk siapa saja. Image berbahaya telah didistribusikan melalui Docker Hub yang menyematkan crypto-miner dan backdoor di dalam riwayat layer — dan tetap ada meski container di-restart. Verifikasi sebelum melakukan pull sangat penting, terutama untuk image dari penerbit tidak resmi atau tidak dikenal.

Masalah lainnya adalah keusangan. Image resmi yang Anda pull enam bulan lalu dan tidak pernah di-rebuild sejak saat itu terus mengakumulasi kerentanan Docker yang belum ditambal setiap kali ada CVE baru yang diungkap terhadap paket-paketnya. Image tersebut tidak rusak. Hanya saja sudah tidak lagi terkini.
Laporan Sonatype 2024 State of the Software Supply Chain menemukan bahwa 95% kali sebuah komponen yang rentan dikonsumsi, versi yang sudah diperbaiki sebenarnya sudah tersedia, dan 80% dependensi aplikasi tidak di-upgrade selama lebih dari satu tahun. Pola ini juga berlaku untuk base image Docker, karena keduanya mengandalkan paket open-source yang sama.
Gunakan image resmi dari penerbit terverifikasi dan pin tag versi spesifik, bukan mengandalkan "latest". Buat jadwal rebuild rutin agar image Anda tetap terkini.
Hardcoding Secrets di Dockerfile dan Compose File
Kredensial yang ditulis ke instruksi ENV atau ARG pada Dockerfile, di-hardcode ke dalam blok environment Compose, diteruskan sebagai argumen build, atau disimpan di file .env yang di-commit ke version control tidak akan hilang saat container dihentikan. Kredensial tersebut tetap ada di riwayat layer image atau source control, dan dapat diakses oleh siapa saja yang bisa menjangkaunya.

Ini adalah salah satu kesalahan keamanan Docker yang paling sering diabaikan karena tidak menimbulkan masalah yang terlihat selama pengembangan. Sebuah API key dalam instruksi ENV berfungsi dengan benar. Namun ia juga ada di repository Anda, tertanam di dalam image, dan ikut tersebar ke mana pun image itu dibawa.
Docker Compose modern mendukung mekanisme secrets native yang me-mount kredensial saat runtime tanpa menyematkannya ke dalam image. Secrets API milik Docker dan secrets manager eksternal mengikuti prinsip yang sama. Inilah opsi yang menjaga kredensial sepenuhnya di luar build artifact dan file yang di-commit.
Environment variable saat runtime adalah peningkatan dari kredensial yang di-hardcode, tetapi tetap bisa terekspos melalui output Docker inspect, log, dan crash dump. Ini memang selangkah lebih baik dari secrets yang tertanam di image, tapi bukan solusi yang sudah selesai.
Tidak Memperbarui Container Image Secara Rutin
Menjalankan image yang sama selama berbulan-bulan adalah kebiasaan yang umum. Setiap hari yang berlalu setelah kerentanan baru diungkap, namun sebelum Anda melakukan rebuild, container Anda membawa jendela eksposur yang terus melebar tanpa ada perubahan yang terlihat.
Buat jadwal rebuild yang konsisten. Otomasi proses tersebut semaksimal mungkin, dan jalankan vulnerability scanner secara berkala terhadap image yang sedang digunakan. Tujuannya bukan kesempurnaan. Tujuannya adalah mempersempit jarak waktu antara patch dirilis dan patch tersebut di-deploy.
Access control dan monitoring sering kali tidak diprioritaskan dalam deployment yang cepat. Padahal, keduanya juga merupakan kategori di mana insiden paling lama tidak terdeteksi.
Celah Access Control dan Visibilitas
Setelah sebuah container berjalan dengan konfigurasi yang solid dan image yang terkini, masih ada dua kategori kegagalan yang tersisa. Keduanya pada dasarnya tidak terlihat: Anda tidak akan menyadari masalah access control yang lemah sampai seseorang memanfaatkannya, dan Anda tidak akan menyadari celah monitoring sampai Anda perlu menyelidiki aktivitas yang tidak pernah dicatat.
Yang sama Riset Red Hat 2024 menemukan bahwa 42% tim tidak memiliki kemampuan yang memadai untuk menangani keamanan container dan ancaman terkait.
Kami menemukan bahwa celah monitoring biasanya baru terlihat saat investigasi insiden, bukan sebelumnya. Pada saat visibilitas menjadi prioritas, seringkali sudah dalam posisi merespons sesuatu, bukan mencegahnya.
Autentikasi Lemah dan Dashboard Manajemen yang Terekspos
Dashboard manajemen container yang terpasang di IP publik tanpa autentikasi tidak membutuhkan penyerang yang canggih. Mereka hanya perlu tahu alamatnya. Itu jauh lebih mudah dari yang kebanyakan tim sadari.

Tool monitoring dan manajemen yang di-hosting sendiri biasanya hadir dengan antarmuka web yang dapat diakses dari semua network interface. Membiarkannya di IP publik tanpa autentikasi sama saja dengan membiarkan panel admin tidak terkunci — dalam konteks container.
Autentikasi, reverse proxy, dan penempatan di jaringan privat adalah standar minimum. Access control adalah langkah konfigurasi yang ditambahkan ke setiap antarmuka manajemen, bukan sesuatu yang sudah aktif sejak awal.
Prinsip yang sama berlaku untuk Docker CLI dan GUI management; akses level admin ke daemon membawa risiko yang sama, apa pun antarmuka yang digunakan.
Tidak Memantau Aktivitas Container
Jika sebuah container berhasil disusupi, aktivitas penyerang meninggalkan jejak: perubahan perilaku proses, koneksi jaringan yang tidak biasa, dan modifikasi file yang tidak terduga. Tanpa log collection yang terpasang, jejak itu tidak tersedia dalam bentuk yang bisa ditindaklanjuti.
Centralized log collection, container audit logging, dan tool runtime monitoring memberi data yang dibutuhkan untuk mendeteksi aktivitas abnormal sebelum makin meluas. Tujuannya bukan menganalisis setiap baris log, melainkan memastikan data tersedia saat perlu dilakukan investigasi.
Setup container yang berjalan diam-diam di production tanpa log pipeline dan tanpa alert bukan berarti minim perawatan. Artinya tidak pernah diperiksa. Itu dua kondisi operasional yang sangat berbeda.
Mengapa Lingkungan Infrastruktur Juga Penting
Keamanan container dimulai dari konfigurasi, tetapi konfigurasi berjalan di atas infrastruktur. Host dengan konfigurasi jaringan yang keliru, resource yang dibagi bersama, atau tanpa network-level filtering menciptakan kondisi yang memengaruhi setiap container di atasnya. Memperbaiki setup container dan memperbaiki konfigurasi server adalah dua tugas yang berbeda.
Banyak celah keamanan Docker yang diperbesar oleh kondisi yang diwarisi oleh container itu sendiri:
- Server shared-tenancy tanpa isolasi hardware antar-tenant
- Kernel host yang berjalan tanpa patch terbaru
- Host tanpa network-level filtering bawaan
Ini tidak menghilangkan kebutuhan akan langkah konfigurasi di atas, karena container hardening yang benar tetap penting di lapisan infrastruktur mana pun. Memulai di infrastruktur yang terisolasi menghilangkan satu lapisan kekhawatiran dari persamaan ini.
Di Cloudzy, kami menawarkan dua pilihan sesuai kebutuhan setup Anda:
- Linux VPS: lingkungan bersih untuk men-deploy Docker sendiri dan menerapkan langkah-langkah hardening dalam artikel ini
- Portainer VPS: opsi one-click dengan Portainer yang sudah terpasang; server langsung berjalan dan Anda sudah berada di dashboard
Kedua opsi berjalan di infrastruktur yang sama: virtualisasi KVM, AMD Ryzen 9 CPU dengan boost clock hingga 5.7 GHz, memori DDR5, penyimpanan NVMe SSD, jaringan hingga 40 Gbps, dan perlindungan DDoS gratis melalui BuyVM filtering, tersebar di 12 lokasi global dengan uptime SLA SLA 99,95%.
Untuk pembahasan lebih mendalam tentang menjalankan Portainer di VPS, kami membahasnya dalam artikel khusus.
Checklist Keamanan Praktis untuk Deployment Docker
Kesalahan keamanan Docker di atas sebagian besar berasal dari satu keputusan konfigurasi yang dibuat sekali dan tidak pernah ditinjau ulang. Menjalankan checklist ini pada setup yang sudah ada akan mengungkap celah-celah tersebut. Ini berfungsi sebagai audit, bukan panduan deployment.
Praktik keamanan Docker berikut mencakup cara mengamankan container Docker dari kesalahan konfigurasi umum yang telah dijelaskan di atas.
Referensi Cepat: 9 Kesalahan Umum
| Kesalahan | Kategori | Perbaikan Singkat |
| Berjalan sebagai root | Konfigurasi | Tambah USER directive ke Dockerfile Anda |
| Port terikat ke 0.0.0.0 | Konfigurasi | Bind ke 127.0.0.1 dan arahkan melalui reverse proxy |
| Tidak ada isolasi jaringan | Konfigurasi | Pisahkan layanan ke jaringan user-defined yang berbeda sesuai kebutuhan akses. |
| Docker socket terpasang | Konfigurasi | Hapus mount tersebut; gunakan API yang terbatas ruang lingkupnya atau alternatif lain |
| Image tidak tepercaya atau sudah usang | Gambar | Gunakan image resmi dengan tag versi yang dipinned |
| Rahasia yang dikodekan keras | Gambar | Pindahkan kredensial ke env var saat runtime atau gunakan secrets manager |
| Tidak ada jadwal rebuild image | Gambar | Tetapkan jadwal rebuild bulanan; otomatiskan jika memungkinkan |
| Dashboard Tak Terautentikasi | Akses | Tambahkan autentikasi dan pindahkan UI manajemen ke jaringan privat |
| Tidak ada pengumpulan log container | Akses | Siapkan logging terpusat dan pemantauan runtime |
Kami menyarankan untuk menjalankannya terhadap setup yang sudah ada terlebih dahulu, karena di situlah celah paling mungkin sudah ditemukan.
Container yang berjalan sebagai non-root: Periksa Dockerfile Anda untuk directive USER. Jika tidak ada, container berjalan sebagai root.
Port binding dibatasi ke localhost atau melalui proxy: Jalankan docker ps dan periksa port binding. Entri 0.0.0.0:PORT dapat diakses secara publik di host yang tidak memiliki security group hulu, firewall eksternal, atau aturan chain DOCKER-USER yang memblokirnya.
Jaringan bridge kustom sedang digunakan: Container di default bridge Docker dapat saling berkomunikasi secara bebas. Container di user-defined bridge yang sama pun masih bisa saling berkomunikasi, jadi pisahkan layanan ke jaringan yang berbeda berdasarkan batas kepercayaan untuk isolasi yang sesungguhnya.
Docker socket tidak terpasang di container: Periksa file Compose dan argumen run. Jika /var/run/docker.sock muncul sebagai volume, pastikan itu memang diperlukan dan disengaja.
Base image dari penerbit terverifikasi dengan versi yang terpaku: Perintah FROM ubuntu:latest mengambil versi yang tidak ditentukan dan berpotensi sudah usang. Paku ke rilis spesifik.
Tidak ada rahasia di file Docker, file Compose, atau argumen build: Riwayat layer image menyimpan kredensial meski container sudah dihapus. Gunakan Compose secrets, Swarm secrets, build secret mounts, atau secrets manager eksternal. Variabel lingkungan saat runtime lebih baik dari nilai yang ditulis langsung, namun tetap akan terlihat di output inspect dan log.
Jadwal rebuild image sudah ditentukan: Image lama menumpuk celah keamanan. Rebuild setiap bulan cukup untuk menjaga jendela paparan tetap terkendali bagi kebanyakan setup.
Antarmuka manajemen dilindungi autentikasi: Dashboard mana pun di IP publik tanpa autentikasi adalah pintu masuk yang terbuka. Penempatan di jaringan privat lebih disarankan bila memungkinkan.
Log container sedang dikumpulkan: Tanpa pipeline log, deteksi insiden baru terjadi saat dampaknya sudah terlihat di sistem. Itu sinyal yang terlambat untuk ditindaklanjuti.
Kesimpulan
Konfigurasi default Docker dirancang untuk kemudahan, bukan keamanan. Sebagian besar kesalahan yang dibahas dalam artikel ini berakar dari pengaturan yang tidak pernah diubah setelah deployment awal, bukan dari serangan yang canggih.
Perbaikannya sebagian besar adalah keputusan konfigurasi sekali jalan: direktif USER, perubahan binding port, jaringan kustom, jadwal rebuild. Tidak ada yang membutuhkan tooling baru untuk kebanyakan setup.
Membenahi konfigurasi container adalah tugas pertama. Infrastruktur tempatnya berjalan adalah tugas kedua. Keduanya penting, dan tidak ada yang bisa menggantikan yang lain.