Linuxword Global
当前位置: 通信协议 > Nginx反向代理,当后端为Https时的一些细节和原理

背景

最近在写一个管理后台,在参考阿里云CDN交互的时候,其中一个叫回源SNI的参数项引起了我的注意,

image-20210513190524400

虽然知道SNI且工作中Nginx上游也有用Https的情况,但并未留意过需要做特别配置,感觉可能触发到知识盲区,一番查询后发现Nginx有一个proxy_ssl_server_name参数与此相似。

1
2
3
4
5
6
7
Syntax:	proxy_ssl_server_name on | off;
Default:	
proxy_ssl_server_name off;
Context:	http, server, location
This directive appeared in version 1.7.0.

Enables or disables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server.

当端服务器为Https,反向代理时是否开启SNI,它的默认值竟然是off。一个IP绑定多个域名很常见,为什么默认不启用呢?

光看文档我还觉得不够,还去看了下源码,SNI的处理必调用SSL_set_tlsext_host_name函数,搜索SSL_set_tlsext_host_name函数就能快速定位相关逻辑。

image-20210513194354048

参数默认关闭,当开启时,在SSL握手的时候会把HostName传给上游服务器,以便上游服务器知道用哪个证书。

使用已经知道了,接下来来测试下。

验证

将我自己的博客做上游服务器做测试,拓扑如下:

-> www.dianduidian.com -> blog.dianduidian.com

先用最简单的配置

1
2
3
4
5
6
7
    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
        }
    }

没问题,能正常打开,我们抓包来看看Nginx是如何与上游服务器建立连接的。

image-20220518160850500

可以看到TLS握手阶段,Nginx在向上游服务器发送Client Hello消息时没有带上SNI信息,由此可以确认Nginx反向代理时,以HTTPS请求上游服务器时默认不启用SNI。

继续来看看上游服务器返回的证书信息

image-20220518160228427

可以看到服务器返回的不是blog.dianduidian.com域名的证书而是nginx配置的一个默认证书,这是由于没有启用SNI,所以TLS握手的时候,上游服务器不知道用那个域名的证书便使用了默认证书返回。

虽然证书返回的不对,但请求不受影响,我们推测Nginx反向代理时,以HTTPS请求上游服务器时默认不验证上游服务器返回的证书

通过查阅文档得知确实默认不验证上游证书

1
2
3
4
5
6
7
Syntax:	proxy_ssl_verify on | off;
Default:	
proxy_ssl_verify off;
Context:	http, server, location
This directive appeared in version 1.7.0.

Enables or disables verification of the proxied HTTPS server certificate.

默认不验证证书情况。

那我们启用下看看会发生什么?

先获取下CA文件,

1
 curl  https://curl.se/ca/cacert.pem  -o /etc/nginx/conf.d/cacert.pem

修改配置文件如下:

1
2
3
4
5
6
7
8
9
    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;          
        }
    }

image-20220518165329294

502了,查看下日志,报upstream SSL certificate verify error错误

1
2022/05/18 16:52:14 [error] 20325#1337445: *23 upstream SSL certificate verify error: (18:self signed certificate) while SSL handshaking to upstream, client: 127.0.0.1, server: sni.dianduidian.com, request: "GET / HTTP/1.1", upstream: "https://47.100.x.x:443/", host: "sni.dianduidian.com"

可以看到启用上游证书验证后Nginx确实会证书上游返回的证书,但是通过上边我们抓包可以看到上游返回的是一个Nginx配置的一个默认证书,这个证书是我们自签的,CA验证自然通过不了,但是如果CA是合法的,证书的CommonName不一致又会怎样呢,会不会验证不过呢?这里测试话需要多张合法的证书,比较麻烦,我们直接从源码中寻找答案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static void
ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,
    ngx_connection_t *c)
{
    long  rc;

    if (c->ssl->handshaked) {

        if (u->conf->ssl_verify) {
            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "upstream SSL certificate verify error: (%l:%s)",
                              rc, X509_verify_cert_error_string(rc));
                goto failed;
            }

            if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
                ngx_log_error(NGX_LOG_ERR, c->log, 0,
                              "upstream SSL certificate does not match \"%V\"",
                              &u->ssl_name);
                goto failed;
            }
        }

        c->write->handler = ngx_http_upstream_handler;
        c->read->handler = ngx_http_upstream_handler;

        ngx_http_upstream_send_request(r, u, 1);

        return;
    }

    if (c->write->timedout) {
        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
        return;
    }

