Nginx vhost从默认块继承ssl_certificate *指令?

时间:2016-10-31 15:44:16

标签: ssl nginx virtualhost

以下是我最终要做的事情,完全披露:我正在尝试使用SNI的单个证书在多个虚拟主机上设置TLS:一个用于顶点的vhost,另一个用于单独的主机。也就是说,我想使用相同的证书提供https://example.comhttps://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_certificatessl_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有效,并使用默认服务器块中配置的证书。为什么呢?

1 个答案:

答案 0 :(得分:1)

这实际上是根据documentation

的预期行为
  

在浏览器发送HTTP之前建立SSL连接   请求和nginx不知道请求的服务器的名称。   因此,它可能只提供默认服务器的证书。

解决方案是使用different IPs

  

应该记住,由于HTTPS协议的限制   虚拟服务器应该监听不同的IP地址   将为第二个站点颁发第一个服务器的证书。