Spring Cloud Vault 客户端通过 VaultTemplate 或 VaultPropertySource 访问 Vault 时,传输层通常切换为 HTTPS。HTTPS 带来的不仅是加密通道,还有证书校验、主机名验证、双向认证等一连串配置细节。如果不理解这些参数的生效范围,很容易在连接建立阶段卡在 SSL 握手,或者在证书轮换后突然出现鉴权失败。

一、先理清客户端 SSL 的信任模型
Vault 默认可以监听纯 HTTP 的 8200 端口,但生产环境通常会通过 listener "tcp" 配置启用 HTTPS,并绑定由 CA 签发的服务端证书。客户端发起请求前,需要先验证服务端证书是否可信,这一步依赖信任库。信任库本质上是一个包含受信任 CA 证书或服务端证书的文件,常见格式为 JKS 或 PKCS12。对于 Java 程序来说,Spring Cloud Vault 最终会把配置转换成 SSLContext,它从信任库读取可信任的证书链,再与服务端返回的证书链做比对。
如果客户端还启用了双向 TLS,也就是 Vault 的 cert 认证方式,那么客户端不仅要验证服务端,还必须出示自己的客户端证书。此时客户端需要配置密钥库,密钥库中存放客户端私钥和证书链。信任库解决的是“我是否信任对方”,密钥库解决的是“我如何向对方证明我是谁”。这两类文件不能混用:把所有证书都塞进一个文件虽然能跑通部分场景,但一旦证书别名重复、私钥权限混乱,排查会非常困难。
Spring Cloud Vault 的 SSL 配置主要围绕 VaultProperties.Ssl 展开,对应的配置前缀通常是 spring.cloud.vault.ssl。在旧版本的 Spring Cloud 体系中,这些配置通常放在 bootstrap.yml 中,因为 Vault 配置源需要在应用上下文启动早期完成连接。较新的版本如果启用了 bootstrap 依赖,仍然保留这种加载方式;如果使用配置中心拆分,也可以放在 application.yml 或环境变量中。核心属性包括信任库路径、信任库密码、密钥库路径、密钥库密码、已启用协议列表、主机名验证算法等。
二、用 keytool 准备证书文件
在配置 Spring Cloud Vault 客户端之前,需要先把证书文件准备好。通常的做法是:生成一个 CA 证书,由 CA 签发 Vault 服务端证书;如果要做双向 TLS,还需签发客户端证书。下面的命令演示了如何生成服务端密钥库,并把客户端需要的信任证书导出来。
# 1. 生成 CA 私钥和证书 keytool -genkeypair -alias vault-ca \ -keyalg RSA -keysize 2048 -validity 3650 \ -dname "CN=vault-ca, OU=security, O=example, L=beijing, ST=beijing, C=CN" \ -ext BasicConstraints=ca:true \ -keystore ca.p12 -storetype PKCS12 -storepass changeit # 2. 导出 CA 证书,后续导入到客户端的信任库 keytool -exportcert -alias vault-ca \ -keystore ca.p12 -storetype PKCS12 -storepass changeit \ -file ca.crt # 3. 生成 Vault 服务端密钥库,并包含 SAN 扩展 keytool -genkeypair -alias vault-server \ -keyalg RSA -keysize 2048 -validity 825 \ -dname "CN=vault.ipipp.com, OU=ops, O=example, L=beijing, ST=beijing, C=CN" \ -ext SAN=dns:vault.ipipp.com,dns:localhost,ip:127.0.0.1 \ -keystore server.p12 -storetype PKCS12 -storepass changeit # 4. 生成证书签名请求 keytool -certreq -alias vault-server \ -keystore server.p12 -storetype PKCS12 -storepass changeit \ -file server.csr # 5. 用 CA 给服务端证书签名 keytool -gencert -alias vault-ca \ -keystore ca.p12 -storetype PKCS12 -storepass changeit \ -infile server.csr -outfile server.crt \ -ext SAN=dns:vault.ipipp.com,dns:localhost,ip:127.0.0.1 \ -validity 825 # 6. 将 CA 证书和服务端证书链导入服务端密钥库 keytool -importcert -alias vault-ca \ -keystore server.p12 -storetype PKCS12 -storepass changeit \ -file ca.crt -noprompt keytool -importcert -alias vault-server \ -keystore server.p12 -storetype PKCS12 -storepass changeit \ -file server.crt -noprompt
执行完成后,ca.crt 就是客户端需要信任的 CA 证书,server.p12 是 Vault 服务端使用的密钥库。这里必须关注 SAN 扩展。如果客户端连接地址使用 https://vault.ipipp.com:8200,但服务端证书只写了 CN=vault.ipipp.com 而没有 SAN,Java 的 HTTPS 客户端会抛出 No subject alternative names present 错误。现代浏览器和 Java 都优先校验 SAN,因此生成证书时要把所有可能访问 Vault 的域名、IP 都写入 SAN。
对于只需要单向 TLS 的场景,客户端配置里只需要 trust-store 指向包含 CA 证书的信任库即可。可以创建一个仅含 ca.crt 的信任库,命令如下。
keytool -importcert -alias vault-ca \ -keystore client-truststore.p12 -storetype PKCS12 -storepass changeit \ -file ca.crt -noprompt
如果使用 JKS 格式,只需要把 -storetype PKCS12 改成 -storetype JKS,文件后缀通常也改为 .jks。但要注意,Java 11 以后的默认密钥库类型已经是 PKCS12,因此很多环境下写 PKCS12 会更省心。如果客户端还配置了 key-store,则必须保证该文件里既有私钥又有完整的证书链,而不是只导入客户端公钥证书。
三、在配置文件中启用 SSL 参数
Spring Cloud Vault 的 SSL 配置既可以通过 bootstrap.yml 静态写入,也可以通过环境变量或系统属性动态覆盖。下面是一个典型的单向 TLS 配置示例,客户端只验证 Vault 服务端证书,不需要提交自己的客户端证书。
spring:
cloud:
vault:
uri: https://vault.ipipp.com:8200
scheme: https
connection-timeout: 5000
read-timeout: 15000
ssl:
trust-store: file:/etc/vault/client-truststore.p12
trust-store-password: changeit
trust-store-type: PKCS12
enabled-protocols:
- TLSv1.2
- TLSv1.3
endpoint-identification-algorithm: HTTPS
这里 uri 使用 https 并不会自动触发所有 SSL 属性加载,但 scheme: https 明确了传输协议。真正让客户端找到信任库的是 spring.cloud.vault.ssl.trust-store。如果路径使用 file: 前缀,Spring 会把它当作文件系统路径;如果不加前缀,可能被类路径资源解析,导致文件明明存在却报找不到。trust-store-type 默认值受 JVM 影响,建议显式指定 PKCS12,避免在 Java 8 和 Java 11 之间迁移时出现密钥库类型不一致的问题。
enabled-protocols 可以限制客户端只使用 TLSv1.2 和 TLSv1.3,防止老旧的 TLSv1 或 TLSv1.1 被启用。通常服务端禁用旧协议后,客户端如果不设置该属性也可能正常协商,但为了安全审计清晰,建议显式声明。endpoint-identification-algorithm 设置为 HTTPS 时,Java 会校验服务端证书的 SAN 是否与请求的主机名一致;如果设置为空字符串,则关闭主机名校验,但生产环境不要这样做。
如果 Vault 服务端使用自签名证书,并且只把服务端证书本身导入信任库,也能完成校验,但这种做法没有把 CA 和叶子证书分离,证书轮换时要重新分发客户端文件。更推荐将 CA 证书导入信任库,这样服务端换发新证书时,只要仍由同一个 CA 签发,客户端信任库不需要变化。Spring Cloud Vault 没有单独提供 ssl.trust-certificates 这类直接引用 PEM 文件的属性,所以通常需要先用 keytool 把 PEM 转成 PKCS12 或 JKS。
四、双向 TLS 与 Vault 的 cert 认证
双向 TLS 在 Vault 中通常与 cert 认证方法配合使用。客户端除了配置信任库,还要在 spring.cloud.vault.ssl.key-store 中指定自己的私钥和证书。Vault 服务端配置 cert 认证方法后,会从客户端证书中读取主体信息,根据角色策略生成一个 Vault token。此时 SSL 层完成的不仅是传输加密,还承担了身份认证职责。
spring:
cloud:
vault:
uri: https://vault.ipipp.com:8200
scheme: https
authentication: CERT
ssl:
trust-store: file:/etc/vault/client-truststore.p12
trust-store-password: changeit
trust-store-type: PKCS12
key-store: file:/etc/vault/client.p12
key-store-password: changeit
key-store-type: PKCS12
cert-auth-path: cert
这里的 authentication: CERT 表示使用客户端证书认证方式,cert-auth-path 指定 cert 认证方法挂载的路径,默认就是 cert。在 Vault 服务端需要先启用 cert 认证并创建角色,下面给出一个简单示例。
vault auth enable cert vault write auth/cert/certs/web-clients \ display_name=web-clients \ policies=default,app-readonly \ certificate=@ca.crt \ ttl=3600
上述命令把 CA 证书绑定到 web-clients 角色,任何由该 CA 签发的客户端证书都可以通过 cert 认证。角色中可配置 allowed_common_names、required_extensions、ttl 等参数,细粒度控制哪些客户端证书能登录。如果客户端证书的组织名称、OU 等字段需要参与策略判断,可以在角色中设置 bound_cidrs、bound_serial_numbers 等。
双向 TLS 最常见的故障是客户端没有把证书链发送出去或者发送了不完整的链。Java 客户端从 key-store 中选择别名时,如果密钥库里有多个私钥条目,Spring 默认可能选择第一个匹配的条目;更稳妥的做法是保证客户端密钥库中只有一个私钥条目,或者通过 key-alias 明确指定别名。在 spring.cloud.vault.ssl 下可以设置 key-alias,避免别名选择的不确定性。
还需要注意,Vault 的 cert 认证方法默认要求客户端证书中的 CN 或 Subject Alternative Name 能被映射到角色。如果客户端证书没有 SAN,且角色没有设置 allowed_common_names,通常不会直接拒绝;如果配置了 required_extensions,证书必须包含对应扩展字段,否则认证失败。
五、常见问题与注意事项
第一个高频错误是 PKIX path building failed,这意味着信任库中没有完整加到受信任 CA 证书,或者证书链顺序不对。解决办法通常是确认信任库里导入的是 CA 证书而不是服务端叶子证书,并且 trust-store-type 与文件实际格式一致。第二个高频错误是 SSLHandshakeException: No subject alternative names present,说明服务端证书缺少 SAN,或者客户端请求的主机名没有出现在 SAN 中,需要重新签发证书,不能靠关闭主机名校验来规避。
第三个常见问题是连接地址使用了 IP,但证书 SAN 里没有该 IP。Java 对 SAN 中 ip:127.0.0.1 的匹配规则比较明确,如果 SAN 只有 DNS 名称,客户端访问 https://127.0.0.1:8200 也会握手失败。因此本地调试时建议将 vault.ipipp.com 映射到 127.0.0.1,同时让客户端访问域名,或者把 IP 写入 SAN。
属性覆盖方面,Spring Boot 支持通过环境变量将配置项转成大写并用下划线分隔,例如 SPRING_CLOUD_VAULT_SSL_TRUST_STORE 可以覆盖配置文件中的信任库路径。密码属性同样可以被环境变量覆盖,但要注意不要把密码写在命令行参数中,避免通过进程列表泄露。在 Windows 环境中,路径分隔符需要使用反斜杠,例如 C:\vault\client-truststore.p12,在 YAML 中建议使用 file:C:\\vault\\client-truststore.p12 或使用正斜杠以避免转义问题;如果直接使用反斜杠,YAML 解析器可能将 \v 当成转义字符,最好显式写双反斜杠。
密钥库密码和信任库密码通常不建议直接写在 bootstrap.yml 里,尤其是该文件会被提交到代码仓库时。Spring Cloud Vault 支持从环境变量或密钥管理服务读取密码,也可以通过 spring-cloud-context 的占位符引用外部属性。例如把密码放在 VAULT_CLIENT_TRUSTSTORE_PASSWORD 环境变量中,配置里写 trust-store-password: ${VAULT_CLIENT_TRUSTSTORE_PASSWORD},启动时不会暴露在明文配置文件里。
证书轮换时,如果客户端信任库中只导入了旧 CA,新服务端证书由新 CA 签发,握手会突然失败。建议在轮换前同时导入新旧 CA,等服务端切换完成后再移除旧 CA。对于双向 TLS,客户端证书到期前需要重新签发并更新密钥库,同时检查 Vault 角色中的 ttl 与证书有效期是否匹配,避免出现证书虽然有效但 Vault 颁发的 token 已经过期的情况。
最后,排查 SSL 问题可以打开 Java 的调试输出,启动参数中加入 -Djavax.net.debug=ssl:handshake:verbose,能看到握手阶段发送了哪些证书、使用了哪些协议和密码套件。但这条输出量较大,只适合开发环境临时使用。生产环境建议配置监控和日志采集,通过异常堆栈中的 CertificateException 或 SSLHandshakeException 快速定位是信任库问题、主机名问题还是双向认证问题。
Spring Cloud VaultSSL配置客户端证书修改时间:2026-10-01 00:33:00