Dua chapter terakhir kita habiskan untuk memangkas hak: identitas diturunkan ke user biasa, capability dicabut sampai tersisa hanya yang dibuktikan dibutuhkan aplikasi. Sekarang kita berhadapan dengan bendera yang bekerja ke arah sebaliknya — satu kata pada docker run yang membalik seluruh kerja keras itu dalam sekejap: --privileged. Bendera ini tetap muncul di banyak tutorial internet, sering dijalankan begitu saja untuk “menghilangkan error”, dan hampir selalu tanpa penjelasan apa yang sebenarnya dilepasnya. Chapter ini membongkar keempat pembatas yang dicabut --privileged sekaligus, membuktikan dampaknya lewat mount yang tiba-tiba berhasil dan disk host yang tiba-tiba terlihat, lalu memetakan lima alternatif yang lebih aman untuk setiap alasan klasik yang biasanya dikemukakan.

Empat Pembatas yang Dilepas Sekaligus #

--privileged bukan menambah satu hak — ia membongkar empat keluarga pembatas dalam satu tarikan napas:

PembatasContainer bawaanContainer --privileged
Capabilities12 bawaan; sisanya dicabutsemua capability diberikan, termasuk SYS_ADMIN
Device /devhanya node minimal buatan Dockerseluruh device host terlihat
Seccompprofil default menyaring syscalldimatikan
AppArmor dan SELinuxprofil default terpasangdilepas

Mari kita telaah arti keempat baris tersebut bersama-sama:

  1. Semua capability diberikan — daftar dua belas dari Chapter Linux Capabilities berubah menjadi daftar lengkap milik root host; SYS_ADMIN yang kita tandai sebagai pintu utama breakout kini melekat pada proses.
  2. Seluruh device host terlihat — antarmuka /dev container yang biasanya hanya berisi belasan node aman kini menampilkan semua device yang dimiliki host, termasuk disk fisik.
  3. Seccomp dimatikan — filter syscall yang menolak syscall berbahaya sebelum menyentuh kernel ikut dicabut; pintu depan berdiri terbuka.
  4. Profil LSM dilepas — cangkang kelima pada diagram Chapter Prinsip Keamanan Container ikut menyingkir; operasi sensitif tidak lagi diperiksa profil AppArmor atau SELinux.

Digabungkan dengan rumus chapter pembuka: root di dalam container --privileged berarti kekuatan penuh root di host tanpa capability yang dicabut — hanya pandangan namespace masih tersisa, dan itupun tidak lagi menyembunyikan device host.

Membuktikannya dengan mount #

mount adalah syscall yang dijaga SYS_ADMIN — capability yang tidak pernah dibawa container bawaan. Kita coba memasang tmpfs pada container biasa:

bash
$ docker run --rm ubuntu:24.04 mount -t tmpfs none /mnt

Output yang Dihasilkan:

text
mount: /mnt: permission denied.
       dmesg(1) may have more details after failed mount(8) system call.

Kernel menolak mount dengan galat izin — persis perilaku yang kita ramalkan dari tabel capability. Sekarang perintah yang sama pada container --privileged:

bash
$ docker run --rm --privileged ubuntu:24.04 sh -c "mount -t tmpfs none /mnt && touch /mnt/berhasil && echo mount-berhasil"

Output yang Dihasilkan:

text
mount-berhasil

Satu bendera, dan syscall yang tadinya ditolak kini dikerjakan kernel tanpa komplain. SYS_ADMIN yang diberikan --privileged membukakan tidak hanya mount tmpfs — yang sama berlaku untuk me-mount filesystem host, membentuk namespace baru, dan puluhan operasi administratif kernel lainnya.

Bukti Bahaya: Disk Host yang Tiba-tiba Terlihat #

Baris kedua tabel di atas terasa abstrak sampai kita melihatnya langsung. Berikut isi /dev container ubuntu biasa:

bash
$ docker run --rm ubuntu:24.04 ls /dev

Output yang Dihasilkan:

text
core  fd  full  mqueue  mqueue-priority  null  ptmx  random  shm  stderr  stdin  stdout  tty  urandom  zero

Belasan node yang seluruhnya buatan Docker — aman, tanpa satu pun device fisik host. Pada container --privileged, daftar itu meledak menjadi seluruh device host:

bash
$ docker run --rm --privileged ubuntu:24.04 ls /dev | wc -l

Output yang Dihasilkan:

text
168
bash
$ docker run --rm --privileged ubuntu:24.04 ls /dev | grep -E "^sd|^nvme"

Output yang Dihasilkan:

text
nvme0n1
nvme0n1p1
nvme0n1p2
nvme0n1p3
sda
sda1
sda2

Mari kita telaah apa yang baru saja terlihat:

  1. nvme0n1 adalah SSD utama host Budi — partisi nvme0n1p1 sampai nvme0n1p3 adalah pembagian disk itu, termasuk partisi sistem tempat kernel host dan seluruh data berada.
  2. sda adalah disk data yang terpasang di host — container biasa tidak pernah tahu keberadaannya; container --privileged bisa membuka dan menulisnya seperti proses root di host.
  3. Satu perintah dd yang menulis ke /dev/sda berarti host terformat — tidak ada lapisan Docker yang menghalangi; device node yang terlihat adalah device sungguhan, dan hak menulisnya kini dimiliki proses di dalam container.

Kenapa –privileged Tetap Ada #

Bendera ini tidak diciptakan untuk menghabisi keamanan — ada tiga kebutuhan sah yang memang menuntutnya. Yang pertama dan paling klasik: Docker-in-Docker sungguhan — image docker:dind menjalankan daemon Docker kedua di dalam container, dan daemon itu perlu membuat namespace, mount, dan cgroups sendiri; pekerjaan yang menuntut SYS_ADMIN dan kawan-kawannya. Yang kedua: akses perangkat keras yang butuh lebih dari satu device dan kontrol penuh atasnya — lab kernel, pengujian modul, atau fuzzing syscall pada kernel yang berjalan. Yang ketiga: sebagian tool diagnosa tingkat rendah yang memang membaca keadaan kernel host secara langsung.

Masalahnya bukan keberadaan benderanya, melainkan pemakaiannya: sembilan dari sepuluh --privileged yang kita temui di tutorial internet menyelesaikan masalah yang sebenarnya hanya butuh satu capability, satu device, atau satu pola arsitektur yang berbeda.

Lima Alternatif yang Lebih Aman #

Kebutuhan klasikAlternatif lebih amanPembahasan
“Error permission, butuh hak lebih”--cap-add satu capability spesifikChapter Linux Capabilities
Bicara dengan device hardware--device /dev/ttyUSB0demo di bawah
Menjalankan Docker untuk CI di containerpola sibling: pasang docker.sock + socket-proxydemo di bawah
Daemon Docker tanpa root sama sekalirootless modeChapter Docker Socket Security
Beban tak dipercaya yang butuh isolasi mendekati VMSysboxdisebut nama

Demo --device menunjukkan betapa sempitnya pintu yang sebenarnya dibutuhkan. Container berikut diminta bicara dengan satu device serial fisik — dan hanya device itu:

bash
$ docker run --rm --device /dev/ttyUSB0 ubuntu:24.04 ls -l /dev/ttyUSB0

Output yang Dihasilkan:

text
crw-rw---- 1 root dialout 188, 0 Oct 13 11:07 /dev/ttyUSB0

