Docker 安全最佳實務與漏洞掃描

Docker Security Best Practices and Vulnerability Scanning

容器安全概述

隨著容器化技術的普及,Docker 已成為現代應用程式部署的標準工具。然而,容器安全也成為企業資安的重要課題。一個不安全的容器映像檔可能包含已知漏洞、惡意軟體,或者配置不當的設定,進而導致整個系統暴露於風險之中。

容器安全的主要威脅

  • 映像檔漏洞:基礎映像檔或安裝的套件可能包含已知的安全漏洞
  • 權限過高:以 root 身份執行容器會增加攻擊面
  • 敏感資料外洩:將密碼、金鑰等敏感資訊寫入映像檔
  • 網路攻擊:容器間或容器與主機間的網路未適當隔離
  • 資源濫用:未限制容器資源可能導致 DoS 攻擊

使用官方或可信任的基礎映像檔

選擇基礎映像檔是容器安全的第一道防線。始終優先使用官方映像檔或經過驗證的發布者。

最佳實務

1
2
3
4
5
6
7
8
# 推薦:使用官方映像檔並指定具體版本
FROM python:3.11-slim-bookworm

# 避免:使用 latest 標籤
# FROM python:latest

# 推薦:使用 Alpine 版本以減少攻擊面
FROM node:20-alpine

驗證映像檔來源

1
2
3
4
5
6
# 使用 Docker Content Trust 驗證映像檔簽章
export DOCKER_CONTENT_TRUST=1
docker pull nginx:1.25

# 查看映像檔的詳細資訊
docker inspect nginx:1.25 | jq '.[0].RepoDigests'

以非 root 使用者執行

以 root 身份執行容器是常見的安全疏忽。若容器被入侵,攻擊者將獲得 root 權限。

建立非 root 使用者的 Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
FROM python:3.11-slim-bookworm

# 建立應用程式使用者和群組
RUN groupadd --gid 1000 appgroup && \
    useradd --uid 1000 --gid appgroup --shell /bin/bash --create-home appuser

# 設定工作目錄
WORKDIR /app

# 複製應用程式檔案
COPY --chown=appuser:appgroup . .

# 安裝相依套件
RUN pip install --no-cache-dir -r requirements.txt

# 切換到非 root 使用者
USER appuser

# 執行應用程式
CMD ["python", "app.py"]

執行時強制使用非 root

1
2
3
4
5
# 即使 Dockerfile 未指定,仍可在執行時指定使用者
docker run --user 1000:1000 my-app

# 驗證容器內的使用者
docker exec my-container whoami

最小化映像檔層數

減少映像檔的層數和大小不僅能提升效能,還能降低攻擊面。

多階段建置範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 建置階段
FROM golang:1.21-alpine AS builder

WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server

# 執行階段
FROM alpine:3.19

# 安裝必要的 CA 憑證
RUN apk --no-cache add ca-certificates

# 建立非 root 使用者
RUN addgroup -g 1000 appgroup && \
    adduser -u 1000 -G appgroup -D appuser

WORKDIR /app

# 從建置階段複製二進位檔案
COPY --from=builder --chown=appuser:appgroup /app/server .

USER appuser

EXPOSE 8080
CMD ["./server"]

使用 Docker Scout 掃描漏洞

Docker Scout 是 Docker 官方提供的漏洞掃描工具,可直接整合於 Docker Desktop 和 CLI。

安裝與使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Docker Scout 已內建於 Docker Desktop
# 對於 CLI 使用者,確保 Docker 版本為 24.0 以上

# 掃描本地映像檔
docker scout cves my-app:latest

# 快速檢視漏洞摘要
docker scout quickview my-app:latest

# 比較兩個版本的安全差異
docker scout compare my-app:v1 my-app:v2

# 輸出詳細報告為 JSON 格式
docker scout cves --format json my-app:latest > vulnerability-report.json

在 CI/CD 中整合 Docker Scout

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# GitHub Actions 範例
name: Docker Security Scan

on:
  push:
    branches: [main]

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

      - name: Build Docker image
        run: docker build -t my-app:${{ github.sha }} .

      - name: Docker Scout Scan
        uses: docker/scout-action@v1
        with:
          command: cves
          image: my-app:${{ github.sha }}
          only-severities: critical,high
          exit-code: true

使用 Trivy 掃描漏洞

