一段时间以来,我的一台服务器上一直有一个 SSL 网站,没有任何问题,然后我不得不在同一台服务器上使用同一端口 (4433) 添加第二个 SSL 网站。我的配置如下:我的服务器中有两个 nginx 前端和 apache 后端网站,两者的配置完全相同(服务器名称等除外)。
我的问题开始出现,当我访问其中一个网站,然后访问第二个网站时,我在浏览器中看到以下错误:
您的浏览器发送了一个该服务器无法理解的请求。
然后我查看了 apache 错误日志并看到以下错误:
[2018 年 11 月 9 日星期五 16:17:51.247904] [ssl:error] [pid 18614] AH02032:通过 SNI 提供的主机名 firstwebsite.intweb.net 和通过 HTTP 提供的主机名 secondwebsite.intweb.net 不同
因此,我决定隔离问题,看看问题是否来自 nginx 或 apache 处理从 nginx 收到的数据的方式。首先,我尝试使用 nginx 来实现这一点,只需让 nginx 位于前端和后端,并使用 proxy_passhttps://127.0.0.1:44133称呼。
令我惊讶的是,问题出在 nginx 上,正如您在以下 curl 调用中看到的那样,我可以看到,事实上,在我尝试访问的第二个网站上,后端 sni 和后端 hostheader 由于某种原因而有所不同。
证明如下:
[root@webdev http]# curl -I https://firstwebsite.intweb.net
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Thu, 29 Nov 2018 00:08:05 GMT
Content-Type: application/octet-stream
Content-Length: 0
Connection: keep-alive
X-backend-ssl-sni: firstwebsite.intweb.net
X-backend-hostheader: firstwebsite.intweb.net
Strict-Transport-Security: max-age=31536000
[root@webdev http]# curl -I https://secondwebsite.intweb.net
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Thu, 29 Nov 2018 00:08:10 GMT
Content-Type: application/octet-stream
Content-Length: 0
Connection: keep-alive
X-backend-ssl-sni: firstwebsite.intweb.net
X-backend-hostheader: secondwebsite.intweb.net
Strict-Transport-Security: max-age=31536000
反过来也会发生这种情况,我的意思是,如果我先调用第二个网站,然后再调用第一个网站,我将获得 backend-sni 和后端主机头,第二个网站的值相同,但第一个网站的值不同。这就像存在任何类型的缓存或类似的东西一样。我真的不知道会发生什么。
以下是每个网站的配置文件:
第一个网站会议:https://paste.ngx.cc/9b
第二个网站会议:https://paste.ngx.cc/43
任何帮助都将不胜感激。
答案1
Apache 2.2(您使用的版本;顺便说一下,已经停产)严格禁止使用不同的 SNI 主机名和 Host 主机名。Apache 2.4 放宽了该条件,并且仅在以下情况下才会失败:
“请求未选择 SNI 选择的虚拟主机,并且其 SSL 参数不同”
如果启用了 keepalive,则 nginx 将重用后端连接,但您的配置中没有该选项。
查看 nginx 源代码还存在其他可能的情况:
* set u->keepalive if response has no body; this allows to keep
* connections alive in case of r->header_only or X-Accel-Redirect
解决方案:切换到apache 2.4。
解决方法:不要在前端和后端之间使用 https(尤其是在您的设置中两者都在同一台机器上)。mod_rpaf 和 mod_remoteip 将有助于向其他 apache 模块公开“假”ssl 信息。
答案2
我可以通过在 nginx.conf 文件中添加以下代码来解决这个问题:
proxy_ssl_session_reuse off;
显然 OpenSSL 中有一个错误。我运行以下代码进行了测试:
openssl genrsa -out fookey.pem 2048
openssl req -x509 -key fookey.pem -out foocert.pem -days 3650 -subj '/CN=testkey.invalid.example'
openssl s_server -accept localhost:30889 -cert foocert.pem -key fookey.pem -state -servername key1.example -cert2 foocert.pem -key2 fookey.pem
openssl s_client -connect localhost:30889 -sess_out /tmp/tempsslsess -servername key1.example
openssl s_client -connect localhost:30889 -sess_in /tmp/tempsslsess -servername key2.example
在 s_server 针对两个请求报告的 SNI 信息中观察 key1.example。(“TLS 扩展中的主机名:”...)
如果 s_server 重新启动,并且首先使用 key2.example,然后使用 key1.example 重新运行 s_client 连接/会话,则在 s_server 为两个请求报告的 SNI 信息中观察 key2.example。
此外,我刚刚在不同的机器上进行了测试,有时 SNI 似乎在第二个请求中缺失。
s_client 过滤器会话是否应该使用,以便如果它在选择缓存会话之前知道 SNI 信息,它只会选择与预期 SNI 名称匹配的会话?如果它在搜索要重新使用的会话时没有 SNI 名称,它是否仍然应该在提供 SNI 信息时(稍后,在连接之前)仔细检查,以确保它与它选择的已保存会话中的 SNI 相同,并且如果它们不同,则不使用已保存的会话?
在我看来,如果客户端应用程序指定的 SNI 与服务器看到的 SNI 不同,那就不好了。
有人在报告使用 nginx 反向代理到 apache 的问题时发现了这一点,apache 警告说 SNI 主机名与 Host: 标头不匹配,尽管 nginx 配置明确将 Host: 和 apache 端 SNI 设置为相同。