零信任網路存取(Zero Trust Network Access, ZTNA)是現代企業安全架構的核心概念。在這個框架中,憑證扮演著至關重要的角色,負責驗證使用者、裝置和服務的身份。本文將深入探討零信任架構中的憑證策略,包括裝置憑證、短期憑證、mTLS 實作等關鍵主題。
零信任架構概念
什麼是零信任?
零信任(Zero Trust)是一種安全模型,其核心原則是「永不信任,始終驗證」(Never Trust, Always Verify)。這種架構假設網路內外都可能存在威脅,因此不再依賴傳統的網路邊界防禦,而是對每一次存取請求進行嚴格的身份驗證和授權。
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
| 傳統安全模型 vs 零信任模型
+-----------------------------------+
| 傳統安全模型 |
+-----------------------------------+
| |
| +---------------------------+ |
| | 企業內部網路 | |
| | (信任區域) | |
| | | |
| | +---------+ +-------+ | |
| | | 伺服器 | | 使用者 | | |
| | +---------+ +-------+ | |
| +---------------------------+ |
| | |
| 防火牆邊界 |
| | |
| +---------------------------+ |
| | 外部網路 | |
| | (不信任區域) | |
| +---------------------------+ |
+-----------------------------------+
+-----------------------------------+
| 零信任模型 |
+-----------------------------------+
| |
| 每個存取請求都需要驗證: |
| |
| +-------+ 驗證 +-------+ |
| | 使用者 | --------> | 資源 | |
| +-------+ +-------+ |
| | | |
| +---------------------------+ |
| | 身份驗證 + 授權 | |
| | 裝置健康檢查 | |
| | 存取政策評估 | |
| | 持續監控 | |
| +---------------------------+ |
+-----------------------------------+
|
零信任的核心原則
明確驗證:基於所有可用的資料點進行驗證,包括使用者身份、位置、裝置健康狀態、服務或工作負載、資料分類和異常檢測。
最小權限存取:使用即時和足夠的存取權限(JIT/JEA),風險型調適政策和資料保護來限制使用者存取。
假設已被入侵:最小化影響範圍,分段存取,驗證端對端加密,並使用分析來提高可見性、驅動威脅偵測和改善防禦。
ZTNA 與傳統 VPN 的差異
| 特性 | 傳統 VPN | ZTNA |
|---|
| 存取模式 | 全網路存取 | 單一應用程式存取 |
| 信任模型 | 連接後信任 | 持續驗證 |
| 可見性 | 僅限連接狀態 | 完整應用程式層可見性 |
| 擴展性 | 需要複雜設定 | 雲原生易擴展 |
| 安全性 | 橫向移動風險高 | 最小攻擊面 |
憑證在零信任中的角色
憑證的核心功能
在零信任架構中,憑證是實現身份驗證的關鍵技術。憑證提供以下核心功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| +-------------------------------------------+
| 憑證在零信任中的角色 |
+-------------------------------------------+
| |
| +-------------+ +-------------+ |
| | 身份證明 | | 加密通訊 | |
| | Identity | | Encryption | |
| +------+------+ +------+------+ |
| | | |
| v v |
| +-------------+ +-------------+ |
| | 存取控制 | | 完整性驗證 | |
| | Access Ctrl | | Integrity | |
| +------+------+ +------+------+ |
| | | |
| +--------+---------+ |
| | |
| v |
| +----------------+ |
| | 零信任安全保障 | |
| +----------------+ |
+-------------------------------------------+
|
憑證類型與用途
| 憑證類型 | 用途 | 零信任應用場景 |
|---|
| 裝置憑證 | 識別和驗證裝置 | 裝置健康檢查、設備准入控制 |
| 使用者憑證 | 識別和驗證使用者 | 多因素認證、無密碼登入 |
| 服務憑證 | 識別和驗證服務 | 微服務通訊、API 認證 |
| 工作負載憑證 | 識別和驗證容器/工作負載 | Kubernetes Pod 認證 |
憑證生命週期管理
1
2
3
4
5
6
7
8
9
| +----------------------------------------------------------+
| 憑證生命週期 |
+----------------------------------------------------------+
| |
| 申請 --> 簽發 --> 分發 --> 使用 --> 監控 --> 更新/撤銷 |
| ^ | |
| +----------------------------------------------+ |
| 持續循環 |
+----------------------------------------------------------+
|
裝置憑證與身份驗證
裝置身份的重要性
在零信任架構中,裝置身份與使用者身份同等重要。即使使用者身份正確,若裝置不受信任或未通過健康檢查,存取請求仍應被拒絕。
裝置憑證部署流程
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
| # 1. 為裝置產生私鑰
openssl genrsa -out device-001.key 2048
# 2. 建立裝置憑證簽署請求(CSR)
openssl req -new -key device-001.key -out device-001.csr \
-subj "/C=TW/ST=Taiwan/O=Enterprise/OU=Devices/CN=device-001"
# 3. 建立裝置憑證擴展設定檔
cat > device-001.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = device-001.internal
URI.1 = urn:device:uuid:550e8400-e29b-41d4-a716-446655440000
EOF
# 4. 使用企業 CA 簽發裝置憑證
openssl x509 -req -in device-001.csr \
-CA enterprise-ca.crt -CAkey enterprise-ca.key \
-CAcreateserial -out device-001.crt -days 90 \
-extfile device-001.ext -sha256
|
裝置憑證驗證腳本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #!/bin/bash
# 裝置憑證驗證腳本
DEVICE_CERT="device-001.crt"
CA_CERT="enterprise-ca.crt"
# 驗證憑證有效性
echo "=== 驗證憑證鏈 ==="
openssl verify -CAfile $CA_CERT $DEVICE_CERT
# 檢查憑證到期時間
echo "=== 憑證有效期限 ==="
openssl x509 -in $DEVICE_CERT -noout -dates
# 提取裝置識別資訊
echo "=== 裝置識別資訊 ==="
openssl x509 -in $DEVICE_CERT -noout -subject
# 檢查憑證用途
echo "=== 憑證用途 ==="
openssl x509 -in $DEVICE_CERT -noout -purpose | grep "SSL client"
|
整合 MDM 的裝置憑證管理
現代企業通常透過行動裝置管理(MDM)或統一端點管理(UEM)系統來自動化裝置憑證的部署和管理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 範例:Intune/Jamf 裝置憑證設定檔
device_certificate_profile:
name: "Zero Trust Device Certificate"
platform:
- Windows
- macOS
- iOS
- Android
certificate_type: "SCEP"
scep_server_urls:
- "https://scep.enterprise.com/certsrv/mscep/"
key_size: 2048
key_usage:
- digital_signature
- key_encipherment
extended_key_usage:
- client_authentication
subject_name_format: "CN={{DeviceId}},OU=Devices,O=Enterprise"
renewal_threshold_percentage: 20
validity_period: 90
validity_period_unit: "days"
|
裝置健康評估與憑證結合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 裝置存取決策流程:
+----------------+ +------------------+ +----------------+
| 裝置連線 | --> | 憑證驗證 | --> | 健康檢查 |
+----------------+ +------------------+ +----------------+
| |
v v
憑證有效? 裝置合規?
/ \ / \
是 否 是 否
| | | |
v v v v
+----------+ +--------+ +---------+ +--------+
| 繼續流程 | | 拒絕 | | 授權 | | 拒絕 |
+----------+ +--------+ +---------+ +--------+
|
短期憑證與動態信任
為什麼需要短期憑證?
傳統的長期憑證(有效期 1-3 年)在零信任架構中存在以下問題:
- 憑證洩露風險:長期有效的憑證一旦洩露,攻擊者可長期利用
- 撤銷機制延遲:CRL/OCSP 可能存在快取和延遲問題
- 權限過時:使用者或裝置的權限可能已變更,但憑證仍有效
- 審計困難:難以追蹤長期憑證的使用情況
短期憑證的優勢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| +-----------------------------------------------+
| 短期憑證優勢 |
+-----------------------------------------------+
| |
| +------------------+ +------------------+ |
| | 降低洩露風險 | | 自動化輪換 | |
| | 憑證有效期短 | | 無需手動介入 | |
| | 洩露影響有限 | | 減少運維負擔 | |
| +------------------+ +------------------+ |
| |
| +------------------+ +------------------+ |
| | 即時權限控制 | | 簡化撤銷流程 | |
| | 每次簽發時評估 | | 憑證自然過期 | |
| | 動態調整權限 | | 無需維護 CRL | |
| +------------------+ +------------------+ |
+-----------------------------------------------+
|
建議的憑證有效期
| 憑證類型 | 建議有效期 | 說明 |
|---|
| 服務網格憑證 | 1-24 小時 | 高頻輪換,最小攻擊面 |
| API 存取憑證 | 1-7 天 | 平衡安全性與效能 |
| 工作負載憑證 | 7-30 天 | 容器和無伺服器環境 |
| 裝置憑證 | 30-90 天 | 配合 MDM 管理週期 |
| 使用者憑證 | 8-24 小時 | 每日重新認證 |
使用 Vault 實作短期憑證
HashiCorp Vault 是實作短期憑證的理想工具。以下是設定範例:
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
| # 1. 啟用 PKI 密鑰引擎
vault secrets enable pki
# 2. 設定 PKI 最大租期
vault secrets tune -max-lease-ttl=87600h pki
# 3. 產生根憑證
vault write pki/root/generate/internal \
common_name="Zero Trust Root CA" \
ttl=87600h
# 4. 設定 CA 和 CRL URLs
vault write pki/config/urls \
issuing_certificates="https://vault.enterprise.com:8200/v1/pki/ca" \
crl_distribution_points="https://vault.enterprise.com:8200/v1/pki/crl"
# 5. 啟用中繼 CA
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int
# 6. 產生中繼 CA CSR
vault write -format=json pki_int/intermediate/generate/internal \
common_name="Zero Trust Intermediate CA" \
| jq -r '.data.csr' > pki_intermediate.csr
# 7. 使用根 CA 簽發中繼 CA 憑證
vault write -format=json pki/root/sign-intermediate \
csr=@pki_intermediate.csr \
format=pem_bundle ttl="43800h" \
| jq -r '.data.certificate' > intermediate.cert.pem
# 8. 設定中繼 CA 憑證
vault write pki_int/intermediate/set-signed \
certificate=@intermediate.cert.pem
|
建立短期憑證角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 建立短期憑證角色(有效期 24 小時)
vault write pki_int/roles/short-lived-cert \
allowed_domains="enterprise.com" \
allow_subdomains=true \
max_ttl="24h" \
ttl="1h" \
key_type="ec" \
key_bits=256 \
require_cn=true \
allow_ip_sans=true \
allow_localhost=true
# 簽發短期憑證
vault write pki_int/issue/short-lived-cert \
common_name="service-api.enterprise.com" \
ttl="1h"
|
自動化短期憑證輪換
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
| #!/bin/bash
# 短期憑證自動輪換腳本
VAULT_ADDR="https://vault.enterprise.com:8200"
CERT_PATH="/etc/ssl/certs/service.crt"
KEY_PATH="/etc/ssl/private/service.key"
SERVICE_NAME="api-service"
RENEWAL_THRESHOLD=600 # 秒
renew_certificate() {
echo "[$(date)] 更新憑證..."
# 從 Vault 取得新憑證
response=$(vault write -format=json pki_int/issue/short-lived-cert \
common_name="${SERVICE_NAME}.enterprise.com" \
ttl="1h")
# 提取憑證和私鑰
echo "$response" | jq -r '.data.certificate' > $CERT_PATH
echo "$response" | jq -r '.data.private_key' > $KEY_PATH
# 設定權限
chmod 644 $CERT_PATH
chmod 600 $KEY_PATH
# 重新載入服務
systemctl reload nginx
echo "[$(date)] 憑證更新完成"
}
check_and_renew() {
# 取得憑證到期時間
expiry=$(openssl x509 -in $CERT_PATH -noout -enddate | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s)
current_epoch=$(date +%s)
remaining=$((expiry_epoch - current_epoch))
echo "[$(date)] 憑證剩餘有效時間: ${remaining} 秒"
if [ $remaining -lt $RENEWAL_THRESHOLD ]; then
renew_certificate
fi
}
# 持續監控
while true; do
check_and_renew
sleep 300 # 每 5 分鐘檢查一次
done
|
mTLS 實作
零信任架構中的 mTLS
mTLS(Mutual TLS)是零信任架構中實現服務間安全通訊的核心技術。在 mTLS 中,通訊雙方都需要提供憑證進行驗證。
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
| mTLS 握手流程
+----------------+ +----------------+
| 客戶端 | | 伺服器 |
+----------------+ +----------------+
| |
| 1. ClientHello |
|----------------------------------->|
| |
| 2. ServerHello + ServerCertificate |
| + CertificateRequest |
|<-----------------------------------|
| |
| 3. ClientCertificate |
| + CertificateVerify |
|----------------------------------->|
| |
| 4. 驗證客戶端憑證 |
| |
| 5. Finished (雙向驗證完成) |
|<---------------------------------->|
| |
| 6. 加密應用資料傳輸 |
|<=================================>|
| |
|
Nginx mTLS 進階設定
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| # /etc/nginx/conf.d/mtls.conf
# 上游服務(也使用 mTLS)
upstream backend_api {
server api-server-1:8443;
server api-server-2:8443;
}
server {
listen 443 ssl http2;
server_name api.enterprise.com;
# 伺服器憑證
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# CA 憑證(用於驗證客戶端)
ssl_client_certificate /etc/nginx/ssl/ca-chain.crt;
# 強制要求客戶端憑證
ssl_verify_client on;
ssl_verify_depth 3;
# TLS 設定
ssl_protocols TLSv1.3;
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256';
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 存取日誌(包含客戶端憑證資訊)
log_format mtls_log '$remote_addr - $ssl_client_s_dn - '
'[$time_local] "$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'ssl_client_verify=$ssl_client_verify';
access_log /var/log/nginx/mtls_access.log mtls_log;
location / {
# 基於客戶端憑證 CN 的存取控制
if ($ssl_client_s_dn_cn !~ "^(service-a|service-b|admin)$") {
return 403 "Unauthorized client\n";
}
# 傳遞客戶端憑證資訊到後端
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_set_header X-SSL-Client-Fingerprint $ssl_client_fingerprint;
# 後端 mTLS 連線
proxy_pass https://backend_api;
proxy_ssl_certificate /etc/nginx/ssl/client.crt;
proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
proxy_ssl_trusted_certificate /etc/nginx/ssl/backend-ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_protocols TLSv1.3;
}
}
|
Kubernetes Ingress mTLS 設定
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
| # Kubernetes Secret 存放 CA 憑證
apiVersion: v1
kind: Secret
metadata:
name: mtls-ca-secret
namespace: production
type: Opaque
data:
ca.crt: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
---
# Nginx Ingress mTLS 設定
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mtls-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-secret: "production/mtls-ca-secret"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "3"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.enterprise.com
secretName: api-tls-secret
rules:
- host: api.enterprise.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
|
使用 curl 測試 mTLS
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 完整的 mTLS 連線測試
curl -v \
--cacert ca-chain.crt \
--cert client.crt \
--key client.key \
https://api.enterprise.com/health
# 驗證憑證鏈
openssl s_client -connect api.enterprise.com:443 \
-CAfile ca-chain.crt \
-cert client.crt \
-key client.key \
-showcerts
|
服務身份認證
SPIFFE/SPIRE 架構
SPIFFE(Secure Production Identity Framework For Everyone)是一個開放標準,用於在動態和異質環境中安全地識別軟體服務。SPIRE 是 SPIFFE 的參考實作。
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
| SPIFFE/SPIRE 架構
+---------------------------------------------------+
| SPIRE Server |
| +---------------------------------------------+ |
| | Registration API | Node/Workload API | |
| +---------------------------------------------+ |
| | Data Store | |
| | (Registration Entries, CA Keys) | |
| +---------------------------------------------+ |
+------------------------+---------------------------+
|
+--------------+--------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| SPIRE Agent | | SPIRE Agent | | SPIRE Agent |
| (Node 1) | | (Node 2) | | (Node 3) |
+----------------+ +----------------+ +----------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| Workload API | | Workload API | | Workload API |
| (Unix Socket) | | (Unix Socket) | | (Unix Socket) |
+----------------+ +----------------+ +----------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| Workload A | | Workload B | | Workload C |
| SPIFFE ID: | | SPIFFE ID: | | SPIFFE ID: |
| spiffe://... | | spiffe://... | | spiffe://... |
+----------------+ +----------------+ +----------------+
|
SPIFFE ID 格式
1
2
3
4
5
6
| SPIFFE ID 格式:
spiffe://trust-domain/path
範例:
spiffe://enterprise.com/ns/production/sa/api-service
spiffe://enterprise.com/region/us-west/workload/payment
|
SPIRE 部署範例
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
| # SPIRE Server ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: spire-server-config
namespace: spire
data:
server.conf: |
server {
bind_address = "0.0.0.0"
bind_port = "8081"
trust_domain = "enterprise.com"
data_dir = "/run/spire/data"
log_level = "INFO"
ca_ttl = "24h"
default_svid_ttl = "1h"
ca_subject {
country = ["TW"]
organization = ["Enterprise"]
common_name = "SPIRE CA"
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "postgres"
connection_string = "host=postgres dbname=spire user=spire password=secret"
}
}
NodeAttestor "k8s_psat" {
plugin_data {
clusters = {
"production" = {
service_account_allow_list = ["spire:spire-agent"]
}
}
}
}
KeyManager "disk" {
plugin_data {
keys_path = "/run/spire/data/keys.json"
}
}
Notifier "k8sbundle" {
plugin_data {
namespace = "spire"
config_map = "trust-bundle"
}
}
}
---
# SPIRE Server Deployment
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: spire-server
namespace: spire
spec:
replicas: 1
selector:
matchLabels:
app: spire-server
serviceName: spire-server
template:
metadata:
labels:
app: spire-server
spec:
serviceAccountName: spire-server
containers:
- name: spire-server
image: ghcr.io/spiffe/spire-server:1.8.0
args:
- -config
- /run/spire/config/server.conf
ports:
- containerPort: 8081
volumeMounts:
- name: spire-config
mountPath: /run/spire/config
- name: spire-data
mountPath: /run/spire/data
volumes:
- name: spire-config
configMap:
name: spire-server-config
- name: spire-data
persistentVolumeClaim:
claimName: spire-data
|
工作負載註冊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 註冊 API 服務工作負載
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://enterprise.com/api-service \
-parentID spiffe://enterprise.com/spire/agent/k8s_psat/production/node1 \
-selector k8s:ns:production \
-selector k8s:sa:api-service \
-ttl 3600
# 註冊資料庫客戶端工作負載
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://enterprise.com/db-client \
-parentID spiffe://enterprise.com/spire/agent/k8s_psat/production/node1 \
-selector k8s:ns:production \
-selector k8s:sa:db-client \
-ttl 3600
# 列出所有註冊條目
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry show
|
應用程式整合範例(Go)
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
| // Go 應用程式整合 SPIFFE
package main
import (
"context"
"log"
"net/http"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/go-spiffe/v2/spiffetls"
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
"github.com/spiffe/go-spiffe/v2/workloadapi"
)
func main() {
ctx := context.Background()
// 連接 SPIRE Agent Workload API
source, err := workloadapi.NewX509Source(ctx)
if err != nil {
log.Fatalf("無法取得 X.509 來源: %v", err)
}
defer source.Close()
// 定義授權的 SPIFFE ID
authorizedID := spiffeid.RequireFromString("spiffe://enterprise.com/api-client")
// 建立 mTLS 伺服器
tlsConfig := tlsconfig.MTLSServerConfig(source, source, tlsconfig.AuthorizeID(authorizedID))
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("已通過 SPIFFE mTLS 驗證!"))
})
log.Println("伺服器啟動於 :8443")
log.Fatal(server.ListenAndServeTLS("", ""))
}
|
應用程式整合範例(Python)
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
| #!/usr/bin/env python3
"""Python 應用程式整合 SPIFFE"""
from pyspiffe.spiffe_id.spiffe_id import SpiffeId
from pyspiffe.workloadapi.default_workload_api_client import DefaultWorkloadApiClient
from pyspiffe.bundle.x509_bundle.x509_bundle_set import X509BundleSet
import ssl
def create_mtls_context():
"""建立 mTLS SSL Context"""
# 連接 SPIRE Agent
client = DefaultWorkloadApiClient()
# 取得 X.509 SVID
x509_svid = client.fetch_x509_svid()
# 取得信任 Bundle
x509_bundles = client.fetch_x509_bundles()
# 建立 SSL Context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.verify_mode = ssl.CERT_REQUIRED
# 載入客戶端憑證
context.load_cert_chain(
certfile=x509_svid.cert_chain_path,
keyfile=x509_svid.private_key_path
)
# 載入 CA 憑證
for bundle in x509_bundles.bundles():
context.load_verify_locations(cafile=bundle.path)
return context
if __name__ == "__main__":
ctx = create_mtls_context()
print("mTLS Context 建立成功")
|
憑證自動化輪換
Cert-Manager 自動化管理
Cert-Manager 是 Kubernetes 中管理憑證生命週期的標準工具:
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
| # 安裝 Cert-Manager
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# 設定 Vault Issuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
path: pki_int/sign/short-lived-cert
server: https://vault.enterprise.com:8200
auth:
kubernetes:
role: cert-manager
mountPath: /v1/auth/kubernetes
serviceAccountRef:
name: cert-manager
---
# 建立短期憑證
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-service-cert
namespace: production
spec:
secretName: api-service-tls
duration: 1h
renewBefore: 15m
issuerRef:
name: vault-issuer
kind: ClusterIssuer
commonName: api-service.production.svc.cluster.local
dnsNames:
- api-service
- api-service.production
- api-service.production.svc
- api-service.production.svc.cluster.local
usages:
- server auth
- client auth
|
自動化輪換監控
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
| # Prometheus 監控規則
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: certificate-alerts
namespace: monitoring
spec:
groups:
- name: certificate.rules
rules:
- alert: CertificateExpiringSoon
expr: |
certmanager_certificate_expiration_timestamp_seconds - time() < 86400
for: 1h
labels:
severity: warning
annotations:
summary: "憑證即將到期"
description: "憑證 {{ $labels.name }} 將在 24 小時內到期"
- alert: CertificateExpired
expr: |
certmanager_certificate_expiration_timestamp_seconds - time() < 0
for: 5m
labels:
severity: critical
annotations:
summary: "憑證已過期"
description: "憑證 {{ $labels.name }} 已經過期"
- alert: CertificateRenewalFailed
expr: |
increase(certmanager_certificate_renewal_timestamp_seconds[1h]) == 0
and certmanager_certificate_expiration_timestamp_seconds - time() < 7200
for: 30m
labels:
severity: critical
annotations:
summary: "憑證更新失敗"
description: "憑證 {{ $labels.name }} 更新失敗,需要人工介入"
|
憑證輪換通知腳本
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
| #!/usr/bin/env python3
"""
憑證輪換監控與通知腳本
"""
import ssl
import socket
import datetime
from typing import Dict, List
import smtplib
from email.mime.text import MIMEText
def check_certificate(hostname: str, port: int = 443) -> Dict:
"""檢查憑證狀態"""
context = ssl.create_default_context()
with socket.create_connection((hostname, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
# 解析到期時間
not_after = datetime.datetime.strptime(
cert['notAfter'], '%b %d %H:%M:%S %Y %Z'
)
# 計算剩餘天數
remaining = (not_after - datetime.datetime.utcnow()).days
return {
'hostname': hostname,
'subject': dict(x[0] for x in cert['subject']),
'issuer': dict(x[0] for x in cert['issuer']),
'not_after': not_after.isoformat(),
'remaining_days': remaining,
'serial_number': cert.get('serialNumber', 'N/A')
}
def send_alert(cert_info: Dict, threshold: int = 7):
"""發送警報通知"""
if cert_info['remaining_days'] <= threshold:
msg = MIMEText(f"""
憑證即將到期警報
主機: {cert_info['hostname']}
主體: {cert_info['subject'].get('commonName', 'N/A')}
發行者: {cert_info['issuer'].get('commonName', 'N/A')}
到期時間: {cert_info['not_after']}
剩餘天數: {cert_info['remaining_days']}
請立即處理憑證更新!
""")
msg['Subject'] = f"[警報] 憑證即將到期: {cert_info['hostname']}"
msg['From'] = 'security@enterprise.com'
msg['To'] = 'ops-team@enterprise.com'
with smtplib.SMTP('smtp.enterprise.com') as server:
server.send_message(msg)
print(f"警報已發送: {cert_info['hostname']}")
def main():
"""主程式"""
hosts = [
'api.enterprise.com',
'auth.enterprise.com',
'app.enterprise.com',
]
for host in hosts:
try:
cert_info = check_certificate(host)
print(f"檢查 {host}: 剩餘 {cert_info['remaining_days']} 天")
send_alert(cert_info, threshold=7)
except Exception as e:
print(f"檢查 {host} 失敗: {e}")
if __name__ == '__main__':
main()
|
Systemd Timer 排程自動輪換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # /etc/systemd/system/cert-renewal.service
[Unit]
Description=Certificate Renewal Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/renew-certificates.sh
User=root
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
|
1
2
3
4
5
6
7
8
9
10
11
| # /etc/systemd/system/cert-renewal.timer
[Unit]
Description=Run certificate renewal every hour
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
|
1
2
3
4
5
6
| # 啟用排程
sudo systemctl enable cert-renewal.timer
sudo systemctl start cert-renewal.timer
# 檢查狀態
sudo systemctl status cert-renewal.timer
|
實施建議與案例
零信任憑證策略實施路線圖
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
| +---------------------------------------------------------------+
| 實施路線圖 |
+---------------------------------------------------------------+
| |
| 階段 1: 評估與規劃 (1-2 個月) |
| +----------------------------------------------------------+ |
| | - 盤點現有憑證和 PKI 基礎架構 | |
| | - 識別關鍵服務和敏感資料流 | |
| | - 定義安全需求和合規要求 | |
| | - 制定遷移計劃和時間表 | |
| +----------------------------------------------------------+ |
| | |
| v |
| 階段 2: 基礎架構建設 (2-3 個月) |
| +----------------------------------------------------------+ |
| | - 部署企業 PKI 或採用雲端 CA 服務 | |
| | - 設定 HashiCorp Vault 或類似的密鑰管理系統 | |
| | - 部署 SPIRE 或其他服務身份管理系統 | |
| | - 建立憑證管理自動化流程 | |
| +----------------------------------------------------------+ |
| | |
| v |
| 階段 3: 試點導入 (1-2 個月) |
| +----------------------------------------------------------+ |
| | - 選擇非關鍵服務進行試點 | |
| | - 部署裝置憑證管理 | |
| | - 實施 mTLS 於試點服務 | |
| | - 驗證短期憑證輪換機制 | |
| +----------------------------------------------------------+ |
| | |
| v |
| 階段 4: 全面部署 (3-6 個月) |
| +----------------------------------------------------------+ |
| | - 逐步推廣至所有服務 | |
| | - 強制執行 mTLS 政策 | |
| | - 淘汰長期憑證 | |
| | - 完善監控和告警機制 | |
| +----------------------------------------------------------+ |
| | |
| v |
| 階段 5: 持續優化 (持續進行) |
| +----------------------------------------------------------+ |
| | - 定期審計和合規檢查 | |
| | - 調整憑證策略和有效期 | |
| | - 更新安全政策和程序 | |
| | - 教育訓練和意識提升 | |
| +----------------------------------------------------------+ |
+---------------------------------------------------------------+
|
企業案例:金融服務公司
以下是一個金融服務公司實施零信任憑證策略的完整案例:
架構設計
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
55
56
57
| # 零信任憑證架構設計
infrastructure:
pki:
root_ca:
provider: "AWS Private CA"
key_algorithm: "RSA_4096"
validity: "10 years"
storage: "HSM"
intermediate_cas:
- name: "Device CA"
validity: "5 years"
purpose: "裝置憑證簽發"
- name: "Service CA"
validity: "3 years"
purpose: "服務憑證簽發"
- name: "User CA"
validity: "3 years"
purpose: "使用者憑證簽發"
certificate_policy:
device_certificates:
validity: "90 days"
key_size: 2048
renewal_threshold: "20%"
distribution: "MDM/UEM"
service_certificates:
validity: "1 hour"
key_type: "ECDSA P-256"
renewal: "automatic"
issuer: "Vault PKI"
user_certificates:
validity: "8 hours"
key_type: "ECDSA P-256"
renewal: "on login"
issuer: "SPIRE"
mtls_zones:
- name: "External API Gateway"
client_verification: "required"
allowed_clients:
- "partner-services"
- "mobile-apps"
- name: "Internal Service Mesh"
client_verification: "required"
allowed_clients:
- "all-registered-services"
- name: "Database Access"
client_verification: "required"
allowed_clients:
- "authorized-services"
|
實施效果
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
| +-----------------------------------------------+
| 實施成效報告 |
+-----------------------------------------------+
| |
| 安全改善: |
| +----------------------------------------+ |
| | - 消除長期憑證洩露風險 | |
| | - 實現完整的服務間 mTLS 加密 | |
| | - 達成 100% 裝置憑證覆蓋率 | |
| | - 憑證相關安全事件減少 95% | |
| +----------------------------------------+ |
| |
| 運維效率: |
| +----------------------------------------+ |
| | - 憑證管理工時減少 80% | |
| | - 憑證過期事件降至零 | |
| | - 自動化輪換覆蓋率 100% | |
| | - 部署時間從數小時縮短至數分鐘 | |
| +----------------------------------------+ |
| |
| 合規達成: |
| +----------------------------------------+ |
| | - 符合 PCI DSS 加密要求 | |
| | - 滿足金管會資安規範 | |
| | - 通過 SOC 2 Type II 審計 | |
| | - 達成 ISO 27001 憑證管理要求 | |
| +----------------------------------------+ |
+-----------------------------------------------+
|
最佳實踐清單
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
| +-----------------------------------------------+
| 零信任憑證最佳實踐 |
+-----------------------------------------------+
1. 憑證有效期
[v] 裝置憑證: 90 天或更短
[v] 服務憑證: 24 小時或更短
[v] 使用者憑證: 工作時間內有效
[v] 避免使用超過 1 年的憑證
2. 金鑰管理
[v] 使用 HSM 保護根 CA 金鑰
[v] 實施金鑰輪換策略
[v] 採用 ECDSA P-256 或以上
[v] 禁止導出私鑰
3. 自動化
[v] 自動化憑證簽發和更新
[v] 實施自動化健康檢查
[v] 設定自動告警機制
[v] 自動化撤銷流程
4. 監控與審計
[v] 記錄所有憑證操作
[v] 監控憑證到期時間
[v] 追蹤憑證使用情況
[v] 定期合規審計
5. 存取控制
[v] 實施最小權限原則
[v] 基於身份的存取控制
[v] 強制 mTLS 於所有服務
[v] 隔離敏感資料存取
6. 災難復原
[v] 備份 CA 金鑰和設定
[v] 文件化復原程序
[v] 定期測試復原流程
[v] 維護離線根 CA 備份
|
常見問題與解決方案
| 問題 | 原因 | 解決方案 |
|---|
| 憑證輪換失敗 | Vault 連線問題 | 實施健康檢查和自動重試 |
| mTLS 連線逾時 | 憑證驗證延遲 | 啟用 OCSP Stapling |
| 服務啟動失敗 | 憑證尚未就緒 | 實施 Init Container 等待機制 |
| 裝置無法存取 | 憑證過期 | 設定更早的更新閾值 |
| 審計日誌遺失 | 儲存空間不足 | 實施日誌輪換和歸檔 |
總結
零信任網路存取憑證策略是現代企業安全架構的核心組成部分。透過實施短期憑證、mTLS、服務身份認證和自動化管理,組織可以顯著提升其安全態勢,同時降低運維負擔。
關鍵要點回顧:
- 零信任原則:永不信任,始終驗證 - 憑證是實現這一原則的關鍵技術
- 短期憑證:大幅降低憑證洩露風險,簡化撤銷流程
- mTLS:確保服務間通訊的雙向驗證和加密
- 服務身份:使用 SPIFFE/SPIRE 建立可靠的工作負載身份
- 自動化管理:透過 Cert-Manager 和 Vault 實現憑證生命週期自動化
成功實施零信任憑證策略需要周詳的規劃、適當的工具選擇,以及持續的優化和監控。建議從試點專案開始,逐步擴展至整個組織,確保平穩過渡並最大化安全效益。
參考資料