OpenVPN / openvpn

OpenVPN is an open source VPN daemon
http://openvpn.net
Other
10.91k stars 3k forks source link

http-proxy does not allow to connect ho https-proxy #633

Open mal19992 opened 1 week ago

mal19992 commented 1 week ago

Currently openvpn --http-proxy option only allows a HTTP proxy, i.e. it directly issues the CONNECT command to http proxy. There is no option to connect to a HTTP proxy not directly, but via TLS/SSL, a so called https-proxy option, a regular HTTP proxy behind TLS/SSL, it is rather easy to setup such with apache or nginx. This suggested --http-proxy-over-TLS option has three very important benefits:

  1. A local ISP cannot be able to eavesdrop the IP of actual VPN server during CONNECT phase and block connection proactively.
  2. Most important. Currently OpenVPN is frequently blocked/throttled by some ISPs. This option would allow to pass ISP blocking. A OpenVPN connection with http-proxy option currently gets blocked by ISP with exactly the same logic as used when it is connected directly. If OpenVPN connection would be encapsulated in HTTPS connection to the proxy -- it would be much more difficult to identify and block such a connection.
  3. A proxy often has basic type of authorization credentials. A connection to such proxy without SSL leaks proxy auth credentials.

This is a request for improvement. Tested on openvpn-2.6.12 and earlier, none support a HTTP proxy over TLS/SSL. This improvement feature is easy to implement and it does not affect other OpenVPN functionality.

cron2 commented 1 week ago

This improvement feature is easy to implement

Such claims should come with actual code that shows that "it is easy to implement".

mal19992 commented 1 week ago

I will try to look,but I am not very familiar with OpenVPN codebase

ordex commented 4 days ago

I agree with @cron2, however I believe this is an interesting feature and would be nice to have.

mal19992 commented 4 days ago

I reviewed the concept

  1. The concept works perfectly. I proved it with stunnel by running a separate TLS tunnel with stunnel.conf
    #engine = pkcs11
    foreground = yes
    [service]
    #engine = auto
    #engineId = pkcs11
    client = yes
    accept = 127.0.0.5:7999
    sni = proxy.xxxxxxxxxx.com
    connect = proxy.xxxxxxxxxx.com:443
    debug = 7
    # uncomment to enable SSL remote server certificate verification 
    #CAfile = /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
    #verify = 2
    #verifyChain = yes
    # to match sni
    #checkHost = proxy.xxxxxxxxxx.com

    and run it as stunnel stunnel.conf This example creates SSL connection to remote http proxy behind SSL proxy.xxxxxxxxxx.com:443, the unencrypted stream is then available locally at 127.0.0.5:7999. OpenVPN http proxy is now set to 127.0.0.5:7999

The result -- the ISP sees only regular https connection (from stunnel) to proxy.xxxxxxxxxx.com:443, the OpenVPN TCP goes under this TLS link. The ISP does not recognize this traffic as being from OpenVPN and does not throttle it. No problem whatsoever. This solution is the best to VPN traffic identification https://community.openvpn.net/openvpn/wiki/TrafficObfuscation among several I tried. It really works and is completely separate from OpenVPN internals. All it requires -- run apache http proxy behind SSL. Just one extra line with letsencrypt config.

  1. It is very inconvenient to run an extra tunnel. It is much easier to include to OpenVPN an ability to work with http proxy behind SSL. I think the best option would be not a separate proxy setting that I originally described in the first post, but, instead, a single boolean config flag http_proxy_over_ssl = true which makes to open TLS connection before opening http proxy. This is somewhere in

    --- a/src/openvpn/socket.c
    +++ b/src/openvpn/socket.c
    @@ -2068,8 +2068,15 @@ phase2_tcp_client(struct link_socket *sock, struct signal_info *sig_info)
    
         if (sock->http_proxy)
         {
    +           int proxy_sd=sock->sd
    +           if(sock->http_proxy && sock->http_proxy->options &&  sock->http_proxy->options->http_proxy_over_ssl){
    +               // FIXME wrap with SSL
    +               // we have connection open to proxy. now wrap it with TLS
    +               // proxy_sd= // FIXME open TLS connection to socket sock->sd. The  obtained fd gives an unencrypted stream. Pass it as it were a http proxy socket. 
    +           }
    +
             proxy_retry = establish_http_proxy_passthru(sock->http_proxy,
    -                                                        sock->sd,
    +                                                        proxy_sd,
                                                         sock->proxy_dest_host,
                                                         sock->proxy_dest_port,
                                                         sock->server_poll_timeout,

    It just replaces fd passed to establish_http_proxy_passthru in socket.c. With a regular http proxy -- just pass it as is. When http proxy is behind SSL -- it opens SSL connection to proxy destination, then use obtained fd of unencrypted contents is passes through.

  2. Basically a single boolean option http_proxy_over_ssl is sufficient for this feature. I would add a few more options affecting the TLS connection to http proxy --- just for convenience (and as a replacement to http basic auth)

    # Enable http proxy over SSL
    http_proxy_over_ssl = true
    # SSL host name to verify, do not verify host name if empty.  The same name is used as SNI for server request
    http_proxy_over_ssl_server_SNI = proxy.xxxxxxxxxx.com
    # Client certificate for proxy auth. Optionally use it instead of basic proxy auth 
    http_proxy_over_ssl_client_auth_cert = /etc/client.pem
    # http_proxy server SSL certificate to verify locally 
    http_proxy_over_ssl_server_cert = /etc/proxyserver.pem
    # Set Host: name for proxy. In virtual host environment often set the same as http_proxy_over_ssl_server_SNI 
    # The same host is used for sni and for http Host: to work with multiple virtual http servers on port 443 
    http_proxy_over_ssl_CONNECT_Host = proxy.xxxxxxxxxx.com

    Actually only a single option http_proxy_over_ssl = true is important, the other controls are to auth the proxy connection, this is a glorified "basic auth" for proxy sever. The options http_proxy_over_ssl_CONNECT_Host sets Host: field for http proxy when doing CONNECT, see #635

ordex commented 3 days ago

http_proxy_over_ssl_CONNECT_Host

this is not specific to httpS. However, in #635 it was found out that Host: should contain the target hostname, no?

mal19992 commented 3 days ago

It may be very beneficial to violate existing "traditions" for CONNECT and Host: having identical information. For example -- if a http proxy over TLS is running in a multi-host environment on a single IP port 443.

Then OpenVPN TLS link to is IP:443 may need to put "non-traditional" Host: from the parameter http_proxy_over_ssl_CONNECT_Host to help web server to identify specific virtual host (the proxy one) to use. If the parameter http_proxy_over_ssl_CONNECT_Host is absent -- the Host: is "traditionally" matching to CONNECT. If a user wants specific Host: to select a virtual server on a web server -- this option may be very handy. It is not needed if a TLS http proxy has its own IP or port, but useful in virtual host environment. By default it is not set and thus does not affect anything. If set --- it would allow to run multiple TLS http proxy on the same IP port 443. It has its own use cases.

In a multi-host virtual server environment this parameter need to be the same as http_proxy_over_ssl_server_SNI for apache httpd to identify the proper virtual host with a proxy server to use.

The parameters http_proxy_over_ssl_server_SNI (to send SNI and the same name to verify SSL certificate) and http_proxy_over_ssl_CONNECT_Host (the parameter to send Host: in HTTP header) must be identical in virtual host server setup with SSL (many web sites and proxies on a single IP port 443), otherwise the following error is possible: [Error] Hostname example.com provided via SNI and hostname www.example.com provided via HTTP are different There is no such issues for a proxy server running on its own IP adderess