WebTunnel bridge with Apache as reverse proxy

Hi folks!

Unfortunately the webtunnel bridge guide only seems to provide instructions on how to setup a webtunnel brige with nginx.
Are there also instructions somewhere for doing this with Apache? I cannot find any.

So far I have adapted the nginx configuration to Apache syntax, but the bridge seems not to work.

I’m on Linux and have build the webtunnel server executable from source.
After configuration and start of tor webtunnel instance verified that the webtunnel processes are running and listening on 127.0.0.1:15000.

Threw socat in to check what Apache is passing to the webtunnel executable:
socat stdio tcp-listen:15000,fork,
pressed the ‘Connect’ button in Tor Browser and got the following output:

GET / HTTP/1.1
Host: example.com
User-Agent: Go-http-client/1.1
Accept-Encoding: 
X-Real-IP: 123.123.123.123
X-Forwarded-Proto: https
X-Forwarded-For: 123.123.123.123
X-Forwarded-Host: example.com
X-Forwarded-Server: example.com
Connection: Keep-Alive

Replaced the host name with example.com and the client IP address with 123.123.123.123 here. The rest is unchanged.

Also tried to send this output to the webtunnel process as follows:
printf "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: \r\nX-Real-IP: 123.123.123.123\r\nX-Forwarded-Proto: https\r\nX-Forwarded-For: 123.123.123.123\r\nX-Forwarded-Host: example.com\r\nX-Forwarded-Server: example.com\r\nConnection: Keep-Alive\r\n\r\n" | socat stdio tcp:127.0.0.1:15000
without any response…

Any ideas what’s wrong? When there is no guide for Apache, I will post the whole configuration here then.

I found out there are two problems here. The first one is that the “magic path” doesn’t appear in the GET line which is required, but this is easy to solve.
The second and the main problem is that Apache doesn’t allow to modify the Connection and Upgrade headers which is required for using of WebSockets. Nginx allows this modification.
They must be set as follows:

Connection: Upgrade
Upgrade: websocket

But what you get from Apache (as you can see above) is

Connection: Keep-Alive

This requires some workaround. Bad decision of Apache devs…

I’ll be back…

I don’t know much about apache, sorry. But if you find a solution it will be nice to extend the documentation including the apache configuration. This is the source of the documentation if you want to send us a merge request:

Good luck with the research.

Hi meskio!

I already have the solution and a working WebTunnel on Apache :slight_smile:
I also want to contribute it to the docs, but do not have an account on GitLab yet.
But already requested and waiting for being unlocked.

2 Likes

Just for the case somebody will read this topic in the future and wants to know, how the HTTP headers to be sent to the webtunnel process have to look like (maybe for a setup of a WebTunnel bridge on another webserver/proxy):

GET /<RANDOM_STRING> HTTP/1.1
Host: <HOST_NAME>
User-Agent: Go-http-client/1.1
X-Real-IP: <CLIENT_IP_ADDRESS>
X-Forwarded-For: <CLIENT_IP_ADDRESS>
X-Forwarded-Proto: https
Connection: Upgrade
Upgrade: websocket

User-Agent is passed through from the browser.
<HOST_NAME> has to be the host name of the host running the proxy.
<RANDOM_STRING> is a placeholder for the random string generated by

echo $(cat /dev/urandom | tr -cd "qwertyuiopasdfghjklzxcvbnmMNBVCXZLKJHGFDSAQWERTUIOP0987654321"|head -c 24)

as mentioned in the guide.

<CLIENT_IP_ADDRESS> is a placeholder for the client (that’s where a browser is running) IP address.

I first thought that WebSocket usage is only required when some website uses them, but it turns out that the whole communication between Tor Browser and the webtunnel process happens through the WebSocket protocol. That’s why these two headers

Connection: Upgrade
Upgrade: websocket

have necessarily to be present on each connection.

If they are missing or have not the proper values, the upgrade to the Websocket protocol and that means any communication cannot happen. The webtunnel process simply doesn’t respond anything in this case.

I haven’t verified yet whether both headers X-Real-IP and X-Forwarded-For are required, because they both carry the same IP address and so one of them is redundant.
I have taken them both over from the Nginx configuration example of the guide.

1 Like

Sorry, folks, I forgot to post the config.
Was reminded via email notification caused by bakunin1848’s post.

Here is a working config:

<VirtualHost *:443>
    ServerName [[HOST_NAME]]

    Protocols http/1.1 h2

    SSLEngine on

    # Certificates generated via acme.sh
    SSLCertificateFile /root/.acme.sh/DOMAIN_ecc/fullchain.cer
    SSLCertificateKeyFile /root/.acme.sh/DOMAIN_ecc/DOMAIN.key

    SSLProtocol TLSv1.2 TLSv1.3
    SSLSessionCacheTimeout 900

    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384

    ProxyPreserveHost On
    ProxyRequests off

    Header always set Strict-Transport-Security "max-age=63072000"

    <Location /[[SECRET_PATH]]>
        # This lets Apache set the "X-Forwarded-For", "X-Forwarded-Host" and
        # "X-Forwarded-Server" HTTP headers automatically if enabled (Default is 'On').
        # "X-Forwarded-For" is set manually below to avoid the other two
        # headers provided by this directive that we don't need.
        ProxyAddHeaders Off

        # Set Proxy headers
        RequestHeader unset Accept-Encoding
        RequestHeader set X-Real-IP         "%{REMOTE_ADDR}s"
        RequestHeader set X-Forwarded-For   "%{REMOTE_ADDR}s"
        RequestHeader set X-Forwarded-Proto "%{REQUEST_SCHEME}s"
        Header        set Front-End-Https   on

        ProxyPass "ws://127.0.0.1:15000/%{REQUEST_URI}s"
        ProxyPassReverse "ws://127.0.0.1:15000%{REQUEST_URI}s"
    </Location>

    ErrorLog off
</VirtualHost>

Don’t forget to turn off the CustomLog (access log) also.
If you don’t know how, use a search engine to find out.
CustomLog Off doesn’t work.

You don’t need to use rewrite engine (@bakunin1848). This causes an unnecessary internal redirect in Apache. Use ws:// prefix directly in ProxyPass and ProxyPassReverse.

There seems to be a bug in Apache’s mod_proxy.

In this line

ProxyPass "ws://127.0.0.1:15000/%{REQUEST_URI}s"

the slash after the port number has to be present, else mod_proxy complains, it cannot parse the URL. This will cause a double slash as result, but it doesn’t break anything.
ProxyPassReverse does not have this bug.

Pls somebody add it to the webtunnel bridge documentation. I currently don’t have the time for it.

Edit:
Caution!
Even when CustomLog is not explicitly defined in a virtual host, Apache might still log accesses for such a virtual host, but to a file you maybe wouldn’t expect.

This file is on Debian/Ubuntu and derivatives (can differ on other OSes):

/var/log/apache2/other_vhosts_access.log

To disable logging to this file run the following command as root:

a2disconf other-vhosts-access-log

And then if Apache is already running:

systemctl reload apache2
1 Like

The simpler and more optimal the better. I delete my configurations so that there are no misunderstandings and I leave your configuration.

Webtunnel Bridge ReverseProxy with Apache Config by @escurso

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.