chobits / ngx_http_proxy_connect_module

A forward proxy module for CONNECT request handling
BSD 2-Clause "Simplified" License
1.8k stars 493 forks source link

Can it used as a https proxy to tunnel tcp? #22

Closed friskfly closed 6 years ago

friskfly commented 6 years ago

I tried in ssl server block , but not succeed

chobits commented 6 years ago

hi, this module is used to handle CONNECT-request tunnel, this request is plain request (not over ssl). Seems that there is no client (webbrowsers / curl ) sending CONNECT request over SSL layer.

So if u want to use this module, config it in non-ssl server.

For more details of CONNECT tunnel, see: https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling

friskfly commented 6 years ago

Chrome and Firefox has https proxy support. other web client can use nghttpx to translate http to h2 , I'm now use nghttpx as frontend server, and nginx with this module in backend . this is a workaround way. If the moudle can directly support CONNECT request over ssl , i can just use nginx 😀. see this blog https://wzyboy.im/post/1052.html

chobits commented 6 years ago

Thanks for sharing this idea, let me look deep into it this weekend:)

chobits commented 6 years ago

hi @friskfly , I have made connect tunnel over ssl work. I'm currenty using chrome-https-proxy-setting with ngx_http_proxy_connect_module to write this comment :)


data stream diagram:

1. create tunnel:

[SSL] + [CONNECT request] --> nginx --TCP connection --> backend

2. proxy data stream via tunnel

[SSL] + [proxied data stream ( maybe http reqeust /https request)] --> nginx -- [proxied data stream ( maybe http reqeust /https request)] --> backend

How to use ngx_http_proxy_connect_module as https proxy

ngx_http_proxy_connect_module configure:

config Nginx server to handle CONNECT tunnel over ssl in port 8443

    server {
        listen       8443 ssl;

        server_name  localhost;

        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        ssl_certificate     /Users/xiaochen/work/nginx/cert.pem;
        ssl_certificate_key /Users/xiaochen/work/nginx/cert.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;

        resolver 8.8.8.8;

        ### connect tunnel
        proxy_connect;
        proxy_connect_allow            443 563;
        proxy_connect_connect_timeout  10s;
        proxy_connect_read_timeout     10s;
        proxy_connect_send_timeout     10s;

        location / {
            proxy_set_header Host $host;
            proxy_pass http://$host;
        }
    }

Google Chrome https proxy setting

Note that I use chrome extension SwitchyOmega to control proxy setting. Select HTTPS in protocol option.

image

Google Chrome Error: ERR_PROXY_CERTIFICATE_INVALID

The main problem, I try to resolve, is chrome reporting error: ERR_PROXY_CERTIFICATE_INVALID. If you have the same problem, I can show how to fix.

Note that I'm using macOS Sierra(version 10.12.6) and Goolge Chrome (Version 62.0.3202.94 (Official Build) (64-bit)).

  1. use the following script to generate SSL certificate & key.
$ cat genkey.sh
#!/bin/bash

openssl req \
  -newkey rsa:2048 \
  -x509 \
  -nodes \
  -keyout cert.key \
  -new \
  -out cert.pem \
  -subj /CN=localhost \
  -reqexts SAN \
  -extensions SAN \
  -config <(cat /System/Library/OpenSSL/openssl.cnf \
       <(printf '[SAN]\nsubjectAltName=DNS:localhost')) \
  -sha256 \
  -days 3650
$ ./genkey.sh
$ ls
cert.key   cert.pem
  1. install generated certificate cert.pem as a trusted root certificate on macOS:
1 Open Keychain Access
2 Choose "System" in the "Keychains" list
3 Choose "Certificates" in the "Category" list
4 Choose "File | Import Items..."
 Browse to the file created above, "cert.pem", select it, and click "Open"
 Select your newly imported certificate in the "Certificates" list.
5. Click the "i" button, or right click on your certificate, and choose "Get Info"
 Expand the "Trust" option
 Change "When using this certificate" to "Always Trust"
 Close the dialog, and you'll be prompted for your password.

For more details, see https://stackoverflow.com/questions/7580508/getting-chrome-to-accept-self-signed-localhost-certificate.

Here is my configure screenshot:

image

friskfly commented 6 years ago

@chobits I tried again , with listen 443 ssl is ok , but when I configure with listen 443 ssl http2; ,chrome shows tunnel failed . There maybe some problem work with http2 .

chobits commented 6 years ago

hi @friskfly

Yes, this is known issue in my TODO-list.

This is a long term plan, I dont have time to deal with this currently.


At least three points we should pay attention to:

  1. This module only patches HTTP status line parsing function for parsing CONNECT method. HTTP2 module has its own parsing function, which is not patched by this module.
  2. how to notify client that this module has established tunnel (maybe return 200 establish, not sure)
  3. how to upgrade client HTTP2 connection to TCP stream tunnel (maybe upgrade one HTTP stream not the whole connection, not sure)
