TDCTF Academy Logo TDCTF ACADEMY

9.4.1 Docker Security

Docker Security mencakup praktik pengamanan container dari hulu ke hilir - mulai dari image building, registri, runtime, hingga deployment. Container bukan VM yang terisolasi penuh; container berbagi kernel dengan host, sehingga satu celah keamanan bisa berdampak ke seluruh sistem.

Image Security (Vulnerability Scanning)

Image adalah fondasi container. Image yang mengandung kerentanan akan mengekspos aplikasi ke risiko serius.

Trivy - Vulnerability Scanner untuk Container

Trivy (dari Aqua Security) adalah scanner open-source yang cepat dan komprehensif:

# Install Trivy
sudo apt-get install -y trivy
# atau
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Scan image lokal
trivy image nginx:1.25

# Scan dengan output format JSON (untuk CI/CD)
trivy image --severity CRITICAL,HIGH --format json --output trivy-report.json node:18-alpine

# Scan registry langsung (tanpa pull)
trivy image --server https://registry.example.com/my-app:latest

# Scan direktori (filesystem)
trivy fs --severity CRITICAL /path/to/project

# Scan repositori git
trivy repo https://github.com/org/repo.git

Contoh output Trivy:

nginx:1.25 (debian 12.0)
=======================
Total: 45 (UNKNOWN: 2, LOW: 12, MEDIUM: 25, HIGH: 4, CRITICAL: 2)

┌──────────────┬──────────────────┬──────────┬────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │
├──────────────┼──────────────────┼──────────┼────────────────────────────────┤
│ libssl3 │ CVE-2024-xxx │ CRITICAL │ 3.0.11-1 │
│ openssl │ CVE-2024-xxx │ HIGH │ 3.0.11-1 │
│ zlib1g │ CVE-2023-xxx │ MEDIUM │ 1:1.2.13.dfsg-1 │
└──────────────┴──────────────────┴──────────┴────────────────────────────────┘

Docker Scout - Native Docker Scanning

Docker Scout terintegrasi langsung dengan Docker Desktop dan Docker Hub:

# Mengaktifkan Docker Scout di image
docker scout quickview nginx:1.25

# Analisis detail
docker scout recommendations nginx:1.25

# Perbandingan dua image
docker scout compare nginx:1.25 nginx:1.26-alpine

# Integrasi dengan CI/CD
docker scout cves image:latest --exit-code --only-severity critical

Snyk - Commercial Container Security

# Install Snyk CLI
npm install -g snyk
snyk auth

# Monitor image
snyk container monitor node:18-alpine --org=my-org

# Test image
snyk container test node:18-alpine --severity-threshold=high

Dockerfile Best Practices

1. Gunakan Multi-Stage Builds

Multi-stage build memisahkan environment build dari runtime, menghasilkan image yang lebih kecil dan aman:

# === STAGE 1: Build ===
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server -ldflags="-s -w" .

# === STAGE 2: Runtime Minimal ===
FROM alpine:3.19
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder --chown=appuser:appgroup /app/server /server
EXPOSE 8080
USER appuser
ENTRYPOINT ["/server"]

Manfaat: Image final hanya berisi binary + minimal dependencies. Ukuran bisa turun dari 1GB+ menjadi <20MB.

2. Minimal Base Image

Base Image Ukuran Use Case
alpine:3.19 ~7 MB Production, binary-only apps
debian:12-slim ~80 MB Need glibc compatibility
distroless ~5-25 MB Maximum security (no shell, no package manager)
scratch 0 B Static binary, Go/Rust apps

Contoh Distroless:

FROM gcr.io/distroless/base-debian12
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINE ["/server"]

Distroless tidak memiliki shell, package manager, atau utility apapun - mengurangi attack surface secara drastis.

3. Non-Root User

JANGAN pernah menjalankan container sebagai root:

# ❌ BURUK - berjalan sebagai root
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]
# ✅ BAIK - non-root user
FROM node:18-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm install
EXPOSE 3000
USER appuser
CMD ["node", "server.js"]

4. Praktik Lainnya

Specific tag, jangan latest:

# ❌ BURUK - tidak reproducible
FROM node:latest

