Belajar Cyber Security #16: Container & Docker Security — Non-Root, Scan Image, dan Jangan Boncengkan docker.sock

Hari ini saya lanjut ke topik yang selama ini saya anggap “sudah aman karena pakai container”: Docker. Di episode #15 kita sudah membahas bagian cloud dari shared responsibility; sekarang giliran lapisan yang berjalan di atasnya. Selama ini mental model saya sederhana — aplikasi saya di container, host saya aman. Setelah baca beberapa kasus escape, ternyata container bukan tembok beton. Ini catatan belajar saya soal keamanan container.

Infografis 4 pilar keamanan container: non-root user, scan image, jaga docker.sock, secrets di runtime
Empat hal yang saya pegang tiap kali menulis Dockerfile atau menjalankan container.

Container bukan mesin virtual

Analogi yang akhirnya nempel di kepala saya: kalau VM itu rumah sendiri dengan halaman dan pagar, container itu kamar-kamar di satu gedung yang sama. Semua kamar berbagi satu dapur, satu instalasi listrik — dalam kasus ini satu kernel Linux. Isolasinya cuma namespace dan cgroup, mekanisme yang menyembunyikan proses dan membatasi resource, bukan memisahkan kernel.

Ini berarti bagi saya: celah di kernel atau di runtime container (runc) bukan sekadar masalah “aplikasi di kamar sebelah” — dia bisa bikin proses lompat keluar dan jalan sebagai root di host. Istilahnya container escape. Di episode #6 soal jaringan dasar kita sudah bicara soal perimeter; di kontainer, perimeternya jauh lebih tipis dari yang saya kira.

Dua kasus nyata yang mengubah cara saya melihat container

Pertama, CVE-2019-5736. Menurut catatan di NVD, runc versi 1.0-rc6 ke bawah (dipakai Docker sebelum 18.09.2) memungkinkan proses di dalam container menimpa binary runc di host lewat /proc/self/exe. Konsekuensinya jelas: kode yang dijalankan di host tetap berjalan sebagai root. Kasus ini yang pertama membuat saya berhenti menganggap enteng kalimat “cukup pakai container”.

Kedua, Leaky Vessels yang diungkap Snyk pada 31 Januari 2024 dan dipatch Docker di hari yang sama. Empat CVE: CVE-2024-21626 di runc (CVSS 8.6, patch di runc 1.1.12), plus tiga di BuildKit — CVE-2024-23651, CVE-2024-23652 (CVSS 10), dan CVE-2024-23653 — yang patch-nya ada di BuildKit 0.12.5, Moby 25.0.2, dan Docker Desktop 4.27.1. Yang paling membuat saya tidak nyaman: vektor serangannya adalah Dockerfile berbahaya. Artinya sekadar build image dari file Dockerfile yang tidak kita tulis sendiri sudah bisa berujung escape. Data Wiz saat itu menyebut setidaknya 80% lingkungan cloud punya instance yang rentan terhadap CVE-2024-21626.

Catatan kecil: semua contoh di sini saya tulis untuk memahami pertahanan. Mengetes keamanan hanya boleh di infrastruktur milik sendiri atau dengan izin tertulis — ingat UU ITE soal akses ilegal sistem elektronik.

Checklist Dockerfile yang saya terapkan sekarang

Perubahan paling sederhana dan paling berdampak: berhenti jalan sebagai root. Secara default Docker menjalankan proses di dalam kontainer sebagai root kalau Dockerfile tidak menyebut USER. Saya mulai dari pola ini:

FROM python:3.12-slim
RUN groupadd -r app && useradd -r -g app app
WORKDIR /app
COPY . .
RUN chown -R app:app /app
USER app
CMD ["python", "app.py"]
  • USER non-root — satu baris, menutup jalur eskalasi paling gampang kalau aplikasi kita sendiri kebobolan.
  • Base image minimal — slim atau alpine, multi-stage build kalau butuh toolchain besar. Makin sedikit software di image, makin kecil permukaan serangan.
  • .dockerignore — supaya .env, .git, dan node_modules tidak ikut ter-copy ke image.
  • --cap-drop=ALL + --read-only saat runtime, dan menghindari --privileged kecuali saya benar-benar paham konsekuensinya.
  • Rootless mode untuk daemon-nya sendiri, sesuai dokumentasi Docker — daemon dan kontainer jalan tanpa hak root di host.

docker.sock: kunci ruang server yang sering saya lihat “dipinjamkan”

Di banyak contoh docker-compose yang beredar, saya menemukan baris -v /var/run/docker.sock:/var/run/docker.sock. Sekilas praktis. Tapi siapa pun yang bisa menjalankan perintah di kontainer itu pada dasarnya punya kendali atas daemon Docker di host — dari socket ia bisa memulai container baru dengan mount filesystem host dan mendapat akses root penuh. Saya sekarang memperlakukan docker.sock seperti kunci ruang server: kalau bukan benar-benar perlu, jangan diboncengkan ke kontainer.

Secrets jangan dibakar ke image, dan scan sebelum deploy

Pelajaran lain yang mahal kalau baru sadar setelah production: setiap baris di Dockerfile jadi layer yang tersimpan permanen di image. ARG atau ENV berisi API key tetap bisa dibaca lewat docker history atau docker inspect, walau nilai akhirnya sudah tidak dipakai. Karena itu sekarang secrets saya masukkan di runtime (environment variable dari orchestrator atau docker secrets), bukan saat build — pembahasan lengkapnya saya simpan untuk episode #18.

Terakhir, scanning. Image yang saya build biasanya tidak bersih dari awal — base image lama selalu membawa CVE paket OS-nya sendiri. Saya jalankan Trivy atau Grype (atau docker scout kalau sudah pakai Docker CLI) di CI, dan menempel image dengan digest, bukan tag :latest, supaya apa yang saya tes persis sama dengan yang jalan di server.

Ringkasnya, container tetap cara deploy yang enak dan ringan — tapi dia memindahkan sebagian tanggung jawab keamanan ke saya: siapa yang build image, dari Dockerfile siapa, dan apa yang diboncengkan ke dalam kontainer. Di episode berikutnya (#17) saya akan lanjut ke ranah yang berhubungan erat: keamanan CI/CD dan supply chain.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses