以下是我最终要做的事情,完全披露:我正在尝试使用SNI的单个证书在多个虚拟主机上设置TLS:一个用于顶点的vhost,另一个用于单独的主机。也就是说,我想使用相同的证书提供https://example.com
和https://host.example.com
。两个主机都解析为相同的IPv4地址。
我无法理解Nginx正在使用ssl_certificate*
指令做什么。这些指令仅在默认服务器块中定义,但无论如何都会在另一个块中“拾取”。
我从默认服务器块开始到404。
server {
return 404;
}
正如所料,
$ curl --head http://example.com
HTTP/1.1 404 Not Found
[...]
然后我创建了第一个example.com块,将http重定向到顶点上的https:
server {
listen 80;
server_name example.com;
return 301 https://example.com;
}
这也有效:
$ curl --head http://example.com
HTTP/1.1 301 Moved Permanently
[...]
Location: https://example.com
$ curl --head http://host.example.com
HTTP/1.1 404 Not Found
[...]
然后我在顶点配置了TLS:
server {
listen 443 ssl;
ssl_certificate [...];
ssl_certificate_key [...];
server_name example.com;
}
到目前为止,非常好:http重定向到https,https有效。
$ curl --head http://example.com
HTTP/1.1 301 Moved Permanently
[...]
Location: https://example.com
$ openssl s_client -connect example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
然后我遇到了一个简短的障碍。我还没有说过关于host.example.com
的话,但是当我尝试通过TLS连接时:
$ curl --head http://host.example.com
HTP/1.1 404 Not Found
[...]
$ openssl s_client -connect host.example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: host.example.com
HTTP/1.1 200 OK
[... content from example.com...]
默认阻止响应http://host.example.com
。但是https://host.example.com
匹配https://example.com
的服务器块。
因此,经过一番搔痒,我意识到我需要两个默认块:每个端口一个。
server {
listen 443 ssl;
ssl_certificate [...];
ssl_certificate_key [...];
return 404;
}
确认这是我需要的:
$ openssl s_client -connect host.example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: host.example.com
HTTP/1.1 404 Not Found
[...]
但后来我意识到了什么。这就是我的困惑开始的地方。我不应该,也不明白为什么我能够通过TLS,连接到example.com:443(或host.example.com:443)。< / p>
当我为端口443创建第二个默认服务器时,我从https://example.com的服务器块中删除了 ssl_certificate
和ssl_certificate_key
指令。它们仅在默认服务器块中定义。
server {
listen 443 ssl;
ssl_certificate [...];
ssl_certificate_key [...];
return 404;
}
server {
listen 443 ssl;
server_name example.com;
# No ssl_certificate* directives!
}
然而,不知何故,当我尝试连接到https://example.com时,我在默认服务器块中定义的证书最终被使用:
$ openssl s_client -connect example.com:443
CONNECTED(00000003)
[...]
---
GET / HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
[...]
我还确认,在浏览器中访问网址https://example.com
会提供实际网页。
因此,https://example.com的配置似乎继承来自默认块的TLS设置,但是应用应用的其余设置匹配的服务器块。那是怎么回事?我试过搜索我能想到的大多数关键字矩阵:“nginx”,“tls”,“ssl”,“vhost”,“默认服务器块”,“继承”,“继承”;但我找不到任何东西,文件似乎是沉默的。它可能写在某个地方,但我找不到合适的地方。
增加:
完整配置,这是一个基本的Debian安装,与软件包默认设置大致相同。
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# above line loads:
# ngx_http_auth_pam_module, ngx_http_geoip_module, ngx_http_image_filter_module, ngx_http_xslt_filter_module, ngx_mail_module, ngx_stream_module
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
include /etc/nginx/mime.types; # nothing unusual in here
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # drop SSLv3
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf; # empty directory, includes nothing
include /etc/nginx/sites-enabled/*; # includes single file which defines server blocks as above
}
基本上就是这样。
再次编辑:
以下是完整的服务器配置文件,所有这些都在一个地方。
server {
listen 80;
return 404;
}
server {
listen 443 ssl;
ssl_certificate [...];
ssl_certificate_key [...];
return 404;
}
server {
listen 80;
server_name example.com;
return 301 https://example.com;
}
server {
listen 443 ssl;
server_name example.com;
root /srv/www/example.com;
index index.html;
}
使用此配置,访问https://example.com有效,并使用默认服务器块中配置的证书。为什么呢?
答案 0 :(得分:1)
这实际上是根据documentation:
的预期行为在浏览器发送HTTP之前建立SSL连接 请求和nginx不知道请求的服务器的名称。 因此,它可能只提供默认服务器的证书。
解决方案是使用different IPs:
应该记住,由于HTTPS协议的限制 虚拟服务器应该监听不同的IP地址 将为第二个站点颁发第一个服务器的证书。