Menjawab dua pertanyaan tentang image: apa yang ada di dalamnya dan apakah bisa dipercaya. Memindai kerentanan dengan Docker Scout, Trivy, dan Grype, memahami SBOM, menandatangani image dengan Docker Content Trust serta Cosign/Sigstore, dan menyusun strategi remediasi yang benar.

Setelah di episode 13 sebelumnya kita mengeraskan kontainer — non-root, read-only, capabilities terbatas, resource limits, seccomp, bahkan Rootless Docker — pada episode kali ini kita menghadapi pertanyaan yang berbeda tapi sama menentukan: apa yang sebenarnya ada di dalam image yang kita jalankan, dan apakah image itu bisa kita percaya? Hardening membatasi kerusakan jika sesuatu terjadi; scanning dan integrity menjawab apa yang sedang terjadi dan siapa yang membuatnya. Keduanya bekerja berdampingan.
Coba bayangkan arsitektur image kalian: hampir selalu dibangun di atas base image publik — node:20-alpine, postgres:16-alpine, dan seterusnya — yang berubah tanpa kalian sadari. Tag latest bisa menunjuk ke konten berbeda hari ini dan besok, dan setiap layer yang kalian tarik adalah kode yang kalian tidak tulis. Pertanyaannya bukan apakah ada kerentanan di ekosistem itu, melainkan apakah kalian mengetahuinya. Inilah esensi supply chain security: keamanan tidak berakhir di kode yang kalian tulis — ia mencakup setiap lapisan yang kalian warisi.
Di episode ini kita akan membangun dua kemampuan: mengetahui isi image (scanning kerentanan dengan Docker Scout, Trivy, dan Grype, plus SBOM sebagai inventori) dan memastikan keasliannya (penandatanganan dengan Docker Content Trust dan Cosign/Sigstore, termasuk keyless signing). Kita tutup dengan strategi remediasi yang benar dan pitfalls yang paling sering membuat scanning menjadi rutinitas tanpa makna.
Ada dua hal berbeda yang sering disatukan di bawah istilah "security image":
Scanning tanpa integrity berarti kalian mungkin memindai image yang sudah diutak-atik oleh penyerang (misal lewat tag yang disusupi). Integrity tanpa scanning berarti image kalian sah tapi bisa jadi membawa kerentanan yang diketahui. Keduanya wajib, dan masing-masing menjawab pertanyaan yang tidak bisa dijawab yang lain.
Docker Scout adalah layanan scanning yang terintegrasi ke dalam CLI Docker. Setelah login ke Docker Hub, kalian bisa langsung menganalisis image tanpa menginstall alat tambahan:
docker scout quickview ghcr.io/arman-dwi-pangestu/my-app:1.0.0
docker scout cve ghcr.io/arman-dwi-pangestu/my-app:1.0.0
docker scout recommendations ghcr.io/arman-dwi-pangestu/my-app:1.0.0docker scout quickview — ringkasan: berapa kerentanan per severity, base image terbaik untuk diganti, dan skor keseluruhan.docker scout cve — daftar kerentanan lengkap: paket, versi, CVSS, dan jalur perbaikan.docker scout recommendations — rekomendasi base image yang lebih aman dan perubahan yang disarankan.Kekuatan Scout adalah konteks kebijakan: ia bisa memfilter kerentanan berdasarkan apakah paket tersebut benar-benar dieksekusi saat runtime (bukan sekadar ter-list di image). Ini mengurangi "kebisingan" — kerentanan di paket yang tidak pernah dijalankan tidak perlu segera diperbaiki. Kelemahannya: sebagian fitur lanjutan (kebijakan, integrasi tim) terikat akun Docker Hub / berbayar, dan ekosistemnya paling natural bila image kalian ada di Docker Hub.
Trivy (dari Aqua Security) adalah scanner sumber terbuka yang menjadi standar de facto di pipeline CI. Ia cepat, mendeteksi kerentanan di paket OS dan dependency bahasa pemrograman (npm, pip, go, gem, dan banyak lagi), serta menghasilkan false positive yang rendah. Trivy tidak hanya memindai image:
trivy image my-app:1.0
trivy image --severity CRITICAL,HIGH my-app:1.0
trivy fs --scanners vuln,secret,config ./src
trivy repo https://github.com/org/repotrivy image — pindai image container.trivy fs — pindai filesystem: kerentanan dependency, secret yang tertinggal, dan salah konfigurasi (IaC/containers).trivy repo — pindai seluruh repository Git, termasuk history-nya.Yang membuat Trivy ideal untuk CI adalah exit code yang deterministik:
trivy image --severity CRITICAL \
--exit-code 1 \
--ignore-unfixed \
ghcr.io/arman-dwi-pangestu/my-app:1.0.0Dengan --exit-code 1, build gagal begitu ditemukan CVE CRITICAL (yang belum ada fix-nya). Tambahkan --ignore-unfixed agar tidak menggagalkan pipeline karena kerentanan yang belum punya perbaikan — menggagalkan tanpa solusi hanya membuat tim mematikan scan-nya. Output bisa diekspor ke SARIF untuk GitHub Code Scanning atau ke HTML/Slack untuk laporan tim.
Grype (Anchore) adalah scanner dengan pendekatan berbeda: ia menggunakan Syft untuk membuat katalog komponen, lalu mencocokkannya dengan database kerentanan. Syft sendiri adalah generator SBOM — alat yang "membaca" image dan mendaftar setiap komponen di dalamnya.
grype my-app:1.0
syft my-app:1.0 -o spdx-json
syft ghcr.io/arman-dwi-pangestu/my-app:1.0.0 -o cyclonedx-jsonKenapa perlu dua alat? Karena keduanya bisa dipakai terpisah dengan cara yang saling menguatkan: Syft menghasilkan SBOM (yang bisa diarsipkan dan di-audit), dan Grype memindai hasil katalog itu. Alur yang umum di tim yang serius: pull image → syft → simpan SBOM → grype → kirim hasil ke dashboard → tanda tangani image yang lolos.
SBOM (Software Bill of Materials) adalah daftar lengkap seluruh komponen perangkat lunak di dalam image: paket OS, dependency aplikasi, beserta versinya. Ibaratnya, SBOM adalah daftar isi bahan di kemasan makanan — kalian bisa tahu apa yang kalian konsumsi dan dari mana asalnya. Manfaatnya praktis, bukan sekadar kepatuhan:
Format yang umum: SPDX dan CycloneDX (keduanya standar terbuka). Syft menghasilkan keduanya. Praktik yang disarankan: simpan SBOM setiap kali kalian merilis image — di samping image itu sendiri atau di artifact CI — sehingga audit setahun kemudian tidak butuh "membongkar" image lama.
Docker Content Trust adalah mekanisme penandatanganan bawaan Docker berbasis Notary + TUF (The Update Framework). Cara kerjanya: ketika diaktifkan, docker push dan docker pull menandatangani/memverifikasi image dengan kunci yang dipegang publisher. Aktivasinya lewat environment variable:
export DOCKER_CONTENT_TRUST=1
docker push ghcr.io/arman-dwi-pangestu/my-app:1.0.0
docker pull ghcr.io/arman-dwi-pangestu/my-app:1.0.0Kunci: export DOCKER_CONTENT_TRUST=1 (bisa juga per perintah dengan DOCKER_CONTENT_TRUST=1 docker push ...). Saat aktif, pull menolak image yang tidak bertanda tangan, dan push meminta kunci yang sudah dibuat saat pertama kali.
Warning
DCT punya tiga keterbatasan yang harus kalian tahu sebelum mengandalkannya: (1) default-nya off — satu mesin yang lupa export berarti image ter-push tanpa tanda tangan; (2) kunci privat yang hilang = tidak bisa push versi baru lagi, jadi manajemen kuncinya harus serius; (3) verifikasi hanya terjadi bila klien juga mengaktifkan DCT. Karena keterbatasan ini, ekosistem bergerak ke pendekatan yang lebih modern dan ketat: Cosign/Sigstore.
Cosign (dari proyek Sigstore) adalah alat penandatanganan kontainer yang mengatasi kelemahan DCT. Dua perbedaan kunci: (1) signature disimpan di registry OCI — menempel pada image, bukan di server terpisah; (2) dukungan keyless signing yang menghilangkan masalah manajemen kunci jangka panjang. Alurnya klasik berbasis kunci:
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/arman-dwi-pangestu/my-app:1.0.0
cosign verify --key cosign.pub ghcr.io/arman-dwi-pangestu/my-app:1.0.0cosign generate-key-pair — membuat pasangan kunci (simpan kunci privat di secret store, jangan di repo).cosign sign — menandatangani image; signature otomatis di-push ke registry (sebagai artifact dengan nama image + .sig).cosign verify — memverifikasi signature dengan kunci publik.Signature yang tersimpan di registry adalah nilai jual terbesar Cosign: kalian tidak perlu infrastruktur tambahan — registry yang sudah ada cukup, dan verifikasi bisa dilakukan oleh siapa pun yang memegang kunci publik.
Inovasi Sigstore yang paling menarik adalah keyless signing — menandatangani tanpa menyimpan kunci privat jangka panjang sama sekali. Mekanismenya:
GITHUB_TOKEN).cosign sign ghcr.io/arman-dwi-pangestu/my-app:1.0.0
cosign verify ghcr.io/arman-dwi-pangestu/my-app:1.0.0 \
--certificate-identity email@contoh.com \
--certificate-oidc-issuer https://github.com/login/oauthPerhatikan verify di atas: verifikasi tidak cukup sekadar "tandatangan valid" — kalian harus memeriksa siapa pemegang identitas itu. Itulah gunanya --certificate-identity dan --certificate-oidc-issuer: memastikan image ditandatangani oleh orang/organisasi yang memang kalian harapkan, bukan sembarang identitas yang kebetulan valid. Di CI GitHub Actions, identitas default-nya adalah workflow itu sendiri (GITHUB_TOKEN) — sehingga signature bisa dibuktikan "ditandatangani oleh pipeline rilis kami".
Menemukan kerentanan hanya berguna jika kalian tahu urutan cara menangani. Aturan perbaikan dari yang paling sering efektif:
node:20-alpine lama ≠ node:20-alpine baru. Pembaruan base + rebuild sering menghapus puluhan CVE sekaligus.latest, agar perbaikan bisa dikendalikan — kalian tahu persis base apa yang sedang dipakai dan kapan naik versi.Scan sekali, lalu lupa. Image baru dirilis tiap minggu — scan yang tidak berjalan di CI adalah scan untuk hiasan. Automate di pipeline (nanti kita integrasikan penuh di episode 19).
Scan tag latest, bukan konten sebenarnya. latest bergerak; yang kalian scan sore ini bukan yang dipakai produksi. Scan image yang spesifik (versi atau digest) yang benar-benar akan di-deploy.
Hanya memindai paket OS. Kerentanan di package.json, requirements.txt, dan go.mod sama nyatanya. Gunakan scanner yang menjangkau dependency bahasa (Trivy dan Grype sama-sama bisa).
Menggagalkan CI pada CVE tanpa fix. --ignore-unfixed penting: menggagalkan build karena kerentanan yang belum ada perbaikannya hanya membuat tim mematikan scanner. Blokir yang bisa diperbaiki, pantau yang belum.
Mengira DCT sudah cukup. DCT default-nya off dan tidak memaksa klien memverifikasi. Kalau keamanan supply chain adalah prioritas, Cosign keyless + transparency log adalah arah yang lebih kuat.
Kunci privat Cosign di repo. Sama seperti secret lain: kunci ke repo, tanda tangan jadi tidak berarti. Simpan kunci privat di secret store CI, bagikan hanya kunci publik.
Verifikasi Cosign tanpa cek identitas. cosign verify yang hanya mengecek "tanda tangan valid" bisa dilewati oleh siapa pun yang punya kunci valid. Selalu sertakan --certificate-identity dan --certificate-oidc-issuer.
Pada episode 14 ini kita telah menjawab dua pertanyaan tentang image: apa isinya dan apakah bisa dipercaya. Kita mengenal Docker Scout (scanning bawaan CLI dengan konteks runtime), Trivy (scanner OSS standar CI dengan exit code deterministik dan cakupan OS + bahasa), Grype + Syft (duo scanner dan generator SBOM), memahami SBOM sebagai inventori software yang wajib diarsipkan tiap rilis, membedah Docker Content Trust beserta keterbatasannya, beralih ke penandatanganan modern dengan Cosign/Sigstore termasuk keyless signing berbasis OIDC + Fulcio + Rekor, dan menyusun strategi remediasi yang benar: perbarui base, pin digest, minimal image, dan automasi di CI.
Inti yang harus kalian bawa:
Sekarang image kalian bersih, terdokumentasi, dan dapat dipercaya. Tapi perhatikan: semua yang kita lakukan — hardening, scanning, signing — terjadi sebelum image dijalankan. Begitu kontainer berjalan, pertanyaan baru muncul: apakah aplikasinya sehat? apakah node-nya kehabisan disk? apa yang terjadi lima menit sebelum crash? Jawabannya butuh lapisan yang berbeda: observability. Di episode 15 selanjutnya kita akan membangun Container Observability & Monitoring — logging driver dan log rotation yang mencegah disk penuh, HEALTHCHECK untuk deteksi dini, metrics endpoint daemon, hingga stack monitoring cAdvisor + Prometheus + Grafana. Sampai jumpa di episode 15!