# ✅ BAIK - versi spesifik
FROM node:18.20.0-alpine3.19

Hindari menambahkan tools yang tidak perlu:

# ❌ BURUK - banyak tools yang tidak diperlukan
RUN apt-get update && apt-get install -y curl vim netcat tree wget

# ✅ BAIK - hanya yang diperlukan
RUN apt-get update && apt-get install -y --no-install-recommends curl=8.4.0 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

Layer optimization - gabungkan RUN commands:

# ✅ BAIK - mengurangi jumlah layer
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

HEALTHCHECK:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://vuln.cybersecurity.or.id/health || exit 1

Runtime Security

Runtime security melindungi container saat sedang berjalan - membatasi system call, akses file, dan kemampuan proses.

Seccomp (Secure Computing Mode)

Seccomp membatasi system call yang bisa dilakukan container. Docker memiliki profil default yang memblokir ~44 system call berbahaya:

# Menggunakan profil seccomp default Docker
docker run --security-opt seccomp=default nginx

# Menggunakan profil kustom
docker run --security-opt seccomp=/path/to/custom-seccomp.json nginx

Contoh profil seccomp kustom - strict profile hanya untuk web server:

{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "readv", "writev", "open", "openat",
"close", "mmap", "munmap", "brk", "fstat", "stat",
"accept", "accept4", "bind", "listen", "connect",
"sendto", "recvfrom", "sendmsg", "recvmsg",
"epoll_create1", "epoll_ctl", "epoll_wait",
"clock_gettime", "gettid", "exit_group", "exit",
"set_robust_list", "futex"],
"action": "SCMP_ACT_ALLOW"
}
]
}

AppArmor

AppArmor adalah Mandatory Access Control (MAC) yang membatasi akses file, network, dan capability:

# Cek profil AppArmor yang aktif
sudo aa-status

# Menjalankan container dengan profil AppArmor kustom
docker run --security-opt apparmor=custom-profile nginx

Contoh profil AppArmor untuk nginx:

#include <tunables/global>

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>

network tcp,
network inet tcp,

