Campus Tor Relay Deployment SOP¶
This procedure supplements how to run a Tor relay with what is specific to a university network. It comes from community member NZ's deployment at National Taiwan Normal University, and works alongside the proposal template and the FAQ.
If you are still working out whether to do this or how to persuade the institution, go back to the proposal template. This page assumes approval is in hand.
How a campus differs from a personal deployment¶
Several structural differences drive the technical choices:
- The IP address comes from the institution, usually one static IPv4 address through a formal request
- IPv6 needs a separate request, since most campuses do not enable it by default
- Outbound connectivity is blocked by default on an academic network, so every externally reachable service goes through an exception process
- SSH should be restricted to the campus VPN range rather than exposed to the internet
- The institution expects everything to be explicable: every open port and every traffic flow should map to something in the proposal
- Long-term operation has to survive graduation, so
ContactInfodoes not go to a personal address
Scope and non-scope¶
When talking to an IT centre, stating what you will not do matters more than stating what you will.
In scope
- A non-exit relay only, functioning as guard and middle relay
- One public artifact: a node status page containing no user data
Out of scope
- No VPN, proxy, or Tor client services (no SocksPort, no DNSPort)
- No onion services
- No changes to the campus border firewall, DNS resolution, or content filtering policy
- No deep packet inspection
Reversibility
- Offline within 10 minutes at any time: stop the service and close the ORPort
Services and connections¶
Inbound¶
| Service | Port and protocol | Source | Notes |
|---|---|---|---|
| Tor ORPort | 9001/tcp | Internet | TLS between relays, the only strictly required inbound port |
| HTTP (ACME) | 80/tcp | Internet | Let's Encrypt issuance and a 301 redirect |
| HTTPS (status page) | 443/tcp | Internet | The public status page |
| SSH | 22/tcp | Campus VPN range | Management only |
| DirPort | Closed | – | Left disabled to reduce attack surface |
The principle: expose exactly one Tor-related TCP port. If 443 is free, moving the ORPort to 443 makes the relay easier to reach from heavily censored networks.
Outbound¶
| Purpose | Port and protocol | Destination |
|---|---|---|
| Directory consensus and contacting other relays | TCP 443 and 9001, dynamic | Directory authorities and relays |
| System updates | TCP 80 and 443 | Package mirrors |
| Time synchronization | UDP 123 | Designated NTP server |
Explicitly blocked¶
- All UDP except the designated NTP destination
- Mail ports: SMTP 25, 465, 587
- IRC 6667 and 6697, and common peer-to-peer ports
- SocksPort, DNSPort, and HTTPTunnelPort all left disabled
Operating system baseline¶
Ubuntu Server 24.04 LTS, minimal install:
- Minimal packages:
tor,nginx,certbot,fail2ban,unattended-upgrades,rsyslog,auditd,chrony, plusprometheus-node-exporterif needed - SSH configuration
- Keys only:
PasswordAuthentication no - No direct root login:
PermitRootLogin no - Restricted users:
AllowUsers <account> - Timeout:
ClientAliveInterval 300
- Keys only:
- Automatic security updates through
unattended-upgrades - Time synchronization with
chrony, logging in UTC so incident timelines line up - Hardening: AppArmor enabled,
fs.protected_*parameters set - Persistent logs:
journaldwithStorage=persistent, optionally forwarded to an institutional SIEM throughrsyslog - File integrity (optional): AIDE with periodic baseline comparison
- Disk encryption (optional): LUKS on the whole disk where the hardware allows, or at least protecting
/var/lib/tor - Backups: offline or off-site copies of
/etc/tor/torrcand/var/lib/tor/keys/, with the identity key kept strictly confidential
Installing Tor¶
- Package source: prefer the Tor Project's own repository, since distribution packages usually lag by a version or two
- Service account: run as the packaged
debian-toraccount, never as root - Non-exit only: declare it explicitly with
ExitRelay 0andExitPolicy reject *:*, so nothing mistakes it for an exit relay
torrc reference¶
At /etc/tor/torrc:
# Identity
Nickname <YourSchoolRelayName>
ContactInfo <[email protected]> - Non-exit relay at <Dept/School>; Abuse: <[email protected]>
# Guard and middle only, never exit
ExitRelay 0
ExitPolicy reject *:*
# ORPort: the only required inbound service
ORPort 0.0.0.0:9001
# No directory mirror
DirPort 0
# No local SOCKS proxy
SocksPort 0
SocksPolicy reject *
# Control port, local only, for nyx and operations
ControlPort 127.0.0.1:9051
CookieAuthentication 1
CookieAuthFileGroupReadable 1
DataDirectoryGroupReadable 1
# Bandwidth, adjusted to institutional policy
# Example: 80 MB/s sustained, 120 MB/s burst
RelayBandwidthRate 80 MB
RelayBandwidthBurst 120 MB
# Logging at notice level, avoiding sensitive detail
Log notice file /var/log/tor/notices.log
# MetricsPort (optional): local or internal monitoring only, never exposed
# MetricsPort 127.0.0.1:9035
# MetricsPortPolicy accept 127.0.0.1
On ContactInfo:
- Do not use a personal address. A project or society address survives graduation
- Include an abuse contact so the Tor network health team can reach someone
- This field appears publicly in Relay Search. Treat it as public information
On MetricsPort:
MetricsPort lets Prometheus scrape relay metrics, and the Tor Project strongly advises against exposing it. For remote scraping, restrict the source strictly through both the firewall and MetricsPortPolicy, behind a TLS proxy.
Firewall¶
Default policy:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw logging on
SSH restricted to the campus VPN range (replace <campus CIDR>):
sudo ufw allow from <campus CIDR> to any port 22 proto tcp comment 'SSH from campus VPN'
sudo ufw limit 22/tcp comment 'Rate-limit SSH'
Web (status page):
sudo ufw allow 80/tcp comment 'HTTP for ACME/redirect'
sudo ufw allow 443/tcp comment 'HTTPS status site'
Tor ORPort:
sudo ufw allow 9001/tcp comment 'Tor ORPort'
MetricsPort, closed by default, opened only to an internal monitoring host:
sudo ufw allow from <monitoring host IP> to any port 9035 proto tcp comment 'Prometheus -> Tor MetricsPort'
Enable and verify:
sudo ufw enable
sudo ufw status numbered
The public status page¶
Letting the institution and outsiders confirm the relay is running helps build trust. Either architecture works.
Option A: Nginx and Onionoo (simple, low risk, recommended)¶
- Nginx serves an HTTPS status page
- The page pulls public data for your own fingerprint from the Tor Onionoo API: traffic, flags, uptime
- Only public information is shown, and no internal endpoint is exposed
Advantages: simple, no internal risk, purely client-side. Disadvantage: less flexible charting.
Option B: MetricsPort, Prometheus, and Grafana (full observability)¶
- Enable
MetricsPort 127.0.0.1:9035locally, never externally - Scrape
/metricsfrom Prometheus locally or on the internal network - Grafana for dashboards
- Only Grafana may face outward, and only with:
- Read-only accounts with strong passwords or single sign-on
- HTTPS
- Dashboards containing nothing sensitive
- Prometheus not exposed
- MetricsPort never exposed
Nginx and TLS¶
certbot --nginxfor issuance and renewal- TLS 1.2 and 1.3 only, weak ciphers disabled
- HSTS,
X-Content-Type-Options: nosniff, and a Content Security Policy X-Robots-Tag: noindex, nofollowto stay out of search indexes/.well-known/acme-challenge/over port 80, everything else on 80 redirected 301 to 443
Monitoring, logging, and incident response¶
Logs¶
journaldpersistent, forwarded throughrsyslogwhere required- Separate files for Nginx and Tor, with
logrotate - Tor at
Log notice, avoiding sensitive detail - Raise the level temporarily during an incident and restore it afterwards
Host observability¶
node_exporterbound to127.0.0.1:9100- Local Prometheus scraping, Grafana displaying, read-only if exposed
Audit¶
auditdrecording changes to/etc/tor/torrcand/var/lib/tor/keys/- AIDE comparing periodically, with results emailed to the operations address
Backups and key protection¶
- Offline or off-site copies of
torrcand/var/lib/tor/keys/ - Losing the identity key means the relay restarts as a new identity, which costs its accumulated reputation
Incident runbook¶
On an alert:
- Triage:
sudo ss -tnlp | egrep ':22|:80|:443|:9001|:9035' sudo journalctl -u tor -n 200 sudo ufw status numbered - Reduce exposure quickly:
Preserve complete snapshots of
sudo systemctl stop tor@default/var/logand/var/lib/tor/ - Notify internally: a preliminary incident report within 30 minutes covering impact, cause, action taken, and expected recovery
- If compromise is suspected: preserve evidence, isolate, rebuild, and restore from the backed-up identity key, provided the key is known not to have leaked
Conditions for taking it offline: impact on institutional networks, a security anomaly, or a request from the institution or supervisor.
Acceptance testing¶
- Verify the configuration:
sudo -u debian-tor tor --verify-config - Confirm ORPort reachability from a host outside the campus:
An
nmap -p 9001 <public IP>ORPort reachabilitywarning in the Tor log usually means the firewall or the external exception has not gone through - Confirm the relay is live: after several minutes, look up your fingerprint in Tor Metrics Relay Search
IPv6 (optional)¶
Request an IPv6 address from the IT centre, then add to torrc:
ORPort [<your IPv6>]:9001
Allow the corresponding traffic in the firewall. IPv6 is optional and contributes substantially to the network's diversity, which makes it worth pushing for.
Handover and life after graduation¶
The difference between a campus relay and a personal one is that people graduate. Prepare from day one:
ContactInfoon a shared address: a society, project, or team mailbox- At least two key backups: one with the operator, one in the supervisor's laboratory
- A handover checklist:
torrc, firewall rules, SSH keys, monitoring accounts, and the password for the contact mailbox - Find a successor early: a semester before graduation
- Write a handover document: this machine's history, the problems already hit, and the agreed operational rhythm
Next step
Once the relay is live, the case exists inside the Tor network.
- Tell us: get in touch through Community services and we will add your case to the Tor relays on campus track, so the next institution has more to work from
- Ongoing operation: the FAQ at the end of how to run a Tor relay covers monitoring with nyx, package upgrades, and how the guard relay mechanism works
- Doing more: running a Tor Snowflake bridge alongside it helps users in censored regions reach Tor
Each campus relay that goes live makes the next one cheaper.
Related¶
- How to run a Tor relay: the basic installation from a personal perspective
- Campus Tor relay proposal template: the proposal document
- Campus Tor relay FAQ: the institutional concerns
- Tor relays on campus track: the community's entry point
- Tor Project relay operator guides: the official documentation
- Relay post-install and good practices
Sources and acknowledgements¶
This procedure comes from community member NZ, who provided the original design documentation and operational experience from the deployment at National Taiwan Normal University. The material is released under CC BY 4.0 with NZ's agreement.
If you follow this and find a step that does not fit your institution, or something worth adding, tell us. We keep updating it. Contact routes are on Community services.