Heroku 的 Nginx 反向代理失败 SSL 握手

Posted

技术标签:

【中文标题】Heroku 的 Nginx 反向代理失败 SSL 握手【英文标题】:Nginx reverse proxy to Heroku fails SSL handshake 【发布时间】:2016-11-17 10:51:47 【问题描述】:

不幸的是,我不是一个系统管理员,并且遇到了一个让我头疼的问题。

简而言之,我在 EC2(Ubuntu 14.04.4 LTS)上运行 nginx 以(a)托管我公司的营销网站(https://example.com,顺便说一下是 Wordpress)和(b)充当反向代理到我们在 Heroku (https://app.example.com) 上运行的 Rails 应用程序,以获取某些路径。我们对 example.com 和 app.example.com 使用相同的 SSL 证书。所有这一切都在 8-10 个月内运行良好,但我最近从 Heroku 的付费 SSL 插件切换到新的免费 SSL 产品,现在我们的反向代理坏了。

在检查 Nginx 错误日志时,我看到以下内容:

SSL_do_handshake() 失败(SSL:错误:14094438:SSL 例程:SSL3_READ_BYTES:tlsv1 警报内部错误:SSL 警报编号 80)当 SSL 与上游握手时,客户端:ipaddress1,服务器: example.com,请求:“GET /proxiedpath/proxiedpage HTTP/1.1”, 上游:“https://ipaddress2:443/proxiedpath/proxiedpage”,主机: “example.com”

我试图四处寻找一些额外的指导 - 我已经升级了 Nginx (1.10.1) 和 OpenSSL (1.0.2h),但没有成功。我怀疑这个问题可能是由于 Heroku 在新的免费 SSL 功能 (https://devcenter.heroku.com/articles/ssl-beta) 中使用了 SNI,但无法确定为什么这可能是一个问题。

关于我对这一点的探索的几点补充:

当我切换到新的免费 Heroku SSL 时,我按照文档的说明更改了我们的 app.example.com DNS 记录以指向 app.example.com.herokudns.com。该应用程序可以通过 app.example.com 正常访问,当我在 app.example.com 和 app.example.com.herokudns.com 上运行 nslookup 时,我得到了相同的 IP 地址。不过……

我无法通过从 nslookup 或 app.example.com.herokudns.com 返回的 IP 地址访问应用程序。我怀疑这是正常的和预期的,但不知道确切地说这是为什么。还有……

从 nslookup 返回的 IP 地址与上述日志错误消息中引用的 IP 地址(“ipaddress2”)不同。事实上,“ipaddress2”在整个日志中并不一致 - 它似乎定期更改。再说一次,我知道的不够多,无法知道我不知道的...... Heroku 方面的负载平衡?

最后,我的Nginx反向代理在nginx.conf中配置如下:

http 

    client_max_body_size 500M;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server_names_hash_bucket_size 64;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";

    server 

        listen 443 default_server;
        server_name example.com;

        root /usr/share/nginx/html;
        index index.php index.html index.htm;

        ssl on;
        ssl_certificate mycompanycert.crt;
        ssl_certificate_key mycompanykey.key;

        ssl_session_timeout 5m;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
        ssl_prefer_server_ciphers on;

        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;

        location / 
            try_files $uri $uri/ /index.php?q=$uri&$args;
        

        location ^~ /proxiedpath/ 
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Proto https;
            proxy_pass https://app.example.com/proxiedpath/;
        

    


非常感谢任何帮助 - 非常感谢!

【问题讨论】:

您的 2 个站点是否托管在同一台服务器上?你能告诉我们app.mycompany.com的配置吗?因为如果它们在同一台服务器上,那么您可以 return 301 https://app.mycompany.com/proxiedpath/; 并让 nginx 使用具有相同证书的 app.mycompany.com 服务器名称来处理请求。 不幸的是,它们是完全独立的——代理服务器设置在 AWS 上(我可以完全控制配置),应用程序服务器托管在 Heroku 上(我几乎没有控制甚至洞察力到配置)。不过,它在此更改之前有效,所以我知道这一定是一件相当小的事情。 另外,我想补充一下 - 出于 SEO 的原因,这些页面出现在 mycompany.com 上而不是 app.mycompany.com 上,这对我们来说很重要,它们实际上是从那里提供服务的。跨度> 这是一条相当罕见的消息(也许我没有做足够的代理):"SSL_do_handshake() failed (SSL: error:14094438:SSL routines:SSL3_READ_BYTES:tlsv1 alert内部错误:SSL 警报编号 80)在 SSL 与上游、客户端握手时..."。我会将搜索集中在反向代理上,我相信它是 Nginx。此外,如果您提供真实的信息,我们或许能够更好地帮助您。我无法针对example.com 运行任何测试(mycompany.com 是一个真实的网站;example.com 是 IANA 保留的)。 您的配置未指定proxy_ssl_protocolsproxy_ssl_ciphers 【参考方案1】:

请尝试添加proxy_ssl_server_name on

location ^~ /proxiedpath/ 
    proxy_ssl_server_name on;
 

【讨论】:

【参考方案2】:

作为其他人的说明,Heroku 强加的一个相关条件是 HOST 字段必须与自定义域名匹配。

所以除了proxy_ssl_server_name,你可能还想设置如下一行:

proxy_set_header Host mycustomdomain.com;

当然,这仅适用于传入服务器的主机字段与您的服务器所在的域不同的情况。

你得到的具体错误是:

SSL 证书错误

SSL 连接、其证书和/或包含的 HTTP 请求之间存在冲突信息。

【讨论】:

【参考方案3】:

我今天能够解决这个问题,并想发布解决方案以防其他人遇到同样的问题。

事实证明,问题毕竟与 SNI 有关。我在 nginx.org 上找到了这张票:

https://trac.nginx.org/nginx/ticket/229

这导致我使用 proxy_ssl_server_name 指令:

http://nginx.org/r/proxy_ssl_server_name

通过在您的配置中设置为“on”,您将能够使用 SNI 代理上游主机。

感谢所有提出建议的人!

【讨论】:

同***.com/questions/25329941/… 唯一的一点是你也要强制TLS1.0或以上,否则SNI将无法使用。 感谢您的回答。我认为找到这个答案很容易,但谷歌不这么认为 在这种情况下,由 nginx 日志显示的特定 OpenSSL 错误是 SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:SSL alert number 40alert number 40 是关键部分,转换为 this is probably a SNI host and the handshake didn't attempt SNI,请参阅 this 了解更多信息。 proxy_ssl_server_name on 应该让你继续前进。 你拯救了我的一天。添加了proxy_ssl_server_name on,它对我有用!谢谢 上帝保佑你,亲爱的……你救了我!

以上是关于Heroku 的 Nginx 反向代理失败 SSL 握手的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Nginx(反向代理+SSL 协商)和 Tomcat 上传大文件

Nginx的反向代理及ssl配置

nginx启动忽略dns解析

设置好 NGINX 反向代理后,如何在引入 NGINX 反向代理的 NGINX 上配置 SSL 直通? [复制]

带有两种 SSL 到 weblogic 的 nginx 反向代理

Nginx部署SSL证书反向代理