/etc/nginx/** r,
/var/log/nginx/** w,
/var/run/nginx.pid rw,
/usr/sbin/nginx mr,

deny /etc/shadow r,
deny /etc/gshadow r,
}

SELinux

Di RHEL/CentOS/Fedora, SELinux memberikan label keamanan tambahan:

# Konteks SELinux untuk container
docker run --security-opt label=type:container_t nginx

# Disable SELinux untuk container tertentu (tidak direkomendasikan)
docker run --security-opt label=disable nginx

Linux Capabilities

Container berjalan dengan set capability terbatas. Praktik terbaik: drop ALL, lalu tambahkan hanya yang diperlukan:

# ❌ Buruk - retain default capabilities
docker run nginx

# ✅ Baik - drop ALL, add specific
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# ✅ Terbaik - tidak perlu NET_BIND_SERVICE jika port >1024
docker run --cap-drop=ALL --cap-add=CHOWN nginx

Capabilities umum yang dibutuhkan aplikasi:

Capability Fungsi
NET_BIND_SERVICE Bind ke port <1024
CHOWN Mengubah file ownership
SETUID / SETGID Mengubah UID/GID proses
DAC_OVERRIDE Bypass file permission checks
SYS_PTRACE Debug proses dengan strace/ptrace

Resource Limits

Mencegah satu container menghabiskan resource host:

# Memory limit - 512MB maksimal
docker run --memory="512m" --memory-reservation="256m" --memory-swap="1g" nginx

# CPU limit - maksimal 1.5 core
docker run --cpus="1.5" nginx

# CPU pinning - hanya core 0 dan 1
docker run --cpuset-cpus="0,1" nginx

# Disk I/O - prioritas rendah
docker run --blkio-weight=100 nginx

# Semua limit sekaligus
docker run \
--memory="512m" \
--memory-reservation="256m" \
--cpus="1.0" \
--ulimit nofile=1024:2048 \
--ulimit nproc=100:200 \
nginx

Read-Only Root Filesystem

Membuat rootfs container read-only mencegah penyerang memodifikasi file sistem:

# Root filesystem read-only, volume writable untuk data
docker run --read-only --tmpfs /tmp --tmpfs /var/run nginx

# Dengan persistent volume untuk log
docker run --read-only -v /host/logs:/var/log nginx

Konfigurasi di Docker Compose:

services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64M
- /var/run
volumes:
- app-data:/data
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true

User Namespace Remapping

Memetakan ulang UID/GID sehingga root di container (UID 0) menjadi non-root di host:

// /etc/docker/daemon.json
{
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

Setelah restart Docker, semua container berjalan dengan UID non-privileged di host. Namun fitur ini tidak kompatibel dengan beberapa konfigurasi (host networking, volume mounting).

Docker Bench Security

Docker Bench Security adalah script audit berdasarkan CIS Benchmark for Docker:

# Menjalankan audit
docker run --rm --net host --pid host \
-v /etc:/etc:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /etc/kubernetes:/etc/kubernetes:ro \
docker/docker-bench-security

# Output dengan warna
docker run --rm --net host --pid host \
-v /etc:/etc:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
docker/docker-bench-security \
-c docker_bench_security.json

Contoh output - items yang diperiksa:

[PASS] 1.1.1 - Ensure a separate partition for containers
[PASS] 2.1 - Ensure network traffic is restricted
[WARN] 2.5 - Ensure `--live-restore` is enabled
[PASS] 4.1 - Ensure a user for the container is non-root
[FAIL] 5.4 - Ensure `--read-only` is enabled

Supply Chain Attacks pada Container

Supply chain attack terjadi ketika penyerang menyusupkan kode berbahaya di salah satu titik rantai: base image, dependencies, atau CI/CD pipeline.

1. Malicious Base Images

Penyerang mengupload image berbahaya ke Docker Hub dengan nama yang mirip dengan image resmi:

# ❌ BURUK - image dari unknown publisher (typosquatting)
docker pull node:18
# vs node:18 - mana yang resmi?

# ✅ BAIK - selalu verifikasi publisher
docker pull docker.io/library/node:18-alpine

Cek official image: Cari badge "Official Image" di Docker Hub atau gunakan docker trust inspect:

# Verifikasi signature image
docker trust inspect nginx:latest --pretty

2. Compromised Dependencies

# ❌ BURUK - semua dependency di-install di stage final
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["node", "app.js"]

Mitigasi: Gunakan lock file, verifikasi signature, dan scan dependencies:

# npm audit untuk mendeteksi kerentanan di dependencies
npm audit --audit-level=high

# SBOM generation (Syft)
syft packages node:18-alpine -o spdx-json > sbom.json

# Grype - vulnerability scanner untuk SBOM
grype sbom:sbom.json

3. Image Tampering di Registry

Gunakan Docker Content Trust (Notary) untuk memastikan integritas image:

# Mengaktifkan content trust
export DOCKER_CONTENT_TRUST=1

# Push image yang ditandatangani
docker push perusahaan/myapp:1.0.0

# Pull hanya image yang ditandatangani
docker pull perusahaan/myapp:1.0.0

4. CI/CD Pipeline Poisoning

# .github/workflows/docker-build.yml
name: Build and Push
on:
push:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Scan Dockerfile untuk secrets
run: trufflehog filesystem --json . | jq

- name: Build image
run: |
docker build -t app:latest .
docker scout quickview app:latest

- name: Scan image untuk kerentanan
run: trivy image --exit-code 1 --severity CRITICAL app:latest

- name: Sign dan Push
run: |
export DOCKER_CONTENT_TRUST=1
docker push registry.example.com/app:latest
env:
DOCKER_CONTENT_TRUST_SERVER: https://notary.example.com

Verifikasi

# Audit image dengan Trivy
trivy image myapp:latest --severity CRITICAL,HIGH

# Periksa running container
docker ps --quiet | xargs docker inspect --format '{{.Name}}: {{.HostConfig.ReadonlyRootfs}} {{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}'

# Jalankan Docker Bench Security
docker run --rm --net host --pid host \
-v /etc:/etc:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
docker/docker-bench-security

# Cek user container
docker ps --quiet | xargs docker inspect --format '{{.Name}}: User={{.Config.User}}'

Referensi

PADA HALAMAN INI