friskfly commented 6 years ago

I see. Thx for your reply.

chobits commented 6 years ago

I am closing this issue, the TODO issue for h2 is added, see https://github.com/chobits/ngx_http_proxy_connect_module/issues/25.

chaoqing commented 6 years ago

Hi, I am building a https proxy to block the ads working with PAC. The idea is all https://ads.domain/any/uri will be forward proxy to nginx server and it always return empty_gif or status 204. But after I configure my nginx default.conf as follow, https_proxy=my.server:8080 curl https://www.baidu.com still return the original webpage.

    server {
        listen       8080 ssl;

        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        ssl_certificate     /my/nginx/cert.pem;
        ssl_certificate_key /myk/nginx/cert.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;

        resolver 8.8.8.8;

        ### connect tunnel
        proxy_connect;
        proxy_connect_allow            443 563;
        proxy_connect_connect_timeout  10s;
        proxy_connect_read_timeout     10s;
        proxy_connect_send_timeout     10s;

        location / {
            empty_gif;
        }
    }

Please help me with this.

shell1986 commented 2 years ago

Hi, is it possible to transfer the real ip address of the user through the https proxy?

tomoncle commented 2 years ago

无法启用 ssl

patrickkon commented 2 years ago

I have the same problem

无法启用 ssl

  • nginx : 1.16.1
  • OS: centos7
  • nginx info:
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --with-threads --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-compat --with-file-aio --with-http_v2_module --add-dynamic-module=/home/tomoncle/ngx_http_proxy_connect_module

正向代理配置

server {
    listen                         6443 ssl;

    ssl_protocols                  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers                    AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
    ssl_certificate                /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key            /etc/nginx/ssl/nginx.key;
    ssl_session_cache              shared:SSL:10m;
    ssl_session_timeout            10m;

    resolver 114.114.114.114;

    ### connect tunnel
    proxy_connect;
    proxy_connect_allow            443 563;
    proxy_connect_connect_timeout  10s;
    proxy_connect_read_timeout     10s;
    proxy_connect_send_timeout     10s;

    location / {
        proxy_set_header           Host $host;
        proxy_pass                 http://$host;
    }
}

测试

[root@5c5b6 ~]# curl -x 127.0.0.1:6443 -v www.baidu.com
* About to connect() to proxy 127.0.0.1 port 6443 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 6443 (#0)
> GET HTTP://www.baidu.com/ HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 400 Bad Request
< Server: nginx/1.16.1
< Date: Thu, 23 Dec 2021 01:46:48 GMT
< Content-Type: text/html; charset=utf-8,gbk
< Content-Length: 255
< Connection: close
< 
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.16.1</center>
</body>
</html>
* Closing connection 0
[root@5c5b6 ~]# curl -x 127.0.0.1:6443 -v https://www.baidu.com
* About to connect() to proxy 127.0.0.1 port 6443 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 6443 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 400 Bad Request
< Server: nginx/1.16.1
< Date: Thu, 23 Dec 2021 01:46:54 GMT
< Content-Type: text/html; charset=utf-8,gbk
< Content-Length: 255
< Connection: close
< 
* Received HTTP code 400 from proxy after CONNECT
* Connection #0 to host 127.0.0.1 left intact
curl: (56) Received HTTP code 400 from proxy after CONNECT

问题

当我关闭ssl 时(配置文件第一行删除ssl参数)代理工作正常 当我启用ssl时(如上配置)出现以上的测试问题

我看您这边配置成功了,是可以支持 客户端 -> HTTPS -> 代理服务 -> HTTP/HTTPS -> 目标服务器 , 但是我不知道问题出在哪里,能否给出一些意见。谢谢

I have the same problem too. "client sent plain HTTP request to HTTPS port while reading client request headers,"

chobits commented 1 year ago

I have the same problem

无法启用 ssl

....

问题

当我关闭ssl 时(配置文件第一行删除ssl参数)代理工作正常 当我启用ssl时(如上配置)出现以上的测试问题 我看您这边配置成功了,是可以支持 客户端 -> HTTPS -> 代理服务 -> HTTP/HTTPS -> 目标服务器 , 但是我不知道问题出在哪里,能否给出一些意见。谢谢

I have the same problem too. "client sent plain HTTP request to HTTPS port while reading client request headers,"

hi, your configuration is right, but ur constructed curl command is not right, see my latest documentation here: https://github.com/chobits/ngx_http_proxy_connect_module#example-for-curl-connect-request-in-https

image

$ curl https://nginx.org/ -sv -o/dev/null -x https://<your-nginx-ip>:<your-nginx-port> --proxy-insecure