Docker Security Best Practices

Docker memudahkan deployment aplikasi, tetapi juga membawa risiko keamanan jika tidak dikonfigurasi dengan benar. Container yang tidak diamankan bisa dieksploitasi untuk mengakses host, mencuri data, atau menjadi bagian dari serangan DDoS. Artikel ini membahas praktik keamanan Docker terbaik.

Pengertian Docker Security

Docker Security adalah kumpulan praktik dan konfigurasi untuk melindungi container, image, daemon, dan host Docker dari ancaman keamanan. Keamanan Docker mencakup isolasi container, minimisasi permukaan serangan, dan kontrol akses.

Mengapa Docker Security Penting?

  • Container Escape: Attacker keluar dari container ke host
  • Privilege Escalation: Akses root di container berujung ke root di host
  • Data Theft: Data sensitif bocor dari container
  • Supply Chain Attack: Image yang terinfeksi malware
  • Resource Abuse: Container membebani host atau jaringan
  • Lateral Movement: Container yang diretas digunakan untuk attack lain

Alat yang Dibutuhkan

  • Server Debian 13 dengan Docker terinstal
  • Akses root atau sudo
  • Image Docker yang sudah tersedia
  • Tidak perlu software tambahan (Docker sudah memiliki fitur keamanan bawaan)

Prinsip Dasar Keamanan Docker

1. Prinsip Hak Akses Minimal (Least Privilege)

Jalankan container dengan hak akses sekecil mungkin:

bash
# JANGAN jalankan sebagai root
docker run -u 1000:1000 myapp

# Gunakan user non-root di Dockerfile
FROM node:20
USER node
WORKDIR /app
COPY . .
CMD ["node", "server.js"]

2. Minimisasi Image

Gunakan image seminimal mungkin:

dockerfile
# BURUK: Image besar penuh dependensi
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nodejs npm

# BAIK: Image minimal
FROM node:20-alpine
WORKDIR /app
COPY . .

3. Jangan Simpan Secret di Image

dockerfile
# BURUK: Secret di image
ENV DATABASE_PASSWORD=secret123

# BAIK: Secret dari environment
docker run -e DATABASE_PASSWORD=secret123 myapp

# LEBIH BAIK: Gunakan Docker Secrets
docker secret create db_password ./password.txt
docker service create --secret db_password myapp

Konfigurasi Daemon Docker yang Aman

1. Edit Konfigurasi Docker Daemon

bash
nano /etc/docker/daemon.json
json
{
  "icc": false,
  "userns-remap": "default",
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",
  "live-restore": true,
  "userland-proxy": false,
  "seccomp-profile": "/etc/docker/seccomp-profile.json",
  "default-cgroupns-mode": "host"
}

Penjelasan:

  • "icc": false: Nonaktifkan inter-container communication tanpa user approval
  • `"userns-remap": "default"": Remap user namespace untuk isolasi
  • `"no-new-privileges": true": Mencegah privilege escalation
  • "live-restore": true: Daemon tetap jalan jika container crash
  • "log-driver": "json-file": Log terbatas untuk mencegah disk penuh

2. Restart Docker

bash
systemctl restart docker

3. Verifikasi Konfigurasi

bash
# Cek konfigurasi aktif
docker info
# Cek daemon logs
journalctl -u docker | tail -20

Keamanan Image Docker

1. Gunakan Image Official dan Minimal

dockerfile
# Image official dari Docker Hub
FROM alpine:3.18
FROM node:20-alpine
FROM python:3.12-slim

# Hindari image yang tidak resmi
# JANGAN gunakan: FROM unknown-registry/myapp:latest

2. Scan Image untuk Kerentanan

Instal Docker Scout atau Trivy:

bash
# Trivy (rekomendasi)
apt install trivy -y

# Scan image
trivy image myapp:latest

# Scan untuk critical vulnerabilities
trivy image --severity CRITICAL myapp:latest

# Format report
trivy image --format json myapp:latest > scan-report.json

3. Gunakan Image dengan Tag Spesifik

dockerfile
# BURUK: Tag latest bisa berubah kapan saja
FROM node:latest

# BAIK: Tag spesifik untuk reproducibility
FROM node:20.11.0-alpine

4. Kurangi Jumlah Layer

dockerfile
# BURUK: Banyak layer
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN apt-get clean

