Skip to content
This repository has been archived by the owner on Jun 4, 2021. It is now read-only.

Use random ports for Tor ORPort and obfs4 port #1610

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions playbooks/amazon.yml
@@ -1,4 +1,8 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
# We do this early so ec2-security-group knows Tor's port
- import_playbook: tor-setup.yml

- name: Provision the EC2 Server
# ==============================
hosts: localhost
Expand Down
4 changes: 4 additions & 0 deletions playbooks/azure.yml
@@ -1,4 +1,8 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
# We do this early so azure-security-group knows Tor's port
- import_playbook: tor-setup.yml

- name: Provision the Azure Server (Resource Manager mode)
# ========================================================
hosts: localhost
Expand Down
3 changes: 3 additions & 0 deletions playbooks/digitalocean.yml
@@ -1,4 +1,7 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
- import_playbook: tor-setup.yml

- name: Provision the DigitalOcean Server
# =======================================
hosts: localhost
Expand Down
3 changes: 3 additions & 0 deletions playbooks/existing-server.yml
Expand Up @@ -3,6 +3,9 @@
# role to create a new server and instead applies Streisand to an existing
# remote server.

# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
- import_playbook: tor-setup.yml

- name: Register the genesis role in use
hosts: localhost
gather_facts: yes
Expand Down
4 changes: 4 additions & 0 deletions playbooks/google.yml
@@ -1,4 +1,8 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
# We do this early so gce-network knows Tor's port
- import_playbook: tor-setup.yml

- name: Provision the GCE Server
# =======================================
hosts: localhost
Expand Down
3 changes: 3 additions & 0 deletions playbooks/linode.yml
@@ -1,4 +1,7 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
- import_playbook: tor-setup.yml

- name: Provision the Linode Server
# =================================
hosts: localhost
Expand Down
4 changes: 4 additions & 0 deletions playbooks/localhost.yml
Expand Up @@ -2,6 +2,10 @@
# localhost.yml is an advanced provisioning option that doesn't use a genesis
# role to create a new server and instead applies Streisand to the localhost.

# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
# We do this early so gce-network knows Tor's port
- import_playbook: tor-setup.yml

# Ensure Python is installed on the system
- import_playbook: python.yml

Expand Down
3 changes: 3 additions & 0 deletions playbooks/rackspace.yml
@@ -1,4 +1,7 @@
---
# Randomize Tor's ORPort and obfs4 port if Tor is enabled.
- import_playbook: tor-setup.yml

- name: Provision the Rackspace Server
# ====================================
hosts: localhost
Expand Down
6 changes: 3 additions & 3 deletions playbooks/roles/tor-bridge/defaults/main.yml
@@ -1,7 +1,7 @@
---
tor_orport: 8443
tor_obfs4_port: 9443

tor_orport: "{{ lookup('file', lookup('env','HOME') + '/.streisand/tor-orport') or 0 }}"
tor_obfs4_port: "{{ lookup('file', lookup('env','HOME') + '/.streisand/tor-obfs4-port') or 0 }}"
tor_internal_hidden_service_port: 8181
# By default Streisand does *not* publish the Tor relay's service descriptor to
# the tor network. Using a value of 0 ensures the relay is private to the
# streisand operator and their users. See the Tor documentation[0] for more
Expand Down
2 changes: 1 addition & 1 deletion playbooks/roles/tor-bridge/vars/main.yml
Expand Up @@ -18,4 +18,4 @@ tor_html_instructions: "{{ tor_gateway_location }}/index.html"

tor_obfs4_qr_code: "{{ tor_gateway_location }}/tor-obfs4-qr-code.png"

tor_internal_hidden_service_address: "127.0.0.1:8181"
tor_internal_hidden_service_address: "127.0.0.1:{{ tor_internal_hidden_service_port }}"
88 changes: 88 additions & 0 deletions playbooks/tor-setup.yml
@@ -0,0 +1,88 @@
---
# We randomize the Tor ORPort and obfs4 port fairly early
# in the provisioning process to allow cloud firewalls/playbooks
# to access the tor_orport and tor_obfs4_port variables
# which are chosen randomly. Since we use ansible's "random" filter,
# we run into issues of idempotence if we don't use set_fact to
# make the call to "random". Even if we provide a seed, we
# only get random-but-idempotent behavior within the ansible role.
# Meaning the genesis-<cloud> roles would have a different number
# for tor_orport and tor_obfs4_port than the number generated when
# the tor-bridge role runs, even if we provide a fixed seed.

# We randomize Tor's ORPort and obfs4 port to avoid being
# easily fingerprinted as a Tor bridge (or a Streisand Tor bridge)
# See https://lists.torproject.org/pipermail/tor-dev/2014-December/007957.html
# and https://github.com/StreisandEffect/streisand/issues/101
# for discussion/more data on the topic.
- name: Randomize Tor's ORPort and obfs4 port
hosts: localhost
gather_facts: no
tasks:
- lineinfile:
path: "{{ lookup('env', 'HOME') }}/.streisand/tor-orport"
regexp: "^(.*)$"
state: absent

- lineinfile:
path: "{{ lookup('env', 'HOME') }}/.streisand/tor-obfs4-port"
regexp: "^(.*)$"
state: absent

- name: Randomize Tor's ORPort
run_once: true
lineinfile:
path: "{{ lookup('env', 'HOME') }}/.streisand/tor-orport"
regexp: "^[0-9]+$"
create: yes
# Pick a random port from list of available ports
line: "{{ tor_available_ports | random }}"

- name: Randomize Tor's obfs4 port
run_once: true
lineinfile:
path: "{{ lookup('env', 'HOME') }}/.streisand/tor-obfs4-port"
regexp: "^[0-9]+$"
create: yes
# The "reject" statement is to avoid using the same random port just chosen for tor_orport
line: "{{ tor_available_ports | reject('equalto', 'tor_orport') | list | random }}"


vars:
# Keep track of ports already being used by other services to avoid port conflicts
# "map" call is needed here because some of these vars are strings, others are integers
tor_unavailable_ports: "{{ [
nginx_port,
ssh_port,
le_port,
shadowsocks_server_port,
wireguard_port,
ocserv_port,
openvpn_port,
stunnel_remote_port,
cloudflared_port,
tor_internal_hidden_service_port
] | map('int') | list }}"
# Choose a random port between 1024-32768--below 1024 would require elevated privileges,
# and any port above 32768 will be used as "Ephemeral Ports" by anything listening locally
# (see output of "cat /proc/sys/net/ipv4/ip_local_port_range" for the full range)
tor_random_port_range: "{{ range(1024, 32768) | list }}"
# Avoid the common ORPort and obfs4 ports
tor_common_ports: [9001, 8443, 9443]
# Calculate available ports by excluding common/used ports from our choices for randomization
tor_available_ports: "{{ tor_random_port_range | difference(tor_common_ports + tor_unavailable_ports) }}"

# These variable files are included so we can have a list of all
# used ports, this way we can avoid port conflicts when randomizing
# Tor's ORPort and obfs4 port.
vars_files:
- roles/openconnect/defaults/main.yml
- roles/openvpn/defaults/main.yml
- roles/shadowsocks/defaults/main.yml
- roles/ssh/defaults/main.yml
- roles/lets-encrypt/vars/main.yml
- roles/streisand-gateway/defaults/main.yml
- roles/stunnel/defaults/main.yml
- roles/cloudflared/defaults/main.yml
- roles/tor-bridge/defaults/main.yml
- roles/wireguard/defaults/main.yml