我有一个包含许多虚拟服务器的 Web 服务器。其中只有 1 个是 SSL。问题是,由于没有监听 SSL 的万能服务器块,因此对其他站点的任何 https 请求都由 1 个 SSL 块提供服务。
我的配置基本上是这样的:
# the catch all
server {
listen 80 default;
# I could add this, but since I have no default cert, I cannot enable SSL,
# and this listen ends up doing nothing (apparently).
# listen 443;
server_name _;
# ...
}
# some server
server {
listen 80;
server_name server1.com;
# ...
}
# some other server ...
server {
listen 80;
server_name server2.com;
# ...
}
# ... and it's https equivalent
server {
listen 443;
ssl on;
server_name server2.com;
# ...
}
现在,由于没有 443 的默认侦听器,类似这样的请求https://server1.com
最终将由server2.com
https 块处理。这遵循server_name
了文档。
如果没有匹配,则将根据以下顺序使用配置文件中的 server { ... } 块:
- 具有匹配的 listen 指令的服务器块标记为 [default|default_server]
- 第一个具有匹配的 listen 指令(或隐式 listen 80;)的服务器块
这个问题的首选解决方案是什么?我是否需要为我的捕获所有服务器块设置虚拟证书,以便我可以监听 443 并处理不良请求?是否有我不知道的参数强制使用精确的主机名匹配server
?
答案1
理想情况下,我希望 nginx 根本不提供 https 服务,除非主机名匹配,或者让它重定向到同一主机上的 http。
两者都不可能。来自客户端的连接https://foo.example.com/除名称之一为“foo.example.com”的 SSL 证书外,其他证书均无法接受。在 SSL 连接被接受之前,没有重定向的机会。
如果您为每个站点配置 SSL,则用户点击证书错误后将获得他们请求的站点。如果您为 SSL 配置一个“全部捕获”站点,该站点仅提供错误页面,并为应该支持 SSL 的一个站点配置基于名称的虚拟主机,则可以向客户端提供错误页面。
SSL 和 HTTP 虚拟主机无法很好地协同运行。
答案2
唯一的方法是创建自签名 SSL 证书并使用它来控制传入的 https 请求。您可以通过几个简单的步骤创建自签名 SSL 证书这篇文章中概述。
假设您创建了一个文件名为 server.crt 的自签名证书。然后,您需要在 nginx 配置中附加以下内容:
server {
listen 443;
ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
server_name server1.com;
keepalive_timeout 60;
rewrite ^ http://$server_name$request_uri? permanent;
}
您仍然会收到浏览器 SSL 警告消息,但至少您可以控制接下来发生的情况。
答案3
这就是我解决问题的方法:
- 创建自签名证书:
openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650
- 将其复制到 NginX 可以找到的地方:
cp self*.pem /etc/nginx/ssl/
- 设置一条捕获所有信息的路线:
server {
listen 443 default_server ssl;
ssl on;
ssl_certificate /etc/nginx/ssl/self_cert.pem;
ssl_certificate_key /etc/nginx/ssl/self_key.pem;
return 301 http://$host;
}
这将做什么:它会给你警告(没有办法绕过它)在任何没有自己的证书的服务器上,但警告不会显示错误的证书名称。如果用户点击“仍然访问”,他们将被重定向到他们输入的站点的非 SSL 版本。
警告:
如果您启用 SLL 的站点仅定义www.example.com
(而没有定义example.com
),那么您的捕获所有路由最终将https://example.com
使用自签名证书和相应的警告提供服务。
答案4
现在您可以使用 TLS 服务器名称指示扩展 (SNI,RFC 6066)。HTTPS 侦听器将能够在提供适当的证书之前识别域名。
这意味着您需要为所有域名提供证书,并且当使用 SNI 识别其他域名之一时,您只需使用 HTTP 301 重定向到 HTTP 非加密版本,除非服务器名称与需要加密的单个域名匹配。
有关 SNI 的更多信息,请参阅 nginx 文档http://nginx.org/en/docs/http/configuring_https_servers.html