Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxy_connect: use the default_server or SNI selected server block #1797

Closed
wants to merge 5 commits into from

Conversation

chobits
Copy link
Member

@chobits chobits commented Jun 28, 2023

try to fix #1794.
In the CONNECT request, the host field is used to specify the backend server, rather than for selecting a server block.

@Homqyy
Copy link

Homqyy commented Jul 10, 2023

问题描述

该补丁会导致在没有开启TLS的情况下出现问题。

比如有如下配置:

    server {
        listen 127.0.0.1:9090 default_server;

        server_name localhost;

        location / {
                return 501;
        }
    }

    server {
        listen 127.0.0.1:9090;

        server_name auth.example.com;

        proxy_connect;
        proxy_connect_allow 443 80;
    }

执行命令:curl -v -x http://127.0.0.1:9090 https://auth.example.com,会得到以下结果:

* Rebuilt URL to: https://auth.example.com/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 9090 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to auth.example.com:443
> CONNECT auth.example.com:443 HTTP/1.1
> Host: auth.example.com:443
> User-Agent: curl/7.61.1
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 405 Not Allowed
< Server: Tengine/2.4.1
< Date: Mon, 10 Jul 2023 07:04:49 GMT
< Content-Type: text/html
< Content-Length: 577
< Connection: keep-alive
< 
* Received HTTP code 405 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
curl: (56) Received HTTP code 405 from proxy after CONNECT

可以看到,即便Host命中了auth.example.com也没有效果,生效的配置是default_server

问题原因

HTTPS在TLS的协商阶段,Nginx会通过SNI去获取对应的location配置,在函数ngx_http_ssl_servername()中。但在HTTP的情况下则不能,因此仍旧需要在ngx_http_set_virtual_server()中通过ngx_http_find_virtual_server()去更新location。否则在虚拟服务器下,会使用server的location。

针对Host在CONNECT中存在问题的讨论

目前面临的问题如下:

  1. CONNECT请求中,Host头部值是后端服务器,如果应用了虚拟服务器(server_name)且恰好Host的值命令了其他虚拟服务器的server_name,会导致在find_location的时候应用到别的虚拟服务器的配置上了。
  2. TLS之上的CONNECT请求中,SNI提供了查找location的依据,但会与Host出现冲突。倘若此时打开证书认证功能,那么会报错而无法继续运行。反之如果没有打开的话,也会出现1中所诉的问题。

针对上述问题,发表想法:

  1. CONNECT的场景中,只以自身的配置为选择,不通过host去重新查找配置
  2. CONNECT的场景中,虚拟服务器似乎没有什么价值,是否可以直接禁用掉

修正临时补丁

目前暂没有好的解决思路,针对此补丁有以下两个临时修正方案:

  1. PROXY CONNECTserver {}中不要使用虚拟服务器,这样在HTTP的场景下也能正常使用
  2. 仅在r->method == NGX_HTTP_CONNECT && hc->ssl_servername条件下才跳过:
static ngx_int_t
ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
{
    ngx_int_t                  rc;
    ngx_http_connection_t     *hc;
    ngx_http_core_loc_conf_t  *clcf;
    ngx_http_core_srv_conf_t  *cscf;

#if (NGX_SUPPRESS_WARN)
    cscf = NULL;
#endif

    hc = r->http_connection;

#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)

    if (hc->ssl_servername) {

#if (NGX_HTTP_PROXY_CONNECT)
        if (r->method == NGX_HTTP_CONNECT) {
            return NGX_OK;
        }
#endif // if (NGX_HTTP_PROXY_CONNECT)

        if (hc->ssl_servername->len == host->len
            && ngx_strncmp(hc->ssl_servername->data,
                           host->data, host->len) == 0)
        {
#if (NGX_PCRE)
            if (hc->ssl_servername_regex
                && ngx_http_regex_exec(r, hc->ssl_servername_regex,
                                          hc->ssl_servername) != NGX_OK)
            {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return NGX_ERROR;
            }
#endif
            return NGX_OK;
        }
    }

#endif

省略...
}

@Homqyy
Copy link

Homqyy commented Jul 10, 2023

抱歉,重新阅读了下http_proxy_connect.t测试脚本:

unlike(http_connect_request('sKip-this-server.com', '8081', '/'), qr/500 Internal Server Error/, 'skip ngx_http_set_virtual_server()');

由该用例可以看出,你似乎是特意要跳过location的匹配的,也就是废除了虚拟服务器。

那我在这里加个提醒吧:如果应用该补丁的话,请不要在PROXY CONNECT中使用虚拟服务器!

@chobits
Copy link
Member Author

chobits commented Jul 10, 2023

抱歉,重新阅读了下http_proxy_connect.t测试脚本:

unlike(http_connect_request('sKip-this-server.com', '8081', '/'), qr/500 Internal Server Error/, 'skip ngx_http_set_virtual_server()');

由该用例可以看出,你似乎是特意要跳过location的匹配的,也就是废除了虚拟服务器。

那我在这里加个提醒吧:如果应用该补丁的话,请不要在PROXY CONNECT中使用虚拟服务器!

Its a trade-off that if you use this patch, only default_server can serve the CONNECT request and its established tunnel.
So we has not merged it which will introduce a change for default action

@chobits chobits closed this Jul 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

proxy_connect: SNI checking failed when ssl_verify_client is enabled
2 participants