Spring Cloud Gateway调用Azure私有端点报SSL握手异常:无匹配DNS名称
问题
我在Microsoft Azure上构建基础架构,尝试通过虚拟网络的私有端点连接两个应用:
- 基于Spring Boot、Web-Flux/Netty的Spring Cloud Gateway服务,已配置虚拟网络出站连接;
- 拥有虚拟网络私有端点的Spring Boot后端服务。
两个应用均能正常访问,各自的actuator端点调用正常(后续计划修改后端服务的该配置),且处于同一虚拟网络中。
Gateway服务中转发请求至后端的代码如下:
@Bean public RouteLocator tcTestRoutes(RouteLocatorBuilder builder) { return builder.routes() .route((PredicateSpec ps) -> ps.path("/User-api/**") .filters((GatewayFilterSpec filter) -> filter.stripPrefix(1)) .uri("https://{backend_app_name}.privatelink.azurewebsites.net")) .build(); }
当前测试结果:
- 调用
https://{gateway_app_name}.azurewebsites.net/actuator响应正常; - 调用
https://{backend_app_name}.azurewebsites.net/actuator响应正常; - 调用
https://{gateway_app_name}.azurewebsites.net/User-api/actuator时返回错误页面,未得到后端服务的actuator响应。
查看Log Stream发现核心错误:
2024-02-15T16:39:54.020457970Z: [INFO] 2024-02-15T16:39:53.976Z WARN 116 --- [or-http-epoll-4] r.netty.http.client.HttpClientConnect : [32e3c331, L:/169.254.254.2:47760 - R:{backend_app_name}.privatelink.azurewebsites.net/10.0.3.20:443] The connection observed an error 2024-02-15T16:39:54.020495571Z: [INFO] javax.net.ssl.SSLHandshakeException: No subject alternative DNS name matching {backend_app_name}.privatelink.azurewebsites.net found. 2024-02-15T16:39:54.020501172Z: [INFO] at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:na] ... 2024-02-15T16:39:54.020722982Z: [INFO] Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching {backend_app_name}.privatelink.azurewebsites.net found. 2024-02-15T16:39:54.020727582Z: [INFO] at java.base/sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:212) ~[na:na]
从日志可见私有DNS已解析至正确IP,但不清楚如何确保HTTPS正常工作(不想使用明文HTTP),也不确定该在应用代码还是App Service设置中修复问题。
解决方案
核心原因
Azure App Service的SSL证书仅绑定在原始域名{backend_app_name}.azurewebsites.net上,而你使用的私有链接域名{backend_app_name}.privatelink.azurewebsites.net不在证书的SAN(Subject Alternative Name)列表中,导致SSL握手验证失败。
方法1:使用原始域名+私有DNS解析(推荐)
- 修改Gateway路由配置:将URI替换为后端服务的原始域名
https://{backend_app_name}.azurewebsites.net - 配置私有DNS区域:在Azure中创建或复用现有的
privatelink.azurewebsites.net私有DNS区域,添加{backend_app_name}.azurewebsites.net的A记录,指向后端服务私有端点的IP。这样Gateway在虚拟网络内解析原始域名时会指向私有端点,同时SSL验证使用原始域名,与证书完全匹配。
方法2:配置SNI(Server Name Indication)
若必须使用私有链接域名,可让Gateway在SSL握手时发送原始域名作为SNI:
@Bean public HttpClient httpClient() { return HttpClient.create() .secure(sslContextSpec -> sslContextSpec .sslContext(SslContextBuilder.forClient()) .handlerConfigurator(sslHandler -> { SSLParameters params = new SSLParameters(); params.setServerNames(List.of(new SNIHostName("{backend_app_name}.azurewebsites.net"))); sslHandler.engine().setSSLParameters(params); })); } @Bean public RouteLocator tcTestRoutes(RouteLocatorBuilder builder, HttpClient httpClient) { return builder.routes() .route(ps -> ps.path("/User-api/**") .filters(filter -> filter.stripPrefix(1)) .uri("https://{backend_app_name}.privatelink.azurewebsites.net")) .build(); } @Bean public WebClient.Builder webClientBuilder(HttpClient httpClient) { ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); return WebClient.builder().clientConnector(connector); }
该配置让Netty客户端连接私有链接域名时,发送原始域名作为SNI,后端App Service会返回匹配的证书。
方法3:跳过证书验证(仅测试用,生产禁用)
如果只是临时测试,可在Gateway中禁用SSL证书验证,但生产环境绝对不能使用此方法:
@Bean public HttpClient httpClient() { return HttpClient.create() .secure(sslContextSpec -> sslContextSpec .sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE))); }
内容的提问来源于stack exchange,提问作者Trec Apps
相关产品推荐
相关产品推荐

