添加TLS证书到Envoy配置后gRPC连接失败及证书CN字段问题排查
为Envoy添加TLS证书配置后,gRPC客户端出现连接错误:
code: 14,
metadata: Metadata { _internal_repr: {}, flags: 0 },
details: 'failed to connect to all addresses'
移除Envoy的TLS配置后请求可正常执行,同时使用grpcurl工具测试时提示:
Failed to dial target host "localhost:50000" x509: certificate relies on legacy Common Name field, use SANs instead
设置GODEBUG=x509ignoreCN=0后问题仍未解决。
Envoy相关配置(监听50000端口,转发至50052端口的itemService):
transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext common_tls_context: tls_certificates: - certificate_chain: filename: server.cert private_key: filename: server.key
Node.js客户端代码(Next.js服务端getServerSideProps):
options = { key: readFileSync("certs/client.key"), cert: readFileSync("certs/ca.crt"), csr: readFileSync("certs/client.crt"), }; const creds = credentials.createSsl( options.cert, options.key, options.csr ); grpcServer.servicesList.itemsService = new ItemsServiceClient( "localhost:50000", creds, { "grpc.ssl_target_name_override": "localhost", "grpc.default_authority": "localhost", } );
1. 重新生成包含SAN字段的证书
现代TLS客户端(包括gRPC、grpcurl)已强制要求证书包含**Subject Alternative Name(SAN)**字段,仅使用旧的Common Name(CN)字段会导致验证失败,即使设置GODEBUG=x509ignoreCN=0也无法解决。
生成步骤:
- 创建openssl配置文件
san.conf:
[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] CN = localhost [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost IP.1 = 127.0.0.1
- 用该配置重新生成服务端证书:
# 生成CSR和私钥 openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -config san.conf # 用CA签名生成证书 openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.cert -days 365 -extensions v3_req -extfile san.conf
2. 修正Node.js客户端的SSL凭证参数顺序
credentials.createSsl的正确参数顺序为:createSsl(rootCerts, privateKey, certChain),当前代码参数传递错误,导致凭证无效。
调整后的代码:
options = { rootCert: readFileSync("certs/ca.crt"), // CA根证书 clientKey: readFileSync("certs/client.key"), // 客户端私钥 clientCert: readFileSync("certs/client.crt"), // 客户端证书 }; const creds = credentials.createSsl( options.rootCert, options.clientKey, options.clientCert );
如果Envoy未配置客户端证书认证,可简化为仅传入CA根证书:
const creds = credentials.createSsl(readFileSync("certs/ca.crt"));
3. 完善Envoy的TLS配置(可选,若需客户端认证)
如果需要Envoy验证客户端证书,需添加CA证书配置:
transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext common_tls_context: tls_certificates: - certificate_chain: filename: server.cert private_key: filename: server.key validation_context: trusted_ca: filename: ca.crt # 用于验证客户端证书的CA根证书 require_client_certificate: true # 可选,强制客户端提供证书
4. 验证Envoy TLS监听状态
使用openssl工具检查Envoy的TLS端口是否正常:
openssl s_client -connect localhost:50000 -servername localhost
查看输出中是否包含正确的SAN字段,以及证书是否被CA信任。
内容的提问来源于stack exchange,提问作者Kevin Bogdan