Mari kita telaah sempitnya izin tersebut:

  1. Hanya /dev/ttyUSB0 yang dibukakan — cgroup device controller memberi container hak membuka device itu dan tidak ada yang lain; ls /dev tetap menampilkan daftar minimal seperti container biasa.
  2. Device node muncul dengan identitas aslinya — major/minor number 188,0 dan grup dialout sama persis seperti di host, sehingga aplikasi di dalam container bisa langsung membuka dan membaca-menulis port serial.
  3. Proses non-root tinggal menambah grup — flag --group-add 20 memasukkan proses ke grup dialout bernomor 20 tanpa memberikan capability apa pun; akses device diatur izin berkas biasa, bukan privilege kernel.

Kontras dengan --privileged terang benderang: satu pintu kecil yang dibuka dengan kunci pas, dibanding empat tembok yang diratakan sekaligus.

Docker-in-Docker versus Pola Sibling #

Kebutuhan paling sering dikutip sebagai pembenaran --privileged adalah “saya butuh Docker di dalam Docker” — biasanya untuk menjalankan build bercontainer di dalam CI. Dua rancangan sangat berbeda berbagi kebutuhan itu. Docker-in-Docker (DinD) sungguhan menjalankan daemon kedua di dalam container dan memang menuntut --privileged. Pola sibling justru tidak menjalankan daemon sama sekali: container hanya membawa klien CLI, dan berbicara ke daemon host lewat docker.sock yang dipasang sebagai bind mount. Kita buktikan bahwa daemon yang menjawab memang daemon host:

bash
$ docker run --rm --name cli-saudara \
    -v /var/run/docker.sock:/var/run/docker.sock \
    docker:29-cli docker ps

Output yang Dihasilkan:

text
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS         PORTS     NAMES
4b7e2a91c8d3   docker:29-cli  "docker ps"   5 seconds ago   Up 3 seconds              cli-saudara

Mari kita telaah bukti di dalam output tersebut:

  1. docker ps menampilkan container CLI itu sendiri — container cli-saudara sedang berjalan sebagai container di daemon host, dan klien di dalamnya melihat dirinya dari luar; kalau ini DinD sungguhan, daftar justru kosong karena daemon di dalamnya baru menyala tanpa container.
  2. Container CLI adalah saudara, bukan induk — container yang nanti diluncurkan lewat CLI ini menjadi container host juga, sejajar dengan siapa pun yang menjalankan docker run di mesin itu.
  3. Socket adalah kunci kerajaan — siapa pun yang bisa membaca /var/run/docker.sock bisa menjalankan container baru dengan --privileged, mem-mount root filesystem host, dan menarik serta menjalankan image dari registry mana pun; socket itu setara root host. Pola pengamanan socket-proxy yang menyaring perintah yang lewat adalah materi penutup section ini, Chapter Docker Socket Security.

Verifikasi #

Status privileged sebuah container terbaca dari docker inspect — berguna untuk mengaudit container yang dibuat orang lain atau oleh template lama:

bash
$ docker run -d --name contoh-priv --privileged nginx:1.29

Output yang Dihasilkan:

text
f1a9c3e7b2d64f8a0c5e9b3d7f1a4c6e8b0d2f5a7c9e1b3d5f7a0c4e8b2d6f9a
bash
$ docker inspect --format '{{.HostConfig.Privileged}}' contoh-priv

Output yang Dihasilkan:

text
true

Container uji itu langsung kita buang — docker rm -f contoh-priv — karena tidak ada alasan membiarkannya menyala lebih lama dari yang diperlukan demo.

Empat pembatas kini kita kenal sisi longgarnya; chapter-chapter berikutnya menegakkan sisi sebaliknya satu per satu. Yang pertama paling murah sekaligus paling mengejutkan dayanya: membekukan seluruh filesystem container sehingga proses mana pun — root sekalipun — tidak bisa menulis satu byte pun ke disk. Chapter Read-Only Filesystem membahas flag --read-only, pola tmpfs sebagai satu-satunya lubang tulis yang diberikan dengan sengaja, dan kombinasi emas yang menggabungkan seluruh pelajaran section ini.

Referensi #