容器安全概述
隨著容器化技術的普及,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"]
|
參考資料