failed:

    ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
}

image-20220518190512323

从源码中可以看到,开启上游证书验证后不仅会验证证书颁发机构的合法性,且会比对证书中commonName,即验证commonName的一致性。

下面来打开SNI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;  
          proxy_ssl_server_name on;
        }
    }

网站正常打开,这会抓包看看

image-20220518171121667

可以看到TLS握手阶段,Nginx在向上游服务器发送Client Hello信息中的扩展部分多出来一个server_name,告诉上游服务器应该用哪个证书信息响应。可以看到这里Server Name传的是blog.dianduidian.com,但是上边配置中我们并没有指定传哪个域名,这一块的逻辑是怎样的呢?

查阅文档得知其实是有另外一个参数proxy_ssl_name控制

1
2
3
4
5
6
7
8
9
Syntax:	proxy_ssl_name name;
Default:	
proxy_ssl_name $proxy_host;
Context:	http, server, location
This directive appeared in version 1.7.0.

Allows overriding the server name used to verify the certificate of the proxied HTTPS server and to be passed through SNI when establishing a connection with the proxied HTTPS server.

By default, the host part of the proxy_pass URL is used.

可以看到默认为$proxy_host,我们试着修改下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    server {
        listen       80;
        server_name  www.dianduidian.com;
        location / {
          proxy_pass https://blog.dianduidian.com;
          proxy_ssl_verify on;
          proxy_ssl_trusted_certificate /etc/nginx/conf.d/cacert.pem;  
          proxy_ssl_server_name on;
          proxy_ssl_name www.baidu.com;
        }
    }

抓包看看

image-20220518174226031

可以看到Server Name已经修改成www.baidu.com了。

总结

Nginx作用反向代理与上游服务器使用HTTPS建连时,

  1. 默认不启用SNI,使用proxy_ssl_server_name on;参数启用;
  2. 默认不验证上游服务器返回的证书,使用proxy_ssl_verify on;
  3. 开启上游证书验证后Nginx会使用配置文件中指定的CA验证上游服务器返回证书的合法性,同时也会比对证书中的CommonName信息。

「梦想一旦被付诸行动,就会变得神圣,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏
一分也是爱

支付宝扫一扫打赏

微信扫一扫打赏

上一篇:

下一篇:

相关推荐

博客简介

本站CDN采用VmShell免费提供离中国大陆最近的香港CMI高速网络做支撑,ToToTel打造全球最快速的边沿网络支撑服务,具体详情请见 :https://vmshell.com/ 以及 https://tototel.com/,网站所有的文件和内容禁止大陆网站搬迁复制,谢谢,VPS营销投稿邮箱: admin@linuxxword.com,我们免费帮大家发布,不收取任何费用,请提供完整测试文稿!

精彩评论

友情链接

他们同样是一群网虫,却不是每天泡在网上游走在淘宝和网游之间、刷着本来就快要透支的信用卡。他们或许没有踏出国门一步,但同学却不局限在一国一校,而是遍及全球!申请交换友链

站点统计

  • 文章总数: 2289 篇
  • 草稿数目: 12 篇
  • 分类数目: 6 个
  • 独立页面: 0 个
  • 评论总数: 2 条
  • 链接总数: 0 个
  • 标签总数: 5772 个
  • 注册用户: 139 人
  • 访问总量: 8,657,594 次
  • 最近更新: 2024年3月27日