Docker Secrets 概述
Docker Secrets 是 Docker 提供的一種安全機制,專門用於管理容器化應用程式中的敏感資料,例如密碼、API 金鑰、TLS 憑證等。與將敏感資訊直接寫入環境變數或映像檔相比,Docker Secrets 提供了更安全的方式來處理這些機密資料。
主要特點
- 加密傳輸:Secrets 在節點之間透過 TLS 加密傳輸
- 加密儲存:Secrets 在 Raft log 中以加密形式儲存
- 最小權限原則:只有被授權的服務才能存取特定的 Secret
- 記憶體掛載:Secrets 以 tmpfs 檔案系統掛載到容器中,不會寫入磁碟
Swarm Mode 需求
Docker Secrets 原生功能需要 Docker Swarm Mode 才能使用。首先,您需要初始化 Swarm 叢集:
1
2
3
4
5
| # 初始化 Swarm(單節點模式)
docker swarm init
# 如果有多個網路介面,需指定廣播地址
docker swarm init --advertise-addr <IP_ADDRESS>
|
確認 Swarm 狀態:
1
2
| docker info | grep Swarm
# 輸出應為:Swarm: active
|
建立 Secret
Docker 提供兩種方式建立 Secret:
方法一:從標準輸入建立
1
2
3
4
5
| # 建立資料庫密碼 Secret
echo "my_super_secure_password" | docker secret create db_password -
# 建立 API 金鑰 Secret
printf "api_key_12345" | docker secret create api_key -
|
注意:使用 echo 會在字串末尾加入換行符,使用 printf 則不會。
方法二:從檔案建立
1
2
3
| # 從檔案建立 Secret
docker secret create ssl_certificate ./server.crt
docker secret create ssl_private_key ./server.key
|
查看已建立的 Secrets
1
2
3
4
5
| # 列出所有 Secrets
docker secret ls
# 查看 Secret 詳細資訊(不會顯示實際內容)
docker secret inspect db_password
|
使用 Secret
Secrets 透過服務(Service)來使用,而非直接用於容器:
1
2
3
4
5
6
7
8
| # 建立服務並掛載 Secret
docker service create \
--name my_mysql \
--secret db_password \
--secret db_root_password \
-e MYSQL_PASSWORD_FILE=/run/secrets/db_password \
-e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_root_password \
mysql:8.0
|
指定 Secret 掛載路徑與權限
1
2
3
4
| docker service create \
--name my_app \
--secret source=api_key,target=/app/config/api_key,mode=0400 \
my_app_image:latest
|
參數說明:
source:Secret 名稱target:容器內的掛載路徑mode:檔案權限(八進位格式)
Secret 檔案存取
在容器內部,Secrets 預設掛載於 /run/secrets/ 目錄:
1
2
3
4
5
6
7
8
| # 進入容器查看 Secret
docker exec -it <container_id> sh
# 列出所有 Secrets
ls -la /run/secrets/
# 查看 Secret 內容
cat /run/secrets/db_password
|
應用程式讀取範例
Python 範例:
1
2
3
4
5
6
7
8
| def get_secret(secret_name):
try:
with open(f'/run/secrets/{secret_name}', 'r') as secret_file:
return secret_file.read().strip()
except IOError:
return None
db_password = get_secret('db_password')
|
Node.js 範例:
1
2
3
4
5
6
7
8
9
10
11
| const fs = require('fs');
function getSecret(secretName) {
try {
return fs.readFileSync(`/run/secrets/${secretName}`, 'utf8').trim();
} catch (err) {
return null;
}
}
const dbPassword = getSecret('db_password');
|
更新與輪換 Secret
Docker Secrets 是不可變的(immutable),無法直接更新內容。輪換 Secret 需要建立新的 Secret 並更新服務:
1
2
3
4
5
6
7
8
9
10
11
| # 1. 建立新的 Secret
echo "new_secure_password" | docker secret create db_password_v2 -
# 2. 更新服務使用新 Secret
docker service update \
--secret-rm db_password \
--secret-add source=db_password_v2,target=db_password \
my_mysql
# 3. 確認服務正常運作後,刪除舊 Secret
docker secret rm db_password
|
Docker Compose 整合
在 Docker Compose(Swarm 模式)中使用 Secrets:
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
| # docker-compose.yml
version: "3.9"
services:
db:
image: mysql:8.0
secrets:
- db_password
- db_root_password
environment:
MYSQL_PASSWORD_FILE: /run/secrets/db_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_USER: app_user
MYSQL_DATABASE: app_db
web:
image: my_web_app:latest
secrets:
- source: api_key
target: /app/config/api_key
mode: 0400
depends_on:
- db
secrets:
db_password:
external: true
db_root_password:
external: true
api_key:
file: ./secrets/api_key.txt
|
部署到 Swarm:
1
2
3
4
5
6
| # 先建立外部 Secrets
echo "password123" | docker secret create db_password -
echo "root_password456" | docker secret create db_root_password -
# 部署 Stack
docker stack deploy -c docker-compose.yml my_stack
|
非 Swarm 環境替代方案
如果您不使用 Swarm Mode,可考慮以下替代方案:
1. Docker Compose Secrets(開發環境)
1
2
3
4
5
6
7
8
9
10
11
12
| # docker-compose.yml(非 Swarm 模式)
version: "3.9"
services:
app:
image: my_app:latest
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
|
注意:這種方式僅適用於開發環境,生產環境應使用專門的密鑰管理工具。
2. 外部密鑰管理工具
- HashiCorp Vault:企業級密鑰管理解決方案
- AWS Secrets Manager:AWS 雲端密鑰管理服務
- Azure Key Vault:Azure 雲端密鑰管理服務
- Google Secret Manager:GCP 雲端密鑰管理服務
3. 環境變數搭配 .env 檔案
1
2
3
| # .env 檔案(加入 .gitignore)
DB_PASSWORD=my_password
API_KEY=my_api_key
|
1
2
3
4
5
6
| # docker-compose.yml
services:
app:
image: my_app:latest
env_file:
- .env
|
最佳實踐
永遠不要將 Secrets 寫入映像檔
- 避免在 Dockerfile 中使用
COPY 或 ADD 複製敏感檔案 - 避免在 Dockerfile 中使用
ENV 設定敏感資訊
使用 _FILE 後綴的環境變數
- 許多官方映像支援
*_FILE 環境變數來讀取 Secret 檔案 - 例如:
MYSQL_PASSWORD_FILE、POSTGRES_PASSWORD_FILE
最小權限原則
- 只授予服務必要的 Secrets
- 使用適當的檔案權限(如
0400)
定期輪換 Secrets
- 建立 Secret 輪換機制
- 使用版本命名(如
db_password_v1、db_password_v2)
監控與稽核
- 記錄 Secret 的存取與使用情況
- 定期審查哪些服務使用了哪些 Secrets
開發與生產環境分離
- 開發環境使用測試用的 Secrets
- 生產環境使用專門的密鑰管理系統
參考資料