SVID 三层配置 · 逐项实战手册(Step-by-Step,含真实 A/B 与负向)
配套 SVID.md。这里把每个配置项到底影响 SVID 的什么,用能亲手跑的正向/负向/AB 实验一条条钉死。 所有
实测段落是在本 lab 真跑出来的输出(server VM192.168.71.159、agent VM192.168.71.126、K3S192.168.71.3)。
0. 实验沙箱与统一观测口径
为不污染文档里那两条正式 entry(/vm/web、/ns/demo/sa/svc),实验都在一条沙箱条目 /lab/exp 上做,挂在 VM agent 下、默认用 uid 1002(用户 other)——因为 uid 1002 平时不匹配任何 entry,观测最干净。
两个小工具(已在仓库 demos/,也已放到对应 VM 的 /opt/spire/):
| 工具 | 在哪跑 | 作用 |
|---|---|---|
exp-entry.sh "<selectors>" "<dns>" "<ttl>" | server | 把 /lab/exp 重置成给定形态(先删后建) |
observe-svid.sh <uid> | VM agent | 以该 uid 取一次 SVID,打印 URI/Subject/DNS/NotAfter/公钥类型;取不到就报"负向命中" |
# server 上(root),设 VM agent 的 SPIFFE ID 为 parent:
export EXP_PARENT="spiffe://spire.qstarstar.com/spire/agent/join_token/049212fc-…" # spire-server agent list 里查
sh /opt/spire/exp-entry.sh "unix:uid:1002" "" 3600 # 建基线
# agent 上(root),观测:
sh /opt/spire/observe-svid.sh 1002
# ✔ URI SAN : URI:spiffe://spire.qstarstar.com/lab/exp
# Subject : C = US, O = SPIRE
# NotAfter: … (+1h) PubKey: id-ecPublicKey (256bit)改 entry 是动态的(agent 几秒内自动同步,无需重启);改 server.conf / agent.conf 要重启对应进程才生效。下面每条会标注。
通用查看命令:
spire-server entry show -socketPath /tmp/spire-server/private/api.sock # 看所有 entry
spire-server agent list -socketPath /tmp/spire-server/private/api.sock # 看 agent 身份/认证方式
openssl x509 -in svid.pem -noout -text # 解 X.509 全字段层① server.conf(全局)—— 定 CA 与"默认/上限",改后需 systemctl restart spire-server
1.1 trust_domain —— 信任域(所有身份的根)
- 影响什么:所有 SPIFFE ID 的前缀
spiffe://<trust_domain>/…;CA 证书的 URI SAN;换域=换一整套信任根。 - 怎么查看:
openssl x509 -in captured/trust-bundle.pem -noout -ext subjectAltName→URI:spiffe://spire.qstarstar.com。 - 实验:改它是破坏性的(旧 SVID/agent 全失效,要清库重认证),本 lab 不实跑。预期:改成
example.org重启+清data/后,新签的一切都变spiffe://example.org/…。
1.2 ca_ttl —— CA 寿命,封顶所有下游 SVID 【实测·最重要】
- 影响什么:CA 证书自身寿命;并且 SVID 到期时间被"CA 到期"死死封顶——
SVID TTL ≤ CA 剩余寿命。 - AB / 负向实验:给
/lab/exp一个超过 CA 剩余寿命的-x509SVIDTTL(48h),看它不会真活 48h。
参考:CA 到期 = Jul 10 15:31 GMT(ca_ttl=24h);实验时 now = Jul 9 17:06 GMT
── -x509SVIDTTL 3600 (1h) → NotAfter Jul 9 18:06:35 (now+1h,正常)
── -x509SVIDTTL 300 (5min) → NotAfter Jul 9 17:11:45 (now+5min,正常)
── -x509SVIDTTL 172800(48h) → NotAfter Jul 10 15:31:04 ← 不是 +48h!被 CA 到期封顶结论:要 SVID 能签长,先得
ca_ttl够长。这是排查"我 TTL 配了很大却没生效"的头号原因。
1.3 default_x509_svid_ttl / default_jwt_svid_ttl —— 默认 TTL(entry 没配时用)【实测】
- 影响什么:entry 没写
-x509SVIDTTL/-jwtSVIDTTL时,SVID 用这个默认值。 - AB 实验:建一条不带
-x509SVIDTTL的/lab/exp,改 server 默认值,看它跟着变。
default_x509_svid_ttl = "1h" 时: entry 不写 TTL → NotAfter = now+1h (now 17:11 → 18:11)
改成 "10m" 重启后: entry 不写 TTL → NotAfter = now+10m (now 17:12 → 17:22)- JWT 默认:抓到的 JWT
exp-iat = 1783617389-1783617089 = 300s=default_jwt_svid_ttl默认 5m。
1.4 jwt_issuer —— 给 JWT-SVID 加 iss claim 【实测】
- 影响什么:JWT-SVID payload 里是否有
iss(OIDC 场景常需要)。对 X.509 无影响。 - AB 实验(注意 JWT 按
(spiffeID,audience)缓存,验证要换新 audience 绕缓存):
未配 jwt_issuer: {"aud":["test-aud"], "sub":"spiffe://…/lab/exp", "exp":…, "iat":…} ← 无 iss
配 jwt_issuer="https://spire.qstarstar.com" 重启,换新 audience 现签:
{"aud":["aud-withiss"], "iss":"https://spire.qstarstar.com", "sub":"…"} ← 出现 iss顺带证明:同 audience 第二次取 JWT 是缓存命中(exp 一模一样);换 audience 才会回源现签。
1.5 ca_subject —— CA 证书主体(= 每张 SVID 的 Issuer)
- 影响什么:CA 证书的 Subject,也就是所有 SVID 的 Issuer 字段。
- 怎么查看:
openssl x509 -in captured/vm-workload-svid.pem -noout -issuer→C=CN, O=qstarstar-homelab, CN=spire.qstarstar.com(正是本 lab 配的)。 - 实验:改它要 CA 重签(清
data/重启),本 lab 不实跑。预期:新 CA 生效后,新 SVID 的 Issuer 跟着变。
1.6 ca_key_type —— CA 签名密钥类型
- 影响什么:CA 私钥算法(
ec-p256/ec-p384/rsa-2048/rsa-4096),进而 SVID 的签名算法。 - 怎么查看:
openssl x509 -in <svid> -noout -text | grep "Signature Algorithm"(本 lab 默认ecdsa-with-SHA256)。 - 实验:同样需 CA 重生成,不实跑。预期:改
rsa-2048重启清库后,签名算法变sha256WithRSAEncryption。
层② 注册条目 entry(spire-server entry create)—— 定单个身份的形态,动态生效
2.1 -spiffeID —— 这条身份的名字(写进 URI SAN)
- 影响什么:签出的 SVID 唯一 URI SAN = 这个值。工作负载改不了它,只能拿到 entry 规定的这个。
- 查看/实验:
exp-entry.sh固定用/lab/exp;把脚本里 spiffeID 换成/lab/foo再observe,URI SAN 立刻变…/lab/foo。
2.2 -selector(可重复)—— 决定"谁能拿到",多个是 AND 【实测·灵魂】
- 影响什么:工作负载实测 selector 必须 ⊇ entry 的所有 selector(
entry.Selectors ⊆ workload.Selectors)才发。这不是形状、是准入。 - 负向实验(单 selector):
selector = unix:uid:1002 → uid 1002 取: ✔ 拿到 /lab/exp
selector = unix:uid:1003 → uid 1002 取: ✘ 无 SVID(uid 不匹配,负向命中)- 负向实验(多 selector = AND,必须全中)(用户 other = uid 1002 / gid 1002):
{unix:uid:1002, unix:gid:1002} → uid 1002 取: ✔ 拿到(两个都中)
{unix:uid:1002, unix:gid:9999} → uid 1002 取: ✘ 无 SVID(gid 不中 → AND 失败)2.3 unix:sha256 —— 用二进制指纹当身份门槛 【实测·最震撼】
- 影响什么:selector 可以是调用二进制的 SHA-256。身份门槛 = "你必须是这段字节",改一个字节就换了个"人"。
- 正向 + 负向实验:
/tmp/sa-good sha256=8ef49b0b… → entry selector 用它 → 用 /tmp/sa-good 取: ✔ 拿到 /lab/exp
cp sa-good sa-bad && 追加 1 字节 sha256=2df16746… → 用 /tmp/sa-bad 取: ✘ 无 SVID(指纹变了)这是"主体不能自证身份、身份由平台能证明的事实决定"的最强演示——连"你是不是那段代码"都由内核实测,不由你说。
2.4 -x509SVIDTTL / -jwtSVIDTTL —— 覆盖全局默认 TTL 【实测】
- 见 1.2/1.3 的真实数据:
3600→+1h、300→+5min、172800→被 CA 封顶。注意仍受ca_ttl上限。
2.5 -dns(可重复)—— 给 X509-SVID 加 DNS SAN;第一个还当 CN 【实测】
- 影响什么:X.509 的
DNS SAN;且CN = 第一个 -dns(没配则无 CN)。对 JWT 无影响。 - AB 实验:
无 -dns → Subject: C=US, O=SPIRE DNS SAN: (空)
-dns web.exp.lab → Subject: C=US, O=SPIRE, CN=web.exp.lab DNS SAN: DNS:web.exp.lab多个
-dns a -dns b:CN=a,SAN 里 a、b 都在。用途:让传统只认 DNS/CN 的 TLS 客户端也能校验这张 SVID。
2.6 -parentID —— 这条身份由哪个 agent 代领 【实测(建 entry 时已用)】
- 影响什么:只有 SPIFFE ID = 该 parentID 的 agent 才会同步/下发这条 entry。跨 agent 拿不到(防越权,
LookupAuthorizedEntries)。 - 实证:
/vm/web的 parent 是 VM agent,/ns/demo/sa/svc的 parent 是 K3S agent——所以 K3S 里永远取不到/vm/web。可用spire-server entry show看每条的 Parent ID。
2.7 其余 entry flag(影响 + 怎么验,按需实验)
| flag | 影响什么 | 怎么查看 / 预期 |
|---|---|---|
-federatesWith <td> | 下发 SVID 时附带该外域的 bundle(跨信任域 mTLS) | 需两套 trust domain + bundle set;fetch x509 结果里会多出联邦 bundle |
-hint | 同一 SPIFFE ID 有多条 entry 时给消费者消歧的标签 | fetch x509 的 SVID 上带 hint;不影响证书字段 |
-admin | 该 SPIFFE ID 可调 server 管理 API | 用该 SVID 连 -socketPath 管理端不被拒 |
-downstream | 标记为下游 SPIRE server,可领中间 CA(Nested) | NewDownstreamX509CA 能签出 CA:TRUE 的中间 CA |
-storeSVID | SVID 经 SVIDStore 插件落地(不走 Workload API) | 结果写到配置的 store(如云 secret),fetch 取不到 |
-node | 该条作用于节点(node alias),而非工作负载 | 用于按节点 selector 归组 agent |
层③ agent.conf(agent 全局)—— 定本地私钥与轮换,改后需 systemctl restart spire-agent
3.1 workload_x509_svid_key_type —— workload 私钥类型 【实测】
- 影响什么:agent 为工作负载本地生成的私钥算法 → SVID 的公钥类型。身份(URI)不变,密钥材料变。
- AB 实验:
默认(ec-p256) → PubKey: id-ecPublicKey (256bit)
改 "rsa-2048" 重启 agent → PubKey: rsaEncryption (2048bit)
还原重启 → PubKey: id-ecPublicKey (256bit)私钥永远在 agent 本地生成、永不出机;CSR 里甚至不含 SPIFFE ID(身份由 server 定)。
3.2 其余 agent 关键项
| 配置 | 影响什么 | 怎么查看 |
|---|---|---|
socket_path | Workload API 的 Unix socket 路径(工作负载来这取 SVID) | 本 lab VM=/run/spire/agent.sock,K3S=/run/spire/sockets/agent.sock |
trust_bundle_path | 启动时的信任根(先信任 server,才能安全 attest) | 指向 captured/trust-bundle.pem |
WorkloadAttestor "unix" { discover_workload_path } | 是否额外产出 unix:path/unix:sha256 selector | 关掉则 2.3 的 sha256 实验无法做 |
availability_target | SVID 轮换提前量(高可用) | 影响轮换时机,不改证书字段 |
4. 速查表:改哪、影响啥、怎么看、要不要重启
| 配置项 | 层 | 影响 SVID 的… | 怎么查看 | 生效方式 |
|---|---|---|---|---|
trust_domain | server | URI 前缀 / 信任根 | openssl … -ext subjectAltName | 重启(破坏性) |
ca_ttl | server | SVID 到期上限 | 对比 SVID 与 CA 的 NotAfter | 重启 |
default_x509/jwt_svid_ttl | server | 默认 TTL | openssl … -enddate / JWT exp-iat | 重启 |
jwt_issuer | server | JWT 的 iss | 解 JWT payload(换 audience) | 重启 |
ca_subject | server | Issuer | openssl … -issuer | 重启(重签 CA) |
ca_key_type | server | 签名算法 | openssl … Signature Algorithm | 重启(重签 CA) |
-spiffeID | entry | URI SAN(名字) | openssl … -ext subjectAltName | 动态 |
-selector(多=AND) | entry | 谁能拿到(准入) | observe 有/无 | 动态 |
unix:sha256 | entry | 二进制指纹准入 | 换二进制 observe | 动态 |
-x509SVIDTTL | entry | 该身份 TTL(≤ca_ttl) | openssl … -enddate | 动态 |
-dns | entry | DNS SAN + CN | openssl … -ext subjectAltName -subject | 动态 |
-parentID | entry | 哪个 agent 能领 | spire-server entry show | 动态 |
workload_x509_svid_key_type | agent | 公钥算法 | openssl … -text(Public Key) | 重启 agent |
5. 收尾
本手册所有实验做完已还原到 SVID.md 记录的干净态(default_x509_svid_ttl=1h、无 jwt_issuer、2 条正式 entry、agent 默认 EC)。自己做完实验后:
# 删沙箱 entry(server 上)
spire-server entry show -socketPath $SOCK -spiffeID spiffe://spire.qstarstar.com/lab/exp # 取 Entry ID
spire-server entry delete -socketPath $SOCK -entryID <id>
# 若改过 server.conf / agent.conf,改回后 systemctl restart 对应服务整套 lab 清除:bash teardown.sh。