Docker Linux容器中使用Yarp.ReverseProxy时SSL连接建立失败
Yarp.ReverseProxy在Docker Linux容器中连接Reddit时SSL握手失败问题
问题现象
- 本地HTTPS环境下,Yarp.ReverseProxy代理
https://www.reddit.com/运行正常 - 切换到Docker Linux容器环境后,启动应用立即触发SSL连接失败错误
- 将目标地址替换为
https://www.google.com/时,Docker环境可正常工作
报错信息
Yarp.ReverseProxy.Forwarder.HttpForwarder[48] Request: An error was encountered before receiving a response. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL. ---> Interop+Crypto+OpenSslCryptographicException: error:14094419:SSL routines:ssl3_read_bytes:tlsv1 alert access denied --- End of inner exception stack trace --- at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount) at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate clientCertificateSelectionCallback) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken) at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(QueueItem queueItem) at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, String destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer, CancellationToken cancellationToken)
配置文件(appsettings.json)
{ "ReverseProxy": { "Routes": { "route1" : { "ClusterId": "redditCluster", "Match": { "Path": "{**catch-all}" } } }, "Clusters": { "redditCluster": { "Destinations": { "reddit/destination1": { "Address": "https://www.reddit.com/" } } } } } }
代码(program.cs)
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; var builder = WebApplication.CreateBuilder(args); builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); var app = builder.Build(); app.MapReverseProxy(); app.Run();
Docker配置
采用默认Dockerfile配置
原因分析
报错中的tlsv1 alert access denied表明Reddit服务器在SSL握手阶段主动拒绝了请求,可能的触发因素:
- Docker容器环境的TLS版本/加密套件与Reddit要求不兼容
- Reddit的反爬机制识别到非浏览器特征的请求(缺少标准请求头)
- Docker容器内的CA证书过期或不完整
解决方案
1. 调整TLS配置与加密套件
强制指定兼容的TLS版本和加密套件,确保与Reddit服务器匹配:
builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) .ConfigureHttpClientForAllClusters(httpClient => { // 指定TLS版本 httpClient.SslOptions.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13; // 配置Reddit支持的加密套件 httpClient.SslOptions.CipherSuitesPolicy = new System.Net.Security.CipherSuitesPolicy(new[] { System.Net.Security.TlsCipherSuite.TLS_AES_256_GCM_SHA384, System.Net.Security.TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256, System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 }); });
2. 添加浏览器特征请求头
模拟正常浏览器请求,绕过Reddit的反爬检测:
builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) .AddTransforms(transforms => { transforms.AddRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); transforms.AddRequestHeader("Accept-Language", "en-US,en;q=0.9"); transforms.AddRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); });
3. 更新Docker容器内CA证书
确保容器内根证书是最新的,避免证书信任问题:
如果使用Debian/Ubuntu基础镜像,在Dockerfile中添加:
RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates
4. 强制使用HTTP/1.1
若HTTP/2握手存在兼容性问题,强制切换到HTTP/1.1:
builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) .ConfigureHttpClientForAllClusters(httpClient => { httpClient.DefaultRequestVersion = new Version(1, 1); httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact; });
内容的提问来源于stack exchange,提问作者Serhii Shtokal
相关产品推荐
相关产品推荐

