Create a Tor WebTunnel bridge
To assist users behind restrictive firewalls, the Tor Project has developed pluggable transports. These obfuscate the connection between the user and the Tor network. WebTunnel is one such pluggable transport. It wraps the payload in a WebSocket-like HTTPS connection. This gives the impression that the user is simply browsing the web. This article shows you how to set up a Tor WebTunnel bridge, either for public distribution to users behind firewalls, or for your own private use.
1. Set up web server
If you already have a working web server, and you have done all the steps below, you can skip this section and go directly to section 2.
1.1. Create DNS records
You will need to purchase a domain name to implement the configuration in this article.
Example:
xjkj8.xyz
Add your domain to Cloudflare.
1.2. Set up hostname
Choose a subdomain that will be the hostname for your server.
Example:
wt.xjkj8.xyz
Wherever you see this example in what follows, change it to your own chosen hostname (subdomain).
Go to the Cloudflare DNS page for your domain. Add a DNS A
record (or AAAA
for IPv6) pointing from your subdomain (i.e. hostname) to your virtual private server IP address.
Turn the DNS record's proxy status off for now. The Cloudflare icon for an unproxied record (DNS only) is a gray cloud.
Click Save to save the new record you just added.
1.3. Open firewall
Open your server's firewall and/or security groups for input:
tcp/80
for HTTP input and SSL hostname validationtcp/443
for HTTPS input
1.4. Install Nginx
SSH into your server as root
.
The easiest way to install Nginx is to use your Linux distribution's repositories.
apt update && apt upgrade -y && apt autoremove -y
apt install nginx -y
Confirm that the default configuration is active (running)
:
systemctl status nginx
1.5. Create directory for web content
Create a new directory for the web root of your chosen hostname:
mkdir -p /var/www/wt.xjkj8.xyz/html
1.5. Create virtual host
Create a configuration file for a new site with your chosen hostname:
vi /etc/nginx/sites-available/wt.xjkj8.xyz
Insert contents like this, substituting in your own hostname:
server { listen 80; server_name wt.xjkj8.xyz; root /var/www/wt.xjkj8.xyz/html; index index.html; location / { try_files $uri $uri/ =404; } }
Save the file.
Enable the new virtual host by creating a symbolic link in /etc/nginx/sites-enabled
:
ln -s /etc/nginx/sites-available/wt.xjkj8.xyz /etc/nginx/sites-enabled/
1.6. Restart Nginx
Restart Nginx with your new virtual host:
systemctl restart nginx
Check that Nginx is still active (running)
:
systemctl status nginx
1.7. Add camouflage web content
Add some web content to /var/www/wt.xjkj8.xyz/html
. In the example given here, we will use PavelDoGreat/WebGL-Fluid-Simulation. Download the content:
curl -L https://github.com/PavelDoGreat/WebGL-Fluid-Simulation/archive/refs/heads/master.zip -O
Extract the archive:
apt install -y unzip
unzip master.zip
Copy the web content into place:
cp -r WebGL-Fluid-Simulation-master/* /var/www/wt.xjkj8.xyz/html
ls -l /var/www/wt.xjkj8.xyz/html
1.8. Obtain SSL certificate
Install the snap daemon:
apt install -y snapd
Install the certbot
snap:
snap install --classic certbot
Create a symbolic link to the certbot
binary:
ln -s /snap/bin/certbot /usr/bin/certbot
Invoke certbot
. As always, replace the example hostname by your actual hostanme in the instruction below:
certbot --nginx --agree-tos --register-unsafely-without-email -d wt.xjkj8.xyz
The certificate is saved at /etc/letsencrypt/live/wt.xjkj8.xyz/fullchain.pem
. The key is saved at /etc/letsencrypt/live/wt.xjkj8.xyz/privkey.pem
.
Test certificate renewal with a dry run:
certbot renew --dry-run
Restart Nginx with your SSL virtual host:
systemctl restart nginx
Check that Nginx is still active (running)
:
systemctl status nginx
1.9. Turn on Cloudflare full settings
Now you can turn on the final configuration of Cloudflare.
On the DNS page, turn on your subdomain's proxy status. The Cloudflare icon turns to an orange cloud. Click Save to save your change.
On the SSL/TLS page, select Custom SSL/TLS, and configure Cloudflare to use the Full (Strict) option for your domain. Click Save to save your change.
1.10. Test web server
At this stage, open any browser and verify that you can connect to your web site, and that it displays the expected web content. For example:
https://wt.xjkj8.xyz
2. Add location block to virtual host configuration
When clients visit your web server, they will be connected to your WebTunnel proxy if they request the secret path.
2.1. Generate secret path
Generate a pseudorandom path by running this command:
< /dev/urandom tr -dc a-z0-9 | head -c${1:-24};echo;
Example:
ujonu8rp58uuz9du1sxo6tb1
2.2. Add location block to virtual host
Edit the configuration file for your virtual host:
vi /etc/nginx/sites-available/wt.xjkj8.xyz
It now includes an SSL server
block listening on port tcp/443
, generated by certbot
. Insert in this server
block a new location
block, replacing the path in the example by your actual path:
location = /ujonu8rp58uuz9du1sxo6tb1 { proxy_pass http://127.0.0.1:10000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Accept-Encoding ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; add_header Front-End-Https on; proxy_redirect off; access_log off; error_log /dev/null; }
Note that WebTunnel is deliberately hidden, so you do not need to open port tcp/10000
in your firewall.
Save the virtual host configuration file.
2.3. Reload Nginx
Reload Nginx with your new configuration:
systemctl reload nginx
3. Build WebTunnel server binary
3.1. Install Golang
Install Git and the Go language compiler:
apt update && apt upgrade -y && apt autoremove -y
apt install -y git golang
Check your installation:
git -v
go version
3.2. Clone WebTunnel source code
Clone the WebTunnel git repository:
git clone https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/webtunnel
3.3. Compile WebTunnel
Compile the WebTunnel server
binary:
cd webtunnel/main/server
go build
3.4. Install WebTunnel
Copy the server
binary to a location in your execution path:
cp server /usr/local/bin/webtunnel
4. Install Tor
Install the prerequisite packages:
apt install -y gnupg apt-transport-https lsb-release
Determine your distribution's code name:
lsb_release -cs
Example:
bookworm
Create a new repository list for the Tor Project:
vi /etc/apt/sources.list.d/tor.list
Insert contents like this, replacing bookworm
by your actual distribution code name:
deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org bookworm main
deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org bookworm main
Save the file.
Download and import the Tor Project keyring:
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
Update your package metadata due to the new repositories:
apt update
And install Tor:
apt install -y tor deb.torproject.org-keyring
5. Configure Tor
Edit your Tor configuration file:
vi /etc/tor/torrc
Delete the existing contents.
Insert new contents modeled on the following:
BridgeRelay 1 ORPort 127.0.0.1:auto AssumeReachable 1 ServerTransportPlugin webtunnel exec /usr/local/bin/webtunnel ServerTransportListenAddr webtunnel 127.0.0.1:10000 ServerTransportOptions webtunnel url=https://wt.xjkj8.xyz/ujonu8rp58uuz9du1sxo6tb1 ExtORPort auto ContactInfo [email protected] Nickname bridgexjkj8 SocksPort 0
Replace the hostname, path, email address, and server nickname by your actual values.
By default, the bridge will be distributed to whoever requests it. If you want the bridge to be reserved for your private use, then append a line:
BridgeDistribution none
Save the file.
Restart tor
:
systemctl restart tor@default
systemctl status tor@default
6. Configure AppArmor
Determine if AppArmor is running on your system:
apparmor_status
If so, edit the AppArmor system_tor
profile, which is in the file /etc/apparmor.d/system_tor
:
vi /etc/apparmor.d/system_tor
Add an entry before the closing curly brace }
allowing tor
to execute webtunnel
:
# During startup, tor (as root) tries to open various things such as # directories via check_private_dir(). Let it. /usr/local/bin/webtunnel ix,
Save the file.
Reload the AppArmor profile:
apparmor_parser -r /etc/apparmor.d/system_tor
7. Review log
Check the logs for the tor@default
systemd service unit:
journalctl -e -u tor@default
You may see a line complaining about your IPv4 ORPort address 127.0.0.1
. This is fine, because Nginx will listen for external connections and pass them to 127.0.0.1
.
To confirm your bridge is running with no issues, you should see something like this:
[notice] Bootstrapped 100%: Done
Display your Tor installation's fingerprint:
cat /var/lib/tor/fingerprint
Example:
bridgexjkj8 16803...EDBA3
Display your Tor installation's hashed fingerprint:
cat /var/lib/tor/hashed-fingerprint
Example:
bridgexjkj8 A07CA...89047
You can monitor your WebTunnel bridge's status on Relay Search. Just enter your bridge's hashed fingerprint (without the nickname) and click Search. It will take about three hours for your bridge to show up after you first create it.
8. Test WebTunnel bridge
If your bridge is running, you can test it by copying and pasting your bridge line into Tor Browser.
Build a bridge line modeled on the following:
webtunnel 10.0.0.2:443 16803...4EDBA3 url=https://wt.xjkj8.xyz/ujonu8rp58uuz9du1sxo6tb1
Replace the fingerprint, hostname, and secret path in the above. 10.0.0.2:443
is just a meaningless IP address and port that are never used for anything. They are there only because the pluggable transport syntax requires them.
When you start Tor Browser, click Configure Connection.
Scroll down to where it says Enter bridge address you already know
, and click Add new bridges....
Enter your WebTunnel bridge line, and click Next.
Click Connect.
To test your connectivity, follow the steps of the Standard testing procedure.