第十九章:SPIFFE — 通用工作负载身份框架
“人有身份证,机器也需要身份证。SPIFFE 就是机器世界的身份证标准。”
mindmap
root((SPIFFE))
SPIFFE ID
trust domain
path
格式规范
SVID
X.509-SVID
JWT-SVID
短生命周期
Trust Bundle
信任根
联邦信任
Workload API
Unix Socket
gRPC
自动轮换
设计哲学
平台无关
自动化
零配置
19.1 工作负载身份的挑战
在云原生时代,机器身份(工作负载身份)面临独特的挑战:
挑战 |
描述 |
|---|---|
数量级 |
工作负载数量远超人类用户(数十万级) |
动态性 |
容器随时创建和销毁,IP 地址不固定 |
异构性 |
跨 Kubernetes、VM、裸机、多云 |
凭证管理 |
手动管理证书/密钥不可扩展 |
信任建立 |
如何证明”这个进程确实是它声称的服务” |
传统方式的问题:
┌──────────┐ ┌──────────┐
│ Service A │───▶│ Service B │
│ │ │ │
│ 如何证明 │ │ 如何验证 │
│ "我是A"? │ │ "它是A"? │
└──────────┘ └──────────┘
方案1: 硬编码密钥 → 泄露风险、无法轮换
方案2: 环境变量 → 容易被窃取
方案3: K8s Secret → 仅限 K8s、明文存储
方案4: Vault → 需要先认证才能获取密钥(鸡生蛋问题)
方案5: SPIFFE ✅ → 自动化、短生命周期、平台无关
19.2 SPIFFE 是什么
SPIFFE(Secure Production Identity Framework For Everyone)是一套开源标准,为工作负载提供统一的身份框架。它是 CNCF 毕业项目。
SPIFFE 定义了三个核心规范:
SPIFFE ID:工作负载的唯一身份标识
SVID:可验证的身份文档
Workload API:工作负载获取身份的接口
19.3 SPIFFE ID
SPIFFE ID 是一个 URI,格式为:
spiffe://trust-domain/path
示例:
spiffe://example.org/ns/production/sa/web-server
spiffe://example.org/region/us-east/service/payment
spiffe://bank.com/department/trading/app/risk-engine
组成部分 |
说明 |
示例 |
|---|---|---|
scheme |
固定为 spiffe |
spiffe:// |
trust domain |
信任域,类似 DNS 域名 |
example.org |
path |
工作负载路径,自由定义 |
/ns/prod/sa/web |
Trust Domain 设计原则
推荐的 Trust Domain 命名:
✅ prod.example.com — 生产环境
✅ staging.example.com — 预发布环境
✅ cluster1.example.com — 按集群划分
❌ example.com — 太宽泛
❌ my-app — 不像域名
路径设计建议:
/ns/{namespace}/sa/{service-account} — Kubernetes 风格
/region/{region}/service/{service-name} — 按区域划分
/env/{environment}/app/{app-name} — 按环境划分
19.4 SVID — 可验证身份文档
SVID(SPIFFE Verifiable Identity Document)是工作负载的”身份证”,有两种形式:
X.509-SVID
最常用的形式,基于 X.509 证书:
Certificate:
Subject: O=SPIRE
Subject Alternative Name:
URI: spiffe://example.org/ns/prod/sa/web-server ← SPIFFE ID
Issuer: O=SPIRE, CN=SPIRE CA
Validity:
Not Before: 2024-01-01 00:00:00 UTC
Not After: 2024-01-01 01:00:00 UTC ← 1小时有效期!
Public Key Algorithm: ECDSA P-256
关键特性:
SPIFFE ID 存储在证书的 SAN URI 字段
短生命周期(通常 1 小时),自动轮换
用于 mTLS 双向认证
JWT-SVID
基于 JWT 的身份文档,适用于 L7 场景:
{
"alg": "ES256",
"kid": "key-1",
"typ": "JWT"
}
.
{
"sub": "spiffe://example.org/ns/prod/sa/web-server",
"aud": ["spiffe://example.org/ns/prod/sa/api-server"],
"exp": 1709510400,
"iat": 1709506800
}
.
[signature]
X.509-SVID vs JWT-SVID
维度 |
X.509-SVID |
JWT-SVID |
|---|---|---|
用途 |
mTLS(L4) |
HTTP Header(L7) |
验证方式 |
TLS 握手 |
Token 验证 |
受众绑定 |
❌ |
✅(aud claim) |
代理穿透 |
✅ |
✅ |
性能 |
高(TLS 层) |
中(应用层) |
适用场景 |
服务间直连 |
API Gateway、L7 代理 |
19.5 Trust Bundle
Trust Bundle 是信任域的根证书集合,用于验证 SVID:
Trust Domain: example.org
Trust Bundle: [Root CA Certificate]
验证流程:
1. Service A 出示 X.509-SVID
2. Service B 用 example.org 的 Trust Bundle 验证证书链
3. 验证 SAN 中的 SPIFFE ID
4. 信任建立 ✅
19.6 Workload API
Workload API 是工作负载获取 SVID 的标准接口:
┌──────────────┐ Unix Domain Socket ┌──────────────┐
│ 工作负载 │◀────────────────────────▶│ SPIRE Agent │
│ (应用进程) │ /run/spire/sockets/ │ │
│ │ agent.sock │ 自动签发 │
│ 获取 SVID │ │ 自动轮换 │
│ 获取 Bundle │ │ 缓存管理 │
└──────────────┘ └──────────────┘
// Go 示例:使用 Workload API 获取 SVID
import "github.com/spiffe/go-spiffe/v2/workloadapi"
ctx := context.Background()
source, err := workloadapi.NewX509Source(ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr("unix:///run/spire/sockets/agent.sock"),
),
)
defer source.Close()
// 获取 SVID
svid, err := source.GetX509SVID()
fmt.Printf("SPIFFE ID: %s\n", svid.ID)
fmt.Printf("证书有效期: %s\n", svid.Certificates[0].NotAfter)
// 用 SVID 建立 mTLS 连接
tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeID(
spiffeid.RequireID(spiffeid.Must("example.org", "/ns/prod/sa/api-server")),
))
19.7 SPIFFE 与传统方案对比
方案 |
平台无关 |
自动轮换 |
短生命周期 |
零配置 |
标准化 |
|---|---|---|---|---|---|
K8s ServiceAccount |
❌ |
❌ |
❌ |
✅ |
❌ |
AWS IAM Role |
❌ |
✅ |
✅ |
❌ |
❌ |
手动 mTLS |
✅ |
❌ |
❌ |
❌ |
❌ |
HashiCorp Vault |
✅ |
✅ |
✅ |
❌ |
❌ |
SPIFFE/SPIRE |
✅ |
✅ |
✅ |
✅ |
✅ |
19.8 小结
SPIFFE 是工作负载身份的开放标准(CNCF 毕业项目)
SPIFFE ID 格式:
spiffe://trust-domain/pathSVID 有两种形式:X.509-SVID(mTLS)和 JWT-SVID(L7)
短生命周期(1小时)+ 自动轮换 = 大幅降低凭证泄露风险
Workload API 通过 Unix Socket 提供零配置的身份获取
SPIFFE 是平台无关的,可以跨 K8s、VM、多云使用