Trivy 是由 Aqua Security 開發的開源漏洞掃描工具,支援容器映像檔、檔案系統和 Git 儲存庫掃描。

安裝 Trivy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Ubuntu/Debian
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy

# 或使用 Docker 執行
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/trivy image my-app:latest

掃描映像檔

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 基本掃描
trivy image my-app:latest

# 僅顯示嚴重和高風險漏洞
trivy image --severity CRITICAL,HIGH my-app:latest

# 輸出 JSON 格式報告
trivy image --format json --output report.json my-app:latest

# 忽略未修復的漏洞
trivy image --ignore-unfixed my-app:latest

# 掃描並在發現嚴重漏洞時失敗
trivy image --exit-code 1 --severity CRITICAL my-app:latest

Dockerfile 安全檢查(hadolint)

hadolint 是一款 Dockerfile 靜態分析工具,可以檢查 Dockerfile 是否符合最佳實務。

安裝與使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 使用 Docker 執行
docker run --rm -i hadolint/hadolint < Dockerfile

# 在本地安裝(macOS)
brew install hadolint

# 在本地安裝(Linux)
wget -O /usr/local/bin/hadolint \
    https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint

hadolint 檢查範例

1
2
3
4
5
6
# 檢查 Dockerfile
hadolint Dockerfile

# 輸出範例:
# Dockerfile:3 DL3008 warning: Pin versions in apt get install
# Dockerfile:7 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT

常見的 hadolint 規則

規則說明
DL3008應固定 apt-get 安裝的套件版本
DL3009應刪除 apt-get 快取
DL3018應固定 apk add 安裝的套件版本
DL4006設定 SHELL 選項以便錯誤時失敗
SC2086雙引號以防止字串分割

設定唯讀檔案系統

將容器的檔案系統設為唯讀可以防止攻擊者在容器內寫入惡意檔案。

執行唯讀容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 使用 --read-only 旗標
docker run --read-only my-app

# 若應用程式需要寫入暫存檔案,可掛載 tmpfs
docker run --read-only --tmpfs /tmp:rw,noexec,nosuid my-app

# 掛載特定目錄為可寫
docker run --read-only \
    --tmpfs /tmp \
    --tmpfs /var/run \
    -v app-data:/app/data \
    my-app

在 Docker Compose 中設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
version: '3.8'

services:
  web:
    image: my-app:latest
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    volumes:
      - app-data:/app/data:rw
    security_opt:
      - no-new-privileges:true

volumes:
  app-data:

限制容器資源

未限制資源的容器可能被用於 DoS 攻擊或影響同一主機上的其他服務。

設定資源限制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 限制 CPU 使用
docker run --cpus="1.5" my-app

# 限制記憶體使用
docker run --memory="512m" --memory-swap="512m" my-app

# 限制程序數量
docker run --pids-limit 100 my-app

# 綜合設定
docker run \
    --cpus="1.0" \
    --memory="256m" \
    --memory-swap="256m" \
    --pids-limit 50 \
    --read-only \
    --security-opt no-new-privileges:true \
    my-app

在 Docker Compose 中設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
version: '3.8'

services:
  web:
    image: my-app:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

完整的安全 Dockerfile 範例

以下是一個整合所有安全最佳實務的完整範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# syntax=docker/dockerfile:1

# 建置階段
FROM python:3.11-slim-bookworm AS builder

WORKDIR /build

# 安裝建置相依套件
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential=12.9 \
    && rm -rf /var/lib/apt/lists/*

# 複製並安裝 Python 相依套件
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# 執行階段
FROM python:3.11-slim-bookworm

# 安全標籤
LABEL maintainer="security@example.com" \
      version="1.0" \
      description="Secure Python Application"

# 建立非 root 使用者
RUN groupadd --gid 1000 appgroup && \
    useradd --uid 1000 --gid appgroup --shell /sbin/nologin --create-home appuser

# 設定工作目錄
WORKDIR /app

# 從建置階段複製 Python 套件
COPY --from=builder /root/.local /home/appuser/.local

# 複製應用程式碼
COPY --chown=appuser:appgroup . .

# 設定環境變數
ENV PATH=/home/appuser/.local/bin:$PATH \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# 切換到非 root 使用者
USER appuser

# 健康檢查
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1

# 開放連接埠
EXPOSE 8080

# 執行應用程式
CMD ["python", "app.py"]

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy