Miskatonic University Press

How I set up a Tor bridge

conforguration privacy tor

The Tor Project had a call out for people to set up bridges to help fight censorship. Here’s how I set one up in about ninety minutes.

About bridges

There are three types of relays in the Tor network: guard, middle and exit. They all run the same program but have different purposes. Exits are configured specially and require more care to run: they are where requests come out from the Tor network and hit the open web. The non-exit relays (the Tor network decides whether one should be an entry-point guard or in the middle) don’t require much attention except to make sure the system is working and up to date.

Relays are no secret: the Tor Project provides lists of exit relays and much other data; see also this Tor node list and TorMap. This makes it possible for someone with control of a network (perhaps a country’s network) to deny access to the Tor network.

Bridges are a way to get around this. Tor’s censorship page says:

Bridge relays are Tor relays that are not listed in the public Tor directory.

That means that ISPs or governments trying to block access to the Tor network can’t simply block all bridges. Bridges are useful for Tor users under oppressive regimes, and for people who want an extra layer of security because they’re worried somebody will recognize that they are contacting a public Tor relay IP address.

A bridge is just a normal relay with a slightly different configuration.

Bridges are special in that their IP address are not shared, so they are much less likely to get blocked. People have to do a bit of work to find out how to connect to a bridge, and this is apparently enough to confound repressive governments, for a while at least. Eventually bridges can be found and blocked. That’s one reason why Tor always needs more.

The default configuration file explains a little more:

Bridge relays (or “bridges”) are Tor relays that aren’t listed in the main directory. Since there is no complete public list of them, even an ISP that filters connections to all the known Tor relays probably won’t be able to block all the bridges. Also, websites won’t treat you differently because they won’t know you’re running Tor. If you can be a real relay, please do; but if not, be a bridge!

A bridge will move a lot of bandwidth in and out, but nothing will get out to the public web. It should be able to just do its job quietly without attracting attention. That’s what I want to run.

Setting up the system

I began with a fresh Ubuntu 22.04 LTS machine as a virtual private server run at a hosting company. The use policy doesn’t allow Tor exit nodes, but this won’t be one.

I made an account for myself and gave it sudo access.

I set up ssh access, then tightened the OpenSSH server (sshd) by making sure these were set in /etc/ssh/sshd_config:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
StrictModes yes
MaxAuthTries 2

I set AllowUsers my_username as well. Only I can log in, only using my SSH key. That’s as secure as I can make that. (Change my_username to your username, of course.) I made sure my home directory and ~/.ssh/ were mode 700 and everything in that directory was mode 600.

(You can restart sshd with sudo /etc/init.d/ssh restart and this won’t kill off your login session. Run that in one window, where you stay logged in, and try connecting in another. If it works, great. If not, you’re still logged in and can fix it.)

Next I set up a firewall with UFW:

sudo ufw enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh

It warned me about doing that over ssh, but nothing bad happened.

The next step is to set up my environment. For this I used my own Conforguration, which is a set of configuration management tools done in Org. I use it to manage my dot files and to make it easy to install R, Ruby and Tor from source. These commands download Conforguration, set up some directories, and then install scripts and dot files.

git clone https://github.com/wdenton/conforguration.git ~/src/conforguration/
source ~/.bashrc

Bingo! The prompt changes and all my aliases work. My shell environment is just the way I like it and I feel at home. (If you try Conforguration yourself let me know how it goes.)

(I don’t usually install Conforguration scripts that way. My usual method is to do it all from inside Emacs on my laptop: I use a source code block in Org to run rsync to push the scripts to the other machine, then ssh to execute the scripts remotely. As so often with Org, once you have it set up right you just hit Ctrl-c Ctrl-c and magic happens.)

Setting up Tor

What’s next requires access to packages with source code, so I edited /etc/apt/sources.list to uncomment all of the deb-src lines, then I ran this to freshen everything up:

sudo apt update
sudo apt upgrade

Setting up a Tor server is part of Conforguration, so I just ran this to get it in place:


The first script installs a bunch of necessary stuff and then the second script gets and installs Tor from source. This took a little while. When it finished the Tor server was ready but not running. (If you don’t want to use Conforguration, just copy what’s in those scripts.)

From here on I followed the instructions on how to set up a bridge. I ran into some problems but worked around them.

First I needed to install obfs4. The glossary says, “Obfs4 is a pluggable transport that makes Tor traffic look random like obfs3, and also prevents censors from finding bridges by Internet scanning. Obfs4 bridges are less likely to be blocked than obfs3 bridges.” (I don’t find that understandable either. Worse, the definition of obfs3 is, “Obfs3 is a pluggable transport that makes Tor traffic look random, so that it does not look like Tor or any other protocol. Obfs3 is not supported anymore.”)

Nevertheless, it’s easy to install:

sudo apt-get install obfs4proxy


Tor servers are configured in a torrc file, and mine is in /usr/local/etc/torrc. The Ubuntu instructions have a sample torrc, but I think it could be better. Here’s what I’m using (minus comments that are in the example):

BridgeRelay 1
PublishServerDescriptor bridge

AddressDisableIPv6 1
SocksPort 0

ORPort 2112 IPv4Only

ServerTransportPlugin obfs4 exec /usr/bin/obfs4proxy
ServerTransportListenAddr obfs4

ExtORPort auto

ContactInfo William Denton <tor@williamdenton.org>
Nickname MyServerNicknameHere

Log notice file /usr/local/src/tor/log/notices.log

# Bandwidth
AccountingStart month 10 0:00
AccountingMax 450 GBytes
AccountingRule sum

The ORPort (where Tor talks) is on 2112 because Rush. I put the ServerTransportListenAddr[ess] on 443 because that’s where HTTPS normally is, so, as I understand it, incoming traffic to the bridge will be less noticeable because it will just blend in with other normal-looking web traffic.

Understanding the settings to manage bandwidth in Tor is not easy. The project needs a good torrc guide on its site, explaining all the options and what they mean. The information is in comments in the default torrc and in the man page, but that man page is on Ubuntu’s site—I can’t find it on Tor’s web site!

I looked at What bandwidth shaping options are available to Tor relays? but How can I limit the total amount of bandwidth used by my Tor relay? had the answers I needed, and I edited the torrc based on that. My server has 500 gigs of bandwidth per month (in and out, combined), so setting a limit of 450 (with AccountingMax 450 GBytes) seems safe. I’ll keep an eye on it. The advice is that a fast server that’s up some of the month is better than a slow server up all month, and the server is smart enough to manage that on its own, so I’ll let it do its work. Months here will begin on the tenth day at midnight, and AccountingRule sum means Tor is adding up traffic in and out.

The logging command sends the server’s notices to a file, which makes them easier to read than if they’re scrolling by on the screen. I needed to make the directory:

mkdir /usr/local/src/tor/log/

Running the bridge

Because I’m using port 443 I need to take a special step next.

sudo setcap cap_net_bind_service=+ep /usr/bin/obfs4proxy

That’s a new one to me. setcap allows one to “set file capabilities.” The capabilities man page says the cap_net_bind_service setting allows a program to “bind a socket to Internet domain privileged ports (port numbers less than 1024).” It seems the +ep makes this capability “effective” and “permitted,” which means the binary can do this binding. Putting all that together means I don’t need to be root to run this program and have it bind to port 443.

(A philosophical aside: Speaking of capabilities, it’s worth knowing about the capability approach of Amartya Sen and Martha Nussbaum. It “entails two normative claims: first, the claim that the freedom to achieve well-being is of primary moral importance and, second, that well-being should be understood in terms of people’s capabilities and functionings.”)

The instructions have some commands about systemctl and service but none of that worked for me, I think because I installed from source, not a package. But that’s no problem: I can run tor as myself, and to keep it running I can run it in a tmux session. This is managed by the third script from Conforguration:


Run this to attach to the session:

tmux attach

Then hit Ctrl-b 1 or Ctrl-b 2 or the like to move between windows. Tmux is great.

After running the script information began to scroll by, including this, with a link about the life cycle of a new relay.

[notice] You are running a new relay.
Thanks for helping the Tor network!
If you wish to know what will happen in the upcoming weeks regarding
its usage, have a look at https://blog.torproject.org/lifecycle-of-a-new-relay

[notice] Registered server transport 'obfs4' at '[::]:443'

Right away I used the TCP reachability test to make sure port 443 was working, and it was.

The log also gave me a link to check the status of the bridge (it’s here but that won’t work because I’m not sharing the ID), which a little while later said:

Bridge 123456789xxx advertises:

* obfs4: functional
  Last tested: 2024-04-05 00:55:33.26340596 +0000 UTC (26m26.501321898s ago)

Good! Everything seemed fine.

Finally, I ran this to check on open ports:

sudo apt install net-tools
sudo netstat --numeric --tcp --listen --program

It said (trimming some column headers):

Proto          Local Address      Foreign Addr  State     PID/Program name
tcp     0    0*     LISTEN    1093/tor
tcp     0    0*     LISTEN    739/sshd: /usr/sbin
tcp     0    0*     LISTEN    1093/tor
tcp     0    0*     LISTEN    653/systemd-resolve
tcp6    0    0 :::22              :::*          LISTEN    739/sshd: /usr/sbin
tcp6    0    0 :::443             :::*          LISTEN    1100/obfs4proxy

Aside from ssh (port 22) and DNS (port 53) everything is Tor-related. Good. The server is listening on port 443, just as I want.

I was confused about port 41357. Why was tor listening on localhost on that port? I asked about this on the Tor forums. (The port is different there because I’d restarted the server and it grabbed a new high port at random.) I will update when I have an answer.

To use ss (the replacement for netstat; I use the old program by habit) I would run this to see the same information, plus a little more about the programs running:

sudo ss --numeric --tcp --listen --processes

Traffic statistics

I recently discovered vnStat, which is a really useful command line tool for getting network traffic statistics. Here I run vnstat -h to have it show hourly stats. You can have it show daily or monthly or use -q for a general summary.

$ vnstat -h --iface ens3

 ens3  /  hourly

         hour        rx      |     tx      |    total    |   avg. rate
         00:00     49.38 MiB |   24.51 MiB |   73.89 MiB |  172.18 kbit/s
         01:00    235.54 MiB |  234.17 MiB |  469.72 MiB |    1.09 Mbit/s
         02:00    207.27 MiB |  188.72 MiB |  395.99 MiB |  922.73 kbit/s
         03:00    726.50 MiB |  734.80 MiB |    1.43 GiB |    3.41 Mbit/s
         04:00    922.43 MiB |  924.49 MiB |    1.80 GiB |    4.30 Mbit/s
         05:00    623.17 MiB |  613.17 MiB |    1.21 GiB |    2.88 Mbit/s
         06:00    516.51 MiB |  493.94 MiB |    0.99 GiB |    2.35 Mbit/s
         07:00    547.88 MiB |  532.52 MiB |    1.06 GiB |    2.52 Mbit/s
         08:00    856.94 MiB |  870.95 MiB |    1.69 GiB |    4.03 Mbit/s
         09:00    622.55 MiB |  624.93 MiB |    1.22 GiB |    2.91 Mbit/s

The tor-run.sh script runs speedometer, which shows a graph of bandwidth use marching by. I’ll probably work vnstat into it too. With these two scripts, and the log file, it’s easy to keep an eye on how busy the relay is.

For both of those you need to know the name of the network interface where the packets are moving. On this server it’s ens3, which I found by running


This reported

ens3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

and then a lot of technical details that show it’s online and moving traffic. The ens3 there is the name I needed. (The lo interface you will always see also listed is the loopback interface that lets a machine talk to itself.)

It’s also interesting to see the failed ssh login attempts. Here’s one way to do it:

$ sudo journalctl -u ssh -g "Invalid user" | sed -nE 's/.*user (.*) from.*/\1/p' | sort | uniq -c | sort -rn | head -10
    241 admin
    156 user
    147 ubuntu
     83 oracle
     83 debian
     62 test
     62 ftpuser
     45 usuario
     45 test2
     45 test1

So far the bridge is running well. I’ll report back about how it goes.


When it comes time to upgrade to the next Tor release, I’ll post about how I do that with Conforguration.

(If anyone notices any technical mistakes in any of this, I’d like to know so I can fix them—but up to the limit of getting a Tor bridge safely up and running, not arcane details about Linux networking.)

UPDATED 08 April 2024: This answer to my question explains about the high port: Because I configured ExtORPort auto (as the instructions set out) the Extended OR Port picks a random high port where it listens on localhost for connections from obfs4proxy. A regular Tor relay will have ORPort 9001 set, but this extended one does a little more.