正确设置 https 的“默认” nginx 服务器

正确设置 https 的“默认” nginx 服务器

我在同一台机器上运行了多个服务器,一些服务器仅支持 http,一些服务器同时支持 http 和 https。主配置文件中包含了几个单独的文件,它们定义了几个服务器块。

我已经为 http 设置了一个“默认”服务器,它将为与其他配置文件中的任何其他 server_name 不匹配的请求提供通用“维护页面”。http 默认服务器按预期工作,它使用 server_name“_”,并且它出现在包含列表中的第一个(因为我观察到,在服务器之间出现重复的 server_name 的情况下,将使用第一个出现的 server_name)。这很有效。

我期望完全相同的服务器块(仅将“listen 80 default_server”切换为“listen 443 default_server”,并且不再提供页面“return 444”),但事实并非如此。相反,似乎新的默认 https 服务器实际上正在抓取所有传入的 https 连接并导致它们失败,尽管其他服务器块具有更适合传入请求的 server_name。删除新的默认 https 服务器将导致半正确的行为恢复:带有 https 的网站都将正确加载;但没有 https 的网站都将被路由到包含文件中的第一个 https 服务器(根据文档,如果没有出现“default_server”,则出现的第一个服务器块将是“default”)。

所以我的问题是,在 nginx 中为 SSL 连接定义“默认服务器”的正确方法是什么?为什么当我明确设置“default_server”时,它会变得贪婪并抓取所有连接,而当我隐式让 nginx 决定“默认服务器”时,它会像我预期的那样工作(将错误的服务器设置为默认值,而其他真实服务器正常运行)?

这是我的“默认服务器”。Http 可以正常工作,不会破坏其他服务器。Https 会破坏其他服务器并消耗所有资源。

server {
    listen 443 ssl default_server;
    server_name _;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    return 444;
}

server {
    listen *:80 default_server;
    server_name _;
    charset utf-8;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    root /home/path/to/templates;

    location / {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}

你们有人看出这儿可能出了什么问题吗?

答案1

我设法使用 nginx 在单个 IP 上配置了共享专用主机。默认 HTTP 和 HTTPS 对未知域传入提供 404 服务。

1 - 创建默认区域

由于 nginx 按照 ascii 顺序加载 vhosts,因此您应该00-default在您的/etc/nginx/sites-enabled.

2 - 填充默认区域

使用默认虚拟主机填充您的虚拟00-default主机。这是我正在使用的区域:

server {
    server_name _;
    listen       80  default_server;
    return       404;
}


server {
    listen 443 ssl;
    server_name _;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    return       404;
}

3 - 创建自签名证书、测试并重新加载

您将需要创建一个自签名证书/etc/nginx/ssl/nginx.crt

创建默认的自签名证书:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

只是提醒:

  • 重新加载/重新启动之前测试 nginx 配置:nginx -t
  • 重新加载享受:sudo service nginx reload

希望能帮助到你。

答案2

您的“默认”https 块中未定义任何 ssl_certificate 或 ssl_certificate_key。尽管您没有或不需要此默认方案的真实密钥,但您仍需要配置一个,否则 nginx 将出现您描述的不良行为。

创建一个通用名为 * 的自签名证书并将其插入到您的配置中,它将开始按您希望的方式工作。

此设置下的“默认”行为是浏览器会收到证书不可信的警告,如果用户将证书添加为例外,则 nginx 将断开连接,并且用户将看到浏览器默认的“无法连接”错误消息。

答案3

我们基本上要不惜一切代价避免配置文件中的第一个服务器定义被用作 SSL 连接的万能服务器。我们都知道它就是这样做的(与 http 和使用 default_server 配置相反,后者工作得很好)。

对于 SSL 来说,这还不能以声明方式实现(目前),所以我们必须使用 IF 来对其进行编码...

变量$host是请求行或 http 标头中的主机名。变量$server_name是我们现在所在的服务器块的名称。

因此,如果这两个不相等,那么您为另一台主机提供了此 SSL 服务器块,因此应该被阻止。

该代码不包含对您的服务器 IP 地址的特定引用,因此可以轻松地将其重新用于其他服务器配置而无需修改。

例子:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    ...

答案4

如果您只是想确保不返回任何数据,则可以使用以下不带证书的代码片段:

map "" $empty {
        default "";
}

server {
        listen 80 default_server;
        listen 443 ssl http2 default_server;
        listen [::]:80 default_server;
        listen [::]:443 ssl http2 default_server;

        server_name _;

        ssl_ciphers aNULL;
        ssl_certificate data:$empty;
        ssl_certificate_key data:$empty;

        return 444;
}

相关内容