Setiap image Docker yang Anda tarik adalah keputusan kepercayaan. Image Node.js 20 resmi di Docker Hub berisi instalasi Debian atau Alpine Linux penuh — dan paket sistem operasi tersebut memiliki kerentanan. Pada 2024, laporan keamanan container Snyk menemukan bahwa 84% image container memiliki setidaknya satu kerentanan yang diketahui, dan 45% memiliki CVE kritis atau tinggi. Saya menjalankan pemindaian container Trivy dalam pipeline CI/CD saya untuk semua deployment produksi dan telah memblokir beberapa pembaruan image yang akan memperkenalkan CVE baru.
Container tidak menyediakan isolasi keamanan — mereka menyediakan isolasi proses. Kerentanan dalam library di dalam container Anda adalah kerentanan yang dapat dieksploitasi dalam aplikasi Anda. Kerentanan Log4Shell (CVE-2021-44228) pada akhir 2021 mempengaruhi aplikasi Java mana pun yang menggunakan log4j — termasuk aplikasi yang berjalan dalam container. Container yang belum dibangun ulang dengan dependensi yang dipatch tetap rentan bahkan ketika OS host sudah di-patch sepenuhnya.
Trivy adalah pemindai kerentanan open-source oleh Aqua Security yang memindai image Docker, filesystem, repositori git, dan manifes Kubernetes. Ini memeriksa: paket OS (Debian, Alpine, Ubuntu, RHEL), paket khusus bahasa (npm, pip, modul Go, Maven), miskonfigurasi infrastruktur, dan rahasia yang terekspos dalam image. Trivy memperbarui database kerentanannya setiap hari dari NVD, GitHub Advisory, dan database kerentanan khusus OS.
# Install Trivy
brew install trivy
# or: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Scan a Docker image
trivy image node:20-alpine
trivy image your-registry.com/your-app:latest
# Scan with severity filter (fail on CRITICAL and HIGH)
trivy image --severity CRITICAL,HIGH --exit-code 1 node:20-alpine
# Scan and ignore unfixed vulnerabilities (recommended for CI)
trivy image --severity CRITICAL,HIGH --ignore-unfixed --exit-code 1 your-app:latest
# Output SARIF for GitHub Security tab
trivy image --format sarif --output trivy-results.sarif your-app:latest
# Scan your local Dockerfile for misconfigurations
trivy config .
# Scan running Kubernetes cluster
trivy k8s --report summary cluster
# GitHub Actions integration
# .github/workflows/trivy.yml:
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t app:${{ github.sha }} .
- name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: 1
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarifDari pengalaman saya menjalankan Trivy dalam CI/CD produksi: gunakan `--ignore-unfixed` dalam pipeline CI Anda untuk hanya gagal pada kerentanan yang memiliki perbaikan yang tersedia. Gagal pada setiap CVE termasuk yang tidak dapat diperbaiki menciptakan kelelahan alert — developer mulai mengabaikan output scanner. Hanya temuan yang dapat ditindaklanjuti yang ditindaklanjuti.
Pola integrasi CI yang saya gunakan: bangun image Docker di CI, jalankan pemindaian Trivy pada image yang dibangun, gagalkan pipeline pada kerentanan kritis atau tinggi dengan perbaikan yang tersedia, dan unggah laporan SARIF ke tab GitHub Security untuk pelacakan. Ini memastikan tidak ada image dengan kerentanan kritis yang didorong ke produksi.
Kontrol keamanan container paling efektif adalah memilih base image minimal. Perbandingan untuk aplikasi Node.js: node:20 (berbasis Debian) — 600MB, 300+ paket, banyak yang tidak dibutuhkan. node:20-slim — 200MB, paket Debian minimal. node:20-alpine — 65MB, Alpine Linux dengan hanya ~20 paket. Distroless (gcr.io/distroless/nodejs20) — tidak ada shell, tidak ada package manager, attack surface minimal. Saya menggunakan node:20-alpine untuk sebagian besar layanan.
# Secure Dockerfile — multi-stage, non-root, pinned base
# Pin exact digest to prevent silent updates
FROM node:20.18.1-alpine3.20@sha256:2d07db07a2df6830718ae2a47db6fedce6745f5bcd174c398f2acdda90a11c03 AS builder
WORKDIR /app
COPY package*.json ./
# Install all dependencies for build
RUN npm ci --include=dev
COPY . .
RUN npm run build
# Production stage — minimal image
FROM node:20.18.1-alpine3.20@sha256:2d07db07a2df6830718ae2a47db6fedce6745f5bcd174c398f2acdda90a11c03
WORKDIR /app
# Create non-root user BEFORE copying files
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
# Copy only production artifacts
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./
# Run as non-root
USER nextjs
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "dist/main.js"]
# Trivy scan comparison:
# node:20 → ~120 CVEs (85MB base)
# node:20-slim → ~45 CVEs (55MB)
# node:20-alpine → ~8 CVEs (15MB)
# Distroless nodejs → ~2 CVEs (no shell at all)Menggunakan `FROM node:20` dalam Dockerfile Anda berbahaya — ini diam-diam diperbarui ke patch terbaru saat Anda rebuild, yang dapat memperkenalkan kerentanan baru atau breaking change. Selalu pin ke digest tertentu: `FROM node:20.18.1-alpine3.20@sha256:<digest>`. Gunakan Renovate Bot atau Dependabot untuk mengotomatiskan pembaruan base image — mereka membuat PR dengan changelog ketika base image baru tersedia.
Di luar pemilihan base image, keamanan Dockerfile bergantung pada: menjalankan sebagai pengguna non-root (USER node atau USER 1001 — jangan pernah jalankan container produksi sebagai root), tidak menyalin rahasia ke dalam image (gunakan rahasia build-time dengan --mount=type=secret, bukan ARG), menggunakan multi-stage build untuk mengecualikan alat build dan source code dari image final, dan menetapkan sistem file read-only jika memungkinkan.
Trivy dapat memindai container yang berjalan dan manifes Kubernetes selain image. Untuk Kubernetes: `trivy k8s --report summary cluster` memindai seluruh cluster Anda untuk kerentanan dan miskonfigurasi. OPA Gatekeeper atau Kyverno dapat menegakkan kebijakan yang mencegah pod menggunakan image yang tidak dipindai atau tidak sesuai agar tidak berjalan di produksi.
Alur kerja lengkap: Dockerfile menggunakan base image Alpine yang di-pin dan pengguna non-root. Multi-stage build mengecualikan devDependencies dan alat build dari image final. GitHub Actions membangun image dan menjalankan Trivy dengan --exit-code 1 pada CVE kritis/tinggi yang dapat diperbaiki. Output SARIF Trivy diunggah ke tab GitHub Security untuk pelacakan. Image yang di-tag dengan git SHA dan didorong ke registry hanya setelah pemindaian lulus. Renovate Bot membuat PR mingguan untuk pembaruan base image.