Variabel lingkungan di container Docker terlihat dalam teks biasa melalui docker inspect — siapa pun yang memiliki akses ke Docker daemon atau runtime container dapat membaca setiap variabel lingkungan, termasuk kata sandi database, kunci API, dan JWT secret. Studi GitGuardian 2023 menemukan bahwa lebih dari 10 juta secret bocor di GitHub dalam tahun itu saja. Di Commsult Indonesia, setelah mengaudit konfigurasi Docker awal kami dan menemukan kredensial teks biasa dalam output docker inspect dan log CI/CD, kami merombak total manajemen secret kami.
Meneruskan secret sebagai variabel lingkungan memiliki beberapa vektor eksposur: docker inspect menampilkan semua variabel lingkungan dalam teks biasa, variabel lingkungan terlihat di /proc/1/environ di dalam container, perintah docker run dengan secret muncul di riwayat shell, dan log pipeline CI/CD dapat menangkap nilai variabel lingkungan pada perintah echo atau debug.
Docker Swarm memiliki mekanisme secret bawaan: docker secret create menyimpan secret yang dienkripsi saat istirahat di log Swarm Raft, hanya dapat diakses oleh layanan yang secara eksplisit memintanya. Secret dipasang sebagai file di /run/secrets/ di dalam container menggunakan filesystem berbasis memori (tmpfs) yang menghilang saat container berhenti.
Docker Compose juga mendukung secret melalui kunci secrets di docker-compose.yml. Anda mendefinisikan sumber secret (file atau secret Swarm eksternal) dan memasangnya ke layanan tertentu. Untuk pengembangan, gunakan secret berbasis file (file lokal yang tidak ada di git). Untuk produksi, gunakan kata kunci external untuk mereferensikan secret Swarm.
┌─────────────────────────────────────────────────────┐
│ SECRETS EXPOSURE COMPARISON │
└─────────────────────────────────────────────────────┘
ENV VAR approach (INSECURE):
docker run -e DB_PASS=secret123 ...
docker inspect container → plaintext visible
/proc/1/environ → plaintext visible
CI/CD debug logs → plaintext leaks
Docker Secrets approach (SECURE):
docker secret create db_pass /run/secrets/db_pass
Encrypted in Swarm Raft log
Mounted as tmpfs: /run/secrets/db_pass
docker inspect → NOT visible
Container reads: fs.readFileSync('/run/secrets/db_pass')
SOPS + age (GitOps-friendly):
secrets.enc.yaml ──(age decrypt)──► .env (CI only)
Encrypted in git Never written to diskDari pengalaman mengamankan deployment NestJS produksi di Commsult Indonesia, pendekatan paling pragmatis untuk setup Docker Compose tanpa Swarm adalah Mozilla SOPS dengan enkripsi age. Simpan file secret terenkripsi di repositori git Anda (aman karena terenkripsi), dekripsi ke file .env pada waktu deployment di pipeline CI/CD Anda menggunakan kunci deploy yang disimpan di GitHub Actions secrets atau variabel GitLab CI.
Pola secret yang paling berlaku secara universal adalah berbasis file: simpan secret sebagai file dengan izin restriktif (600, dimiliki oleh root atau pengguna service), dan minta aplikasi membaca file secret alih-alih variabel lingkungan. Sebagian besar pustaka klien mendukung ini: Node.js dapat membaca file dengan fs.readFileSync. Simpan file secret di /run/secrets/ (tmpfs) atau /etc/myapp/secrets/.
Secret waktu build (token registry npm, kredensial paket privat) memerlukan penanganan khusus. Jangan pernah gunakan ARG atau ENV di Dockerfile untuk secret — mereka dipanggang ke lapisan image dan terlihat di docker history. Gunakan mount secret BuildKit Docker: RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm install.
# docker-compose.yml with secrets
services:
app:
image: myapp:latest
secrets:
- db_password
- jwt_secret
environment:
NODE_ENV: production
DB_HOST: db
secrets:
db_password:
file: ./secrets/db_password.txt # dev
# external: true # prod (Swarm)
jwt_secret:
file: ./secrets/jwt_secret.txt
# In Node.js, read secret from file
const dbPass = require('fs').readFileSync('/run/secrets/db_password', 'utf8').trim()
# Dockerfile BuildKit secret (build-time)
# RUN --mount=type=secret,id=npmrc,target=/root/.npmrc # npm install
# Audit current exposure
docker inspect $(docker ps -q) | grep -i -E "password|secret|key|token"
# Scan git history for leaked secrets
trufflehog git file://. --only-verifiedUntuk tim yang lebih besar atau lingkungan yang sensitif terhadap kepatuhan, gunakan manajer secret khusus: HashiCorp Vault (self-hosted, open source, kaya fitur), AWS Secrets Manager (terkelola, terintegrasi dengan ECS dan EKS), GCP Secret Manager (terkelola, terintegrasi dengan Cloud Run dan GKE), atau Infisical (open source, alternatif Vault dengan UX yang lebih baik).
Saya telah melihat tim menggunakan docker-compose.override.yml untuk secret lokal, git-ignore-nya, lalu secara tidak sengaja melakukan commit ketika entri gitignore dihapus atau file diganti nama. Bahkan satu file secret yang di-commit bisa menjadi kebocoran permanen — riwayat git menyimpannya bahkan setelah penghapusan. Gunakan file secret terpisah yang di-gitignore secara eksplisit, dokumentasikan variabel yang diperlukan dalam file .env.example dengan nilai placeholder.
Di Commsult Indonesia, stack secret produksi kami: SOPS dengan enkripsi age untuk mengenkripsi secret di git (kunci disimpan di 1Password dan secret platform CI/CD), Docker Compose secrets untuk injeksi secret runtime (file secret dipasang di /run/secrets/), dan file .env terpisah per lingkungan (dihasilkan oleh dekripsi SOPS di CI/CD, tidak pernah di-commit).
Sebelum membangun solusi secret yang lebih baik, audit apa yang sudah terekspos. Jalankan docker inspect terhadap container yang berjalan dan grep untuk pola password, secret, key, dan token untuk memeriksa secret di variabel lingkungan. Tinjau file docker-compose.yml Anda untuk secret yang di-hardcode. Periksa riwayat git Anda untuk file .env yang tidak sengaja di-commit.