Nginx - 为多个server_name设置SSL支持

83
我很乐意使用nginx为一个拥有多个域名和SSL的网站提供服务:
  • webmail.example.com
  • webmail.beispiel.de
这两者使用同一个虚拟主机,所以我只需要设置两次server_name。 问题是,我需要nginx为每个域名提供正确的ssl证书。
这是否可能在一个虚拟主机中完成,还是需要设置两个虚拟主机?
2个回答

106

2014年11月修改:初始答案不正确且不完整; 需要更新!以下是正确的内容。

基本上,有两种情况

  • 您拥有通配符证书(或多域证书)

在这种情况下,您可以使用几个 vhosts 监听相同的IP地址/https端口,并且两个 vhosts 使用相同的证书(监听所有接口),例如:

    server {
      listen 443;
      server_name webmail.example.com;
      root /var/www/html/docs/sslexampledata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }
    
    
    server {
      listen 443;
      server_name webmail.beispiel.de;
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }

或者在你的特定情况下,让两个域名由同一数据服务。

    server {
      listen 443;
      server_name webmail.example.com webmail.beispiel.de; # <== 2 domains
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/samecertif.crt;
      ssl_certificate_key /var/www/ssl/samecertif.key;
      ...
    }



  • 您有两个(或更多)不同的证书

如果您拥有上述情况(所有证书使用同一个IP地址),现代浏览器仍可通过Server Name Indication工作。SNI使客户端(浏览器)在请求头中发送其要访问的主机,使服务器(nginx)能够在处理证书之前处理虚拟主机(vhosts)。配置与上面相同,只是每个虚拟主机都有特定的证书、crt和key。

(nginx从0.9.8f开始支持SNI,请检查您的nginx服务器是否符合SNI标准)
(此外,SF也讨论了SNI和浏览器支持)

否则,如果您还希望覆盖旧版浏览器,则需要使用多个监听不同IP地址/https端口的虚拟主机(vhosts),例如:

    server {
      listen 1.2.3.4:443; # <== IP 1.2.3.4
      server_name webmail.example.com;
      root /var/www/html/docs/sslexampledata;
    
      ssl on;
      ssl_certificate /var/www/ssl/certifIP1example.crt;
      ssl_certificate_key /var/www/ssl/certifIP1example.key;
      ...
    }
    
    
    server {
      listen 101.102.103:443; <== different IP
      server_name webmail.beispiel.de;
      root /var/www/html/docs/sslbeispieldata;
    
      ssl on;
      ssl_certificate /var/www/ssl/certifIP2beispiel.crt;
      ssl_certificate_key /var/www/ssl/certifIP2beispiel.key;
      ...
    }

原因在这里解释得很清楚


1
当然,这是一个解决方案,但不是一个好的解决方案。更改一个虚拟主机意味着更改另一个虚拟主机。而且至少会有4个虚拟主机... - PascalTurbo
5
注意:第二个选项——在“if”语句中使用“ssl_certificate”是行不通的。 - James Billingham
3
根据我所了解的,HTTP_HOST在请求头中,而请求头被SSL加密。因此,在使用正确的SSL证书进行解密之前,您无法检查HTTP_HOST。这是一个进退两难的局面。 - Matt
1
完全重写答案。 - Déjà vu
1
@K.F server_name 指令与 server 块相关联。因此,由于您的证书不同(我猜每个域名一个),您需要2个 server 块(每个块一个证书)。但是,您可以使用 include file; 指令来包含公共设置(我添加了一个 /etc/nginx/sites-include 目录,其中放置了所有我的包含文件...),然后在每个块中执行公共部分的包含、server_name 和添加特定的证书指令。 - Déjà vu
显示剩余3条评论

2

使用TLS SNI: SNI允许浏览器在SSL握手期间传递请求的服务器名称

检查Nginx是否启用了TLS SNI

$ nginx -V
...
TLS SNI support enabled
...

请检查 error_log,确保没有出现以下警告信息:

nginx 编译时开启了 SNI 支持,但是现在它与一个不支持 tlsext 的 OpenSSL 库动态链接,因此 SNI 不可用。

Nginx HTTPS 文档 中有更详细的介绍。
如果启用了 TLS SNI,则以下配置可以正常工作。
# Create the self signed certificate

openssl req -x509 -newkey rsa -nodes -keyout default.key -days 36500 -out default.crt -subj "/CN=example.org"

openssl req -x509 -newkey rsa -nodes -keyout a.key -days 36500 -out a.crt -subj "/CN=a.example.org"

openssl req -x509 -newkey rsa -nodes -keyout b.key -days 36500 -out b.crt -subj "/CN=b.example.org"

server {
    listen       443 ssl default_server;
    server_name  "";

    ssl_certificate      default.crt;
    ssl_certificate_key  default.key;

    add_header "Content-Type" "text/plain";
    return 200 "default page";
}

server {
    listen       443 ssl;
    server_name  a.example.org;

    ssl_certificate      a.crt;
    ssl_certificate_key  a.key;

    add_header "Content-Type" "text/plain";
    return 200 "a.example.org page";
}

server {
    listen       443 ssl;
    server_name  b.example.org;

    ssl_certificate      b.crt;
    ssl_certificate_key  b.key;

    add_header "Content-Type" "text/plain";
    return 200 "b.example.org page";
}

# Add -v to verify the certificate

$ curl --insecure --resolve "a.example.org:443:127.0.0.1" https://a.example.org

a.example.org page

$ curl --insecure --resolve "b.example.org:443:127.0.0.1" https://b.example.org

b.example.org page

$ curl --insecure https://127.0.0.1

default page

参考:Nginx TLS SNI

如果Nginx禁用了TLS SNI:Nginx会为所有请求使用默认的server证书。

Nginx文档

这是由SSL协议行为引起的。浏览器发送HTTP请求之前建立SSL连接,nginx不知道所请求的服务器名称。因此,它只能提供默认服务器的证书。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接