# BAIK: Satu layer
RUN apt-get update && apt-get install -y curl git && apt-get clean && rm -rf /var/lib/apt/lists/*

Runtime Security

1. Jalankan Container dengan User Non-Root

bash
# Dockerfile
FROM node:20-alpine
RUN adduser -D appuser
USER appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
CMD ["node", "server.js"]
bash
# Atau override runtime
docker run -u 1000:1000 myapp

2. Read-Only Filesystem

bash
# Filesystem hanya bisa dibaca
docker run --read-only myapp

# Tambahkan tmpfs untuk data sementara
docker run --read-only --tmpfs /tmp --tmpfs /app/uploads myapp

3. Batasi Resource

bash
# Batasi memory
docker run --memory=512m myapp

# Batasi CPU
docker run --cpus=1.5 myapp

# Batasi CPU shares
docker run --cpu-shares=512 myapp

# Batasi PIDs
docker run --pids-limit=100 myapp

# Batasi blkio
docker run --blkio-weight=100 myapp

4. Nonaktifkan Capability yang Tidak Diperlukan

bash
# Hapus semua capability, tambahkan yang diperlukan
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

# Capability berbahaya yang harus di-drop
docker run --cap-drop=SYS_ADMIN --cap-drop=SYS_PTRACE --cap-drop=SYS_MODULE myapp

5. Nonaktifkan Privilege Mode

bash
# JANGAN gunakan --privileged kecuali benar-benar diperlukan
# Gunakan opsi spesifik sebagai gantinya
docker run --device=/dev/snd myapp

6. Network Isolation

bash
# Buat network khusus
docker network create --driver bridge myapp-network

# Jalankan container di network khusus
docker run --network=myapp-network myapp

# Network isolasi total (tanpa akses internet)
docker run --network=none myapp

7. Mount Volume dengan Opsi Aman

bash
# BURUK: Mount tanpa opsi
docker run -v /host/data:/data myapp

# BAIK: Mount read-only dan specific
docker run -v /host/data:/data:ro myapp

# BAIK: Mount dengan opsi
docker run -v /host/data:/data:ro,noexec,nosuid,nodev myapp

# Bind mount dengan permissions
docker run -v /host/data:/data:rw,uid=1000,gid=1000 myapp

8. Gunakan Docker Secrets

bash
# Buat secret
echo "supersecret" | docker secret create db_password -

# Gunakan di service
docker service create --secret db_password myapp

# Akses secret di dalam container
# Secret tersedia di /run/secrets/db_password

9. Gunakan Docker Configs

bash
# Buat config
docker config create app-config ./config.yaml

# Gunakan di service
docker service create --config app-config myapp

# Config tersedia di /run/config/app-config

Keamanan Jaringan

1. Firewall untuk Container

bash
# Buat network terisolasi
docker network create --internal myapp-backend

# Container di network internal tidak bisa akses internet
docker run --network=myapp-backend myapp

# Container bisa akses internet (dengan NAT)
docker run --network=bridge myapp

2. DNS Security

json
// /etc/docker/daemon.json
{
  "dns": ["8.8.8.8", "1.1.1.1"],
  "dns-opts": ["use-vc"],
  "dns-search": ["example.com"]
}

3. Port Exposure Minimal

bash
# BURUK: Ekspos semua port
docker run -P myapp

# BAIK: Hanya port yang diperlukan
docker run -p 80:8080 myapp

# PUBLIK: Hanya port tertentu ke host
docker run -p 127.0.0.1:8080:8080 myapp

Image Security Scanning dan CI/CD

1. Integrasi CI/CD

yaml
# GitHub Actions untuk security scanning
name: Security Scan

on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build Docker Image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Scan with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
      
      - name: Upload Results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

2. Docker Scout

bash
# Install Docker Scout
curl -L https://github.com/docker/scout/releases/latest/download/scout_linux_amd64.tar.gz | tar xz
mv scout /usr/local/bin/

# Scan image
docker scout cves myapp:latest
docker scout quickview myapp:latest
docker scout recommendations myapp:latest

Monitoring dan Logging

1. Aktifkan Docker Audit Log

bash
# Edit daemon.json
nano /etc/docker/daemon.json
json
{
  "audit": true,
  "audit-file": "/var/log/docker/audit.log"
}

2. Monitor Container dengan Auditd

bash
# Instal auditd
apt install auditd -y

# Tambahkan rule untuk Docker
echo "-w /var/run/docker.sock -p wa -k docker" >> /etc/audit/rules.d/docker.rules
systemctl restart auditd

# Cek log audit
ausearch -k docker

3. Cek Container yang Tidak Normal

bash
# Cek container yang berjalan lama
docker ps --format "table {{.Names}}\t{{.RunningFor}}\t{{.Image}}"

# Cek container yang banyak makan resource
docker stats --no-stream

# Cek image yang tidak dikenal
docker images --filter "dangling=true"

4. Log Container

bash
# Lihat log container
docker logs myapp
docker logs --tail 100 myapp
docker logs -f myapp

# Cek log Docker daemon
journalctl -u docker.service

Backup dan Recovery Docker

1. Backup Docker Configuration

bash
# Backup daemon.json
cp /etc/docker/daemon.json /backup/daemon.json.bak

# Backup certificates
cp -r /etc/docker/certs.d /backup/certs.d.bak

# Backup container configs
docker config ls
docker secret ls

2. Backup Data Container

bash
# Backup volume
docker run --rm -v mydata:/data -v /backup:/backup alpine tar czf /backup/data.tar.gz /data

# Backup image
docker save myapp:latest | gzip > myapp_latest.tar.gz

3. Restore

bash
# Restore volume
docker run --rm -v /backup:/backup -v mydata:/data alpine tar xzf /backup/data.tar.gz -C /

# Restore image
docker load < myapp_latest.tar.gz

Docker Bench Security

Docker Bench Security adalah skrip audit keamanan otomatis:

bash
# Clone dan jalankan
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
./docker-bench-security.sh

Hasilnya menampilkan checklist keamanan dan rekomendasi perbaikan.

Hardening OS Host

1. Hapus Docker Socket dari User Lain

bash
# Jangan tambahkan user ke group docker tanpa alasan
sudo groupmod docker
sudo gpasswd -d user docker

2. Secure Mount Points

bash
# Nonaktifkan mounting ke container yang tidak perlu
mount -o remount,nodev,nosuid,noexec /var/lib/docker

3. Update Docker Rutin

bash
apt update
apt upgrade docker-ce docker-ce-cli containerd.io
systemctl restart docker

Troubleshooting

1. Container Tidak Bisa Start karena Security Options

Penyebab: Opsi keamanan terlalu ketat. Solusi:

bash
# Cek error
docker logs myapp
# Coba tanpa opsi security
docker run --rm --security-opt seccomp=unconfined myapp

2. Container Crash karena Capability Dropped

Penyebab: Capability yang diperlukan di-drop. Solusi:

bash
# Cek capability yang dibutuhkan
docker run --cap-add=ALL myapp  # Test
# Lalu drop capability satu per satu

3. Permission Denied pada Mount

Penyebab: User mismatch atau permissions salah. Solusi:

bash
# Cek user di dalam container
docker exec myapp id
# Fix permissions
chown 1000:1000 /host/data
# Mount dengan uid/gid
docker run -v /host/data:/data:rw,uid=1000,gid=1000 myapp

4. Container Bisa Akses Internet Padahal Seharusnya Tidak

Penyebab: Network mode salah atau iptables rule. Solusi:

bash
# Cek network
docker network inspect myapp-network
# Gunakan --network=none
# Atau nonaktifkan iptables
iptables -L -n

5. Container Melebihi Resource Limit

Penyebab: Limit terlalu rendah atau tidak ada limit. Solusi:

bash
# Tambah resource limit
docker run --memory=1g --cpus=2 --pids-limit=200 myapp
# Cek resource usage
docker stats

Kesimpulan

Docker Security memerlukan pendekatan berlapis dari image hingga runtime. Dengan prinsip least privilege, image minimal, resource limits, network isolation, dan monitoring yang tepat, container Docker dapat dijalankan dengan aman.

Kunci utama:

  • Jalankan container dengan user non-root dan capability minimal
  • Gunakan image resmi yang di-scan untuk kerentanan
  • Aktifkan read-only filesystem dan nonaktifkan privilege mode
  • Batasi resource (CPU, memory, PIDs) untuk setiap container
  • Gunakan Docker Secrets dan configs untuk data sensitif
  • Scan image secara rutin dengan Trivy atau Docker Scout
  • Audit Docker daemon dan container dengan Docker Bench Security
  • Update Docker dan OS host secara berkala
  • Hapus Docker socket dari user yang tidak berwenang
Catatan penting: Jangan pernah menjalankan Docker daemon dengan --privileged atau menambahkan user ke group docker tanpa alasan yang jelas. Group docker memberikan akses root ke host melalui container. Setiap container yang berjalan sebagai root dengan --privileged bisa mengakses semua file host Anda. Docker security bukan satu kali setup — ini memerlukan pemantauan berkelanjutan, audit rutin, dan update konstan. Terapkan praktik ini sejak awal proyek, bukan setelah terjadi insiden keamanan.
Ingat: Keamanan Docker adalah tanggung jawab bersama. Sebagai administrator, Anda bertanggung jawab untuk mengamankan host Docker. Sebagai developer, Anda bertanggung jawab untuk mengamankan image Docker. Setiap level — dari image yang Anda buat hingga konfigurasi daemon yang Anda atur — memiliki peran penting dalam menjaga keamanan seluruh ekosistem container. Jangan pernah merasa bahwa container sudah "cukup aman" — selalu ada ruang untuk perbaikan dan peningkatan.
Dipublikasikan untuk membantu para administrator dan developer Docker dalam mengamankan container dan host mereka.

Artikel Terkait