openssl 命令通过 ip 而不是主机名查询 NGINX

openssl 命令通过 ip 而不是主机名查询 NGINX

这是我很难理解的情况。我为 nginx 设置了两个主机块,如下所示

server {
    server_name _;
    listen *:443 default_server deferred;
    return 444;
}
server {
    listen      443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.ca;

}

包罗万象有一个自行生成的 ssl 证书,并且我的应用程序的块有一个有效的 SSL 证书。此设置适用于网络浏览器。如果网络浏览器访问,https://www.example.com那么它们会访问我的应用程序,但如果他们访问,htpps://123.123.123.123那么它不会返回任何内容。但现在我遇到了一个非常奇怪的情况。我收到一些失败的域请求(来自脚本),因此我尝试进行调试。首先我尝试卷曲网址:

$ curl https://www.example.ca
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none

我认为这很奇怪,因为浏览器都显示证书正在工作,并且 www.ssllabs.com 显示它正在工作。我尝试的下一步是在命令行上查看 SSL 证书:

echo | openssl s_client -showcerts -connect www.example.ca:443

该命令实际上显示了 catch all 服务器块而不是该块的 SSL 证书example.ca。如果我将 IP 地址添加到第二个 nginx 块,那么我会看到example.ca.所以现在我知道 openssl 和 curl 获得了错误的 ssl 证书,因为它们获得了错误的证书,但我不明白为什么?

我对 openssl 了解不多,但我能想到的只是 openssl/curl 错误地将主机名转换为 IP 地址,或者他们忘记添加正确的标头?这非常奇怪,如果有人知道更多,我将不胜感激。

答案1

像 nginx 这样具有多个证书(和密钥)的虚拟主机服务器使用服务器名称指示(SNI)扩展来选择正确的(虚拟主机和)证书,但是默认openssl s_client不发送SNI这样你就得到了默认的证书;-servername $hostname按照手册页中的描述进行添加(紧接在 后面-connect)。在其他几个堆栈上有很多此部分的重复项,但我在这里找不到。 (更新:2018 年的 OpenSSL 1.1.1 修复了此问题;SNI 现在是默认值,除非您指定-noservername。)

OTOHcurl会自动发送 SNI(浏览器和 ssllab 也是如此),除非您有一个非常旧的版本的 curl 本身或其使用的中间件(即不是始终使用 OpenSSL;检查curl -V大写)。尝试添加-v以获取有关其正在执行和获取的更多详细信息,如果这还不够,请使用 tcpdump/Wireshark/etc 获取线路跟踪。

相关内容