[...]"
10 | exit 1
11 | fi
12 |
13 | GENDIR="$1"
14 | WANTDIR="$1/tor.service.wants"
15 | SERVICEFILE="/lib/systemd/system/tor@.service"
16 | DEFAULTTOR="/lib/systemd/system/tor@default.service"
17 | BASEETC="/etc/tor/instances"
18 |
19 | mkdir -p "$WANTDIR"
20 |
21 | [ -e "/etc/tor/torrc" ] && ln -s "$DEFAULTTOR" "$WANTDIR/"
22 | for name in $( find "$BASEETC" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' ); do
23 | if echo "x$name" | grep -q '[^a-zA-Z0-9_.]' ||
24 | [ "$name" = "default" ] ; then
25 | continue
26 | fi
27 | [ -e "$BASEETC/$name/torrc" ] && ln -s "$SERVICEFILE" "$WANTDIR/tor@$name.service"
28 | done
29 |
30 | exit 0
31 |
--------------------------------------------------------------------------------
/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: stop-and-mask default tor instance
3 | become: true
4 | ansible.builtin.systemd:
5 | name: tor@default
6 | state: stopped
7 | enabled: false
8 | masked: true
9 | when: ansible_pkg_mgr == 'apt'
10 |
11 | - name: restart apparmor
12 | become: true
13 | ansible.builtin.service:
14 | name: apparmor
15 | state: restarted
16 |
17 | - name: systemctl daemon-reload
18 | become: true
19 | ansible.builtin.systemd:
20 | daemon_reload: true
21 |
22 | - name: re-gather facts
23 | ansible.builtin.setup:
24 |
25 | - name: disable default tor instance FreeBSD
26 | become: true
27 | ansible.builtin.lineinfile:
28 | dest: /etc/rc.conf
29 | line: tor_disable_default_instance="YES"
30 | create: true
31 | mode: "0644"
32 | when: ansible_system == 'FreeBSD'
33 |
34 | # TODO: this restarts all instances on a FreeBSD host even if just one torrc changed
35 | - name: Ensure Tor instances are restarted if its torrc changed (FreeBSD)
36 | become: true
37 | ansible.builtin.service:
38 | name: tor
39 | state: restarted
40 | when: ansible_system == 'FreeBSD'
41 |
42 | - name: Ensure Tor instances are restarted if its torrc changed (Linux)
43 | become: true
44 | ansible.builtin.service:
45 | name: tor@{{ item.item.0.ipv4 }}_{{ item.item.1.orport }}.service
46 | state: restarted
47 | with_items: "{{ tor_instances_tmp.results }}"
48 | when: item.changed and ansible_system == 'Linux'
49 |
50 | - name: Ensure Tor instances are restarted if its torrc changed (OpenBSD)
51 | become: true
52 | ansible.builtin.service:
53 | name: tor{{ item.item.0.ipv4|replace('.','_') }}_{{ item.item.1.orport }}
54 | state: restarted
55 | with_items: "{{ tor_instances_tmp.results }}"
56 | when: item.changed and ansible_system == 'OpenBSD'
57 | tags:
58 | - reconfigure
59 |
60 | - name: Ensure Tor instances are reloaded if tor-exit-notice.html changed (FreeBSD)
61 | become: true
62 | ansible.builtin.service:
63 | name: tor
64 | state: reloaded
65 | when: ansible_system == 'FreeBSD'
66 |
67 | - name: Ensure Tor instances are reloaded if tor-exit-notice.html changed (Linux)
68 | become: true
69 | ansible.builtin.service:
70 | name: tor@{{ item.item.0.ipv4 }}_{{ item.item.1.orport }}.service
71 | state: reloaded
72 | with_items: "{{ tor_instances_tmp.results }}"
73 | when: ansible_system == 'Linux'
74 |
75 | - name: Ensure Tor instances are reloaded if tor-exit-notice.html changed (OpenBSD)
76 | become: true
77 | ansible.builtin.service:
78 | name: tor{{ item.item.0.ipv4|replace('.','_') }}_{{ item.item.1.orport }}
79 | state: reloaded
80 | with_items: "{{ tor_instances_tmp.results }}"
81 | when: ansible_system == 'OpenBSD'
82 |
83 | - name: reload prometheus
84 | become: true
85 | ansible.builtin.service:
86 | name: prometheus
87 | state: reloaded
88 | delegate_to: "{{ tor_prometheus_host }}"
89 |
90 | - name: reload nginx
91 | become: true
92 | ansible.builtin.service:
93 | name: nginx
94 | state: reloaded
95 |
--------------------------------------------------------------------------------
/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | galaxy_info:
3 | role_name: relayor
4 | author: nusenu
5 | description: An Ansible role for Tor Relay Operators
6 | license: GPLv3
7 | platforms:
8 | - name: Debian
9 | versions:
10 | - bookworm
11 | - name: FreeBSD
12 | versions:
13 | - all
14 | - name: Ubuntu
15 | versions:
16 | - noble
17 | galaxy_tags:
18 | - tor
19 | - ipv6
20 | - anonymity
21 | - networking
22 | min_ansible_version: 2.16.14
23 | dependencies: []
24 |
--------------------------------------------------------------------------------
/playbook-examples/exit-relays/10_4_instances_per_IP.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_ExitRelay: true
7 | tor_ports:
8 | - { orport: 1, dirport: 0}
9 | - { orport: 2, dirport: 0}
10 | - { orport: 3, dirport: 0}
11 | - { orport: 4, dirport: 0}
12 | roles:
13 | - nusenu.relayor
14 |
--------------------------------------------------------------------------------
/playbook-examples/exit-relays/11_4_instances_per_IP_prometheus.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_ExitRelay: true
7 | tor_enableMetricsPort: true
8 | tor_prometheus_host: 127.0.0.1
9 | tor_ports:
10 | - { orport: 1, dirport: 0}
11 | - { orport: 2, dirport: 0}
12 | - { orport: 3, dirport: 0}
13 | - { orport: 4, dirport: 0}
14 | roles:
15 | - nusenu.relayor
16 |
--------------------------------------------------------------------------------
/playbook-examples/exit-relays/12_4_instances_per_IP_prometheus-alertrules.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_ExitRelay: true
7 | tor_enableMetricsPort: true
8 | tor_prometheus_host: 127.0.0.1
9 | tor_gen_prometheus_alert_rules: true
10 | tor_ports:
11 | - { orport: 1, dirport: 0}
12 | - { orport: 2, dirport: 0}
13 | - { orport: 3, dirport: 0}
14 | - { orport: 4, dirport: 0}
15 | roles:
16 | - nusenu.relayor
17 |
--------------------------------------------------------------------------------
/playbook-examples/exit-relays/8_reduced_exitpolicy.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_alpha: true
9 | tor_ExitRelay: true
10 | roles:
11 | - nusenu.relayor
12 |
--------------------------------------------------------------------------------
/playbook-examples/exit-relays/9_custom_exit_policy.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_alpha: true
9 | tor_ExitRelay: true
10 | tor_ExitPolicy:
11 | - accept *:80
12 | - accept *:443
13 | - reject *:*
14 | roles:
15 | - nusenu.relayor
16 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/1_contactInfo.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | roles:
7 | - nusenu.relayor
8 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/2_keyexpiry_90days.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | roles:
8 | - nusenu.relayor
9 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/3_nickname.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | roles:
9 | - nusenu.relayor
10 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/4_custom_ports.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_ports:
9 | - { orport: 443, dirport: 80}
10 | - { orport: 444, dirport: 8080}
11 | roles:
12 | - nusenu.relayor
13 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/5_single_instance.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_ports:
9 | - { orport: 443, dirport: 80}
10 | roles:
11 | - nusenu.relayor
12 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/6_four_instances.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_maxPublicIPs: 2
9 | roles:
10 | - nusenu.relayor
11 |
--------------------------------------------------------------------------------
/playbook-examples/non-exit-relays/7_alpha-apt-repo.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: relays
4 | vars:
5 | #tor_ContactInfo: relayor-example@example.com
6 | tor_signingkeylifetime_days: 90
7 | tor_nickname: "{{ ansible_hostname }}"
8 | tor_maxPublicIPs: 2
9 | tor_alpha: true
10 | roles:
11 | - nusenu.relayor
12 |
--------------------------------------------------------------------------------
/tasks/apt_prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure lsb-release, apt-transport-https and gpg packages are installed
3 | become: true
4 | ansible.builtin.apt:
5 | name: lsb-release,apt-transport-https,gpg
6 | update_cache: "{{ tor_apt_update_cache }}"
7 | cache_valid_time: 86400
8 | notify:
9 | - re-gather facts
10 |
11 | - ansible.builtin.meta: flush_handlers
12 |
13 | - name: Ensure torproject gpg key is installed (A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89)
14 | become: true
15 | ansible.builtin.apt_key:
16 | url: https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
17 | id: A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89
18 | state: present
19 |
20 | - name: Ensure torproject.org repository is present (APT)
21 | become: true
22 | ansible.builtin.apt_repository:
23 | repo: deb https://deb.torproject.org/torproject.org {{ tor_distribution_release }} main
24 | state: present
25 | update_cache: true
26 |
27 | - name: Override tor_alpha_version if nightly builds repo is enabled (APT)
28 | ansible.builtin.set_fact:
29 | tor_alpha_version: nightly-main
30 | tor_alpha: true
31 | when: tor_nightly_builds
32 |
33 | - name: Ensure torproject.org alpha/nightly repo is present if enabled (APT)
34 | become: true
35 | ansible.builtin.apt_repository:
36 | repo: deb https://deb.torproject.org/torproject.org tor-{{ tor_alpha_version }}-{{ tor_distribution_release }} main
37 | state: present
38 | update_cache: true
39 | when: tor_alpha
40 |
41 | # Background:
42 | # https://github.com/nusenu/ansible-relayor/issues/72
43 | - name: Ensure systemd generator folder exists (Debian/Ubuntu)
44 | become: true
45 | ansible.builtin.file:
46 | path: /etc/systemd/system-generators
47 | state: directory
48 | mode: "0755"
49 |
50 | - name: Ensure custom systemd generator is in place (Debian/Ubuntu only)
51 | become: true
52 | ansible.builtin.copy:
53 | src: tor-generator
54 | dest: /etc/systemd/system-generators/tor-generator
55 | owner: root
56 | mode: "0755"
57 |
--------------------------------------------------------------------------------
/tasks/configure.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure local DataDir folders exist (LOCAL)
3 | become: false
4 | ansible.builtin.file:
5 | path: "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}"
6 | state: directory
7 | mode: "0700"
8 | delegate_to: 127.0.0.1
9 | with_nested:
10 | - "{{ tor_ips }}"
11 | - "{{ tor_ports }}"
12 | tags:
13 | - createdir
14 |
15 | - name: Check happy family key (LOCAL)
16 | become: false
17 | ansible.builtin.stat:
18 | path: "{{ tor_local_happy_family_folder }}/{{ tor_happy_family_basename }}.secret_family_key"
19 | delegate_to: 127.0.0.1
20 | run_once: true
21 | when: tor_happy_family
22 | register: tor_happy_family_stat
23 |
24 | - name: Ensure happy family key exists (LOCAL)
25 | become: false
26 | ansible.builtin.command: >
27 | "{{ tor_binary }}" --quiet --keygen-family "{{ tor_local_happy_family_folder }}/{{ tor_happy_family_basename }}"
28 | delegate_to: 127.0.0.1
29 | run_once: true
30 | when: tor_happy_family and not tor_happy_family_stat.stat.exists
31 |
32 | - name: Check FamilyId presence (LOCAL)
33 | become: false
34 | ansible.builtin.stat:
35 | path: "{{ tor_local_happy_family_folder }}/{{ tor_happy_family_basename }}.public_family_id"
36 | delegate_to: 127.0.0.1
37 | run_once: true
38 | when: tor_happy_family
39 | register: tor_happy_family_publicid_stat
40 |
41 | - name: Ensure FamilyId public id file exists (LOCAL)
42 | become: false
43 | ansible.builtin.assert:
44 | that:
45 | - tor_happy_family_publicid_stat.stat.exists
46 | msg: public_family_id file is missing, aborting!
47 | run_once: true
48 | delegate_to: 127.0.0.1
49 | when: tor_happy_family
50 |
51 | - name: Ensure all relay keys exist (LOCAL)
52 | become: false
53 | ansible.builtin.command: >
54 | "{{ tor_binary }}" --list-fingerprint --DisableNetwork 1 --orport auto --PublishServerDescriptor 0 --ExitRelay 0
55 | --ignore-missing-torrc -f /dev/null --defaults-torrc /dev/null --Log "err stdout"
56 | --datadirectory "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}"
57 | delegate_to: 127.0.0.1
58 | with_nested:
59 | - "{{ tor_ips }}"
60 | - "{{ tor_ports }}"
61 | changed_when: false
62 |
63 | - name: Generate new Ed25519 signing keys (LOCAL)
64 | become: false
65 | ansible.builtin.command: >
66 | "{{ tor_binary }}" --keygen --SigningKeyLifetime {{ tor_signingkeylifetime_days }}\ days --ignore-missing-torrc -f /dev/null
67 | --defaults-torrc /dev/null --Log "err stdout"
68 | --datadirectory "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}"
69 | delegate_to: 127.0.0.1
70 | with_nested:
71 | - "{{ tor_ips }}"
72 | - "{{ tor_ports }}"
73 | tags:
74 | - renewkey
75 | changed_when: false
76 |
77 | - name: Detect duplicate relay keys across relays (LOCAL)
78 | become: false
79 | ansible.builtin.shell: >
80 | set -o pipefail && openssl sha256 -r
81 | {{ tor_offline_masterkey_dir }}/*/keys/secret_id_key {{ tor_offline_masterkey_dir }}/*/keys/ed25519_master_id_secret_key |
82 | cut -d' ' -f1|sort|uniq -d|wc -l
83 | args:
84 | executable: /bin/bash
85 | delegate_to: 127.0.0.1
86 | run_once: true
87 | register: tor_dupkeycount
88 | changed_when: false
89 |
90 | - name: Abort on duplicate relay keys
91 | ansible.builtin.fail:
92 | msg: Duplicate relay key detected! Aborting.
93 | run_once: true
94 | when: tor_dupkeycount.stdout|int(1) != 0
95 |
96 | - name: Detect if Ed25519 master keys are on the relay
97 | become: true
98 | ansible.builtin.stat:
99 | path: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/ed25519_master_id_secret_key"
100 | register: tor_masterkeyonline
101 | with_nested:
102 | - "{{ tor_ips }}"
103 | - "{{ tor_ports }}"
104 |
105 | - name: Abort if Ed25519 master keys are on the relay
106 | ansible.builtin.fail:
107 | msg: Ed25519 MASTER KEY detected on the relay - it is NOT supposed to be there! Aborting.
108 | when: item.stat.exists
109 | with_items: "{{ tor_masterkeyonline.results }}"
110 |
111 | # not relying on the datadir/fingerprint file is more robust
112 | - name: Collect fingerprints for MyFamily (LOCAL)
113 | become: false
114 | ansible.builtin.shell: >
115 | set -o pipefail && for key in {{ tor_offline_masterkey_dir }}/*/keys/secret_id_key;
116 | do openssl rsa -in $key -outform DER -RSAPublicKey_out 2> /dev/null| openssl sha1 -r;
117 | done|cut -d" " -f1|sort|xargs|sed -e 's/ /,/g'
118 | args:
119 | executable: /bin/bash
120 | delegate_to: 127.0.0.1
121 | run_once: true
122 | register: tor_family
123 | tags:
124 | - reconfigure
125 | changed_when: false
126 |
127 | - name: Ensure per-instance tor users exist
128 | become: true
129 | ansible.builtin.user:
130 | name: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
131 | system: true
132 | shell: /bin/false
133 | create_home: true
134 | home: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}"
135 | with_nested:
136 | - "{{ tor_ips }}"
137 | - "{{ tor_ports }}"
138 |
139 | # We only need to create this folder (used for ControlSocket and on FreeBSD for the pidfile) on:
140 | # - FreeBSD (regardless of tor_enableControlSocket)
141 | # - CentOS/Fedora/OpenBSD when tor_enableControlSocket is True
142 | # we never create it on Debian since the systemd service file creates it there (with different permissions)
143 | - name: Ensure PID/ControlSocket directory exists
144 | become: true
145 | ansible.builtin.file:
146 | path: "{{ tor_PidDir }}"
147 | state: directory
148 | owner: root
149 | mode: "0755"
150 | when: ansible_system == 'FreeBSD' or (tor_enableControlSocket and (ansible_system == 'OpenBSD' or ansible_os_family == 'RedHat'))
151 |
152 | - name: Ensure PID/ControlSocket directory is owned by per-instance tor user
153 | become: true
154 | ansible.builtin.file:
155 | path: "{{ tor_PidDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}"
156 | state: directory
157 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
158 | group: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
159 | mode: "0750"
160 | with_nested:
161 | - "{{ tor_ips }}"
162 | - "{{ tor_ports }}"
163 | when: ansible_system == 'FreeBSD' or (tor_enableControlSocket and (ansible_system == 'OpenBSD' or ansible_os_family == 'RedHat'))
164 |
165 | - name: Ensure per-instance config folders exist (Debian only)
166 | become: true
167 | ansible.builtin.file:
168 | path: "{{ tor_ConfDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}"
169 | state: directory
170 | mode: "0755"
171 | with_nested:
172 | - "{{ tor_ips }}"
173 | - "{{ tor_ports }}"
174 | when: ansible_pkg_mgr == 'apt'
175 |
176 | - name: Ensure DataDir exists
177 | become: true
178 | ansible.builtin.file:
179 | path: "{{ tor_DataDir }}"
180 | state: directory
181 | owner: root
182 | mode: "0755"
183 |
184 | - name: Ensure "keys" subfolder exists
185 | become: true
186 | ansible.builtin.file:
187 | path: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys"
188 | state: directory
189 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
190 | group: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
191 | mode: u=rwX,g=,o=
192 | recurse: true
193 | with_nested:
194 | - "{{ tor_ips }}"
195 | - "{{ tor_ports }}"
196 |
197 | - name: Ensure happy family key is in place
198 | become: true
199 | ansible.builtin.copy:
200 | src: "{{ tor_local_happy_family_folder }}/{{ tor_happy_family_basename }}.secret_family_key"
201 | dest: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ tor_happy_family_basename }}.secret_family_key"
202 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
203 | mode: "0600"
204 | with_nested:
205 | - "{{ tor_ips }}"
206 | - "{{ tor_ports }}"
207 | when: tor_happy_family
208 |
209 | - name: Ensure RSA key is in place (without overriding existing keys)
210 | become: true
211 | ansible.builtin.copy:
212 | src: "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}"
213 | dest: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}"
214 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
215 | mode: "0600"
216 | force: false
217 | with_nested:
218 | - "{{ tor_ips }}"
219 | - "{{ tor_ports }}"
220 | - [secret_id_key]
221 |
222 | - name: Fetch RSA key for comparison
223 | become: true
224 | ansible.builtin.fetch:
225 | src: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}"
226 | dest: "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}.untrustedremotekey"
227 | flat: true
228 | with_nested:
229 | - "{{ tor_ips }}"
230 | - "{{ tor_ports }}"
231 | - [secret_id_key]
232 |
233 | - name: Compare local vs. remote RSA key (secret_id_key)
234 | become: false
235 | ansible.builtin.shell: >
236 | set -o pipefail && openssl sha256 -r
237 | {{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-"{{ item.0.ipv4 }}_{{ item.1.orport }}"/keys/secret_id_key*
238 | | cut -d' ' -f1|uniq -d|wc -l
239 | args:
240 | executable: /bin/bash
241 | delegate_to: 127.0.0.1
242 | with_nested:
243 | - "{{ tor_ips }}"
244 | - "{{ tor_ports }}"
245 | register: tor_rsakey_match
246 | changed_when: false
247 |
248 | - name: Abort if local and remote RSA keys do not match
249 | ansible.builtin.assert:
250 | that:
251 | - item.stdout|int == 1
252 | msg: >
253 | "Key mismatch detected! Solution: http://bit.ly/2j6wc70 Affected instance:
254 | {{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.item.0.ipv4 }}_{{ item.item.1.orport }}/keys"
255 | with_items: "{{ tor_rsakey_match.results }}"
256 |
257 | # this task is separated from the task named "Ensure RSA key is in place" because it is not run with 'force=no'
258 | - name: Transmit new Ed25519 signing keys
259 | become: true
260 | ansible.builtin.copy:
261 | src: "{{ tor_offline_masterkey_dir }}/{{ inventory_hostname }}-{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}"
262 | dest: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/keys/{{ item[2] }}"
263 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
264 | mode: "0600"
265 | setype: tor_var_lib_t
266 | with_nested:
267 | - "{{ tor_ips }}"
268 | - "{{ tor_ports }}"
269 | - [ed25519_signing_cert, ed25519_signing_secret_key]
270 | changed_when: false
271 | tags:
272 | - renewkey
273 |
274 | # This needs to be at the end to fix SELinux contexts recursively
275 | - name: Ensure per-instance DataDir have proper permissions
276 | become: true
277 | ansible.builtin.file:
278 | path: "{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}"
279 | state: directory
280 | owner: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
281 | group: _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
282 | mode: u=rwX,g=,o=
283 | recurse: true
284 | setype: tor_var_lib_t
285 | with_nested:
286 | - "{{ tor_ips }}"
287 | - "{{ tor_ports }}"
288 |
289 | - name: Ensure Tor config directory exists
290 | become: true
291 | ansible.builtin.file:
292 | path: "{{ tor_ConfDir }}"
293 | state: directory
294 | owner: root
295 | group: "{{ tor_user }}"
296 | mode: "0755"
297 |
298 | - name: Ensure tor-exit-notice.html is present (if we are an exit)
299 | become: true
300 | ansible.builtin.template:
301 | src: "{{ tor_exit_notice_file }}"
302 | dest: "{{ tor_ConfDir }}/tor-exit-notice.html"
303 | mode: "0444"
304 | when: tor_ExitRelay and tor_ExitNoticePage
305 | notify:
306 | - Ensure Tor instances are reloaded if tor-exit-notice.html changed (FreeBSD)
307 | - Ensure Tor instances are reloaded if tor-exit-notice.html changed (Linux)
308 | - Ensure Tor instances are reloaded if tor-exit-notice.html changed (OpenBSD)
309 |
310 | - name: Ensure torrc configuration file(s) are in place
311 | become: true
312 | ansible.builtin.template:
313 | src: torrc
314 | dest: "{{ (ansible_pkg_mgr != 'apt')| ternary(tor_ConfDir ~ '/' ~ item.0.ipv4 ~ '_' ~ item.1.orport~ '.torrc', tor_ConfDir ~ '/' ~ item.0.ipv4 ~ '_' ~ item.1.orport
315 | ~ '/torrc') }}"
316 | owner: root
317 | mode: "0644"
318 | backup: "{{ tor_backup_torrc }}"
319 | validate: tor --verify-config -f %s
320 | with_nested:
321 | - "{{ tor_ips }}"
322 | - "{{ tor_ports }}"
323 | register: tor_instances_tmp
324 | loop_control:
325 | index_var: loop_idx
326 | notify:
327 | - Ensure Tor instances are restarted if its torrc changed (FreeBSD)
328 | - Ensure Tor instances are restarted if its torrc changed (Linux)
329 | - Ensure Tor instances are restarted if its torrc changed (OpenBSD)
330 | tags:
331 | - reconfigure
332 |
333 | - name: Ensure prometheus per host scrape configs are in place
334 | become: true
335 | ansible.builtin.template:
336 | src: prometheus-scrape-configs
337 | dest: "{{ tor_prometheus_confd_folder}}/{{ tor_prometheus_scrape_file }}"
338 | owner: root
339 | group: "{{ tor_prometheus_group }}"
340 | mode: "0640"
341 | delegate_to: "{{ tor_prometheus_host }}"
342 | when: (tor_enableMetricsPort or tor_blackbox_exporter_host is defined) and tor_prometheus_host is defined
343 | notify:
344 | - reload prometheus
345 | tags:
346 | - reconfigure
347 | - promconfig
348 |
349 | - name: Ensure prometheus alert rules are in place
350 | become: true
351 | ansible.builtin.template:
352 | src: prometheus-alert-rules
353 | dest: "{{ tor_prometheus_rules_file }}"
354 | owner: root
355 | group: "{{ tor_prometheus_group }}"
356 | mode: "0640"
357 | validate: promtool check rules %s
358 | delegate_to: "{{ tor_prometheus_host }}"
359 | run_once: true
360 | when: tor_enableMetricsPort and tor_gen_prometheus_alert_rules and tor_prometheus_rules_file is defined and tor_prometheus_host is defined
361 | notify:
362 | - reload prometheus
363 | tags:
364 | - reconfigure
365 | - promconfig
366 |
367 | - name: Ensure nginx reverse proxy configuration for MetricsPort is in place
368 | become: true
369 | ansible.builtin.template:
370 | src: nginx-reverse-proxy-for-metricsport
371 | dest: "{{ tor_metricsport_nginx_config_file }}"
372 | mode: "0644"
373 | owner: root
374 | when: tor_enableMetricsPort and tor_prometheus_host is defined
375 | notify:
376 | - reload nginx
377 | tags:
378 | - reconfigure
379 | - promconfig
380 |
381 | # The htpasswd ansible module requires python passlib to be on the remote host
382 | - name: Ensure htpasswd Ansible dependencies are installed
383 | become: true
384 | ansible.builtin.package:
385 | name: "{{ tor_htpasswd_dependency }}"
386 | state: present
387 | when: tor_gen_metricsport_htpasswd and tor_enableMetricsPort
388 |
389 | - name: Ensure htpasswd file for MetricsPort protection is in place
390 | become: true
391 | community.general.htpasswd:
392 | create: true
393 | path: "{{ tor_metricsport_htpasswd_file }}"
394 | name: "{{ tor_metricsport_user }}"
395 | password: "{{ lookup('password', tor_prometheus_scrape_password_folder+inventory_hostname ) }}"
396 | mode: "0640"
397 | group: "{{ tor_metricsport_htpasswd_file_owner }}"
398 | when: tor_gen_metricsport_htpasswd and tor_enableMetricsPort
399 | tags:
400 | - reconfigure
401 | - promconfig
402 |
--------------------------------------------------------------------------------
/tasks/freebsd_prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Choose tor alpha version package (FreeBSD)
3 | ansible.builtin.set_fact:
4 | tor_packages: tor-devel
5 | when: tor_alpha
6 |
7 | - name: Ensure sequential IP IDs are avoided (FreeBSD)
8 | become: true
9 | ansible.posix.sysctl:
10 | name: net.inet.ip.random_id
11 | value: 1
12 | reload: false
13 | sysctl_set: true
14 |
15 | - name: Gather current kern.ipc.somaxconn setting (FreeBSD)
16 | ansible.builtin.command: /sbin/sysctl -n kern.ipc.somaxconn
17 | become: false
18 | register: tor_currentsomaxconn
19 | changed_when: false
20 |
21 | - name: Ensure somaxconn setting is reasonable (FreeBSD)
22 | become: true
23 | ansible.posix.sysctl:
24 | name: kern.ipc.somaxconn
25 | value: "{{ tor_freebsd_somaxconn }}"
26 | reload: false
27 | sysctl_set: true
28 | when: tor_currentsomaxconn.stdout|int < tor_freebsd_somaxconn
29 |
30 | - name: Gather current kern.ipc.nmbclusters setting (FreeBSD)
31 | become: false
32 | ansible.builtin.command: /sbin/sysctl -n kern.ipc.nmbclusters
33 | register: tor_currentnmbc
34 | changed_when: false
35 |
36 | - name: Ensure nmbclusters setting is reasonable (FreeBSD)
37 | become: true
38 | ansible.posix.sysctl:
39 | name: kern.ipc.nmbclusters
40 | value: "{{ tor_freebsd_nmbclusters }}"
41 | reload: false
42 | sysctl_set: true
43 | when: tor_currentnmbc.stdout|int < tor_freebsd_nmbclusters
44 |
--------------------------------------------------------------------------------
/tasks/freebsd_service.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # tor_intances defines the number and configurations of instances
3 | # an instance is defined with the following fields:
4 | # inst_name:configfile:username:groupname:pidfile:data_dir
5 | # username/groupname is set to root to be able to bind to <1024 ports
6 | # but privileges are dropped afterwards (torrc User parameter)
7 | - name: Ensure Tor multi-instance configuration is present (FreeBSD)
8 | become: true
9 | ansible.builtin.lineinfile:
10 | dest: /etc/rc.conf
11 | line: tor_instances="${tor_instances} {{ item.0.ipv4 }}_{{ item.1.orport }}:{{ tor_ConfDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}.torrc:root:root:{{ tor_PidDir
12 | }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/pid:{{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}"
13 | create: true
14 | mode: "0644"
15 | with_nested:
16 | - "{{ tor_ips }}"
17 | - "{{ tor_ports }}"
18 |
19 | # this task is a workaround, because 'service tor status'
20 | # fails if this line is not present (which in turn fails the ansible service module)
21 | - name: Ensure tor is enabled in /etc/rc.conf (FreeBSD)
22 | become: true
23 | ansible.builtin.lineinfile:
24 | dest: /etc/rc.conf
25 | line: tor_enable="YES"
26 | create: true
27 | mode: "0644"
28 |
29 | # this affects all instances
30 | - name: Ensure Tor instances are running and enabled (FreeBSD)
31 | become: true
32 | ansible.builtin.service:
33 | name: tor
34 | enabled: true
35 | state: started
36 |
--------------------------------------------------------------------------------
/tasks/ip-list.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Use a single private IPv4 address if we have no public IPv4 address
3 | ansible.builtin.include_vars: private_IPv4_only.yml
4 | when: tor_v4ips == []
5 |
6 | - name: Setup IP list (1/2)
7 | ansible.builtin.set_fact:
8 | ips:
9 | ipv4: "{{ item.0 }}"
10 | ipv6: "{{ item.1 }}"
11 | with_together:
12 | - "{{ tor_v4ips }}"
13 | - "{{ tor_v6ips }}"
14 | register: tor_ipsinterm
15 |
16 | - name: Setup IP list (2/2)
17 | ansible.builtin.set_fact:
18 | tor_ips: "{{ tor_ipsinterm.results | map(attribute='ansible_facts.ips') | list }}"
19 |
--------------------------------------------------------------------------------
/tasks/linux_service.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Linux/systemd section (uses service module)
3 | # ===========================================
4 |
5 | - name: Ensure Tor instances are enabled and started (Linux/systemd)
6 | become: true
7 | ansible.builtin.service:
8 | name: tor@{{ item.0.ipv4 }}_{{ item.1.orport }}.service
9 | enabled: true
10 | state: started
11 | with_nested:
12 | - "{{ tor_ips }}"
13 | - "{{ tor_ports }}"
14 |
--------------------------------------------------------------------------------
/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Check for min. ansible version requirement
3 | ansible.builtin.assert:
4 | that:
5 | - ansible_version.full is version_compare('2.16.14', '>=')
6 | msg: Your ansible version is too old, please upgrade to v2.16.14 or newer. Exiting.
7 | run_once: true
8 | delegate_to: 127.0.0.1
9 | tags:
10 | - always
11 | - ansible-version-check
12 |
13 | - name: Check for local requirements
14 | ansible.builtin.shell: >
15 | command -V /bin/bash && command -V "{{ tor_binary }}" && command -V openssl && command -V sort
16 | && command -V uniq && command -V wc && command -V cut && command -V xargs && command -V sed
17 | run_once: true
18 | become: false
19 | delegate_to: 127.0.0.1
20 | tags:
21 | - always
22 | changed_when: false
23 |
24 | - name: Ensure tor_ContactInfo is set
25 | ansible.builtin.assert:
26 | that:
27 | - tor_ContactInfo is defined
28 | msg: tor_ContactInfo is not configured but mandatory, please set it.
29 | tags:
30 | - always
31 |
32 | - name: Ensure we do not create more than 8 instances per IP
33 | ansible.builtin.assert:
34 | that:
35 | - tor_ports | length > 0
36 | - tor_ports | length < 9
37 | msg: You can not run more than 8 instances per IP address, please update your tor_ports configuration.
38 | tags:
39 | - always
40 |
41 | - name: >
42 | Abort if we use a centralized/common DNS resolver (Google, Quad9, CloudFlare, OpenDNS, Level3).
43 | See https://torproject.org/relay-guide#DNSonExitRelays (Exits only)
44 | ansible.builtin.command: "{{ tor_grep_blacklisted_dnsresolvers }}"
45 | register: tor_dns_check
46 | failed_when: tor_dns_check.rc == 0
47 | changed_when: false
48 | when: tor_ExitRelay
49 | tags:
50 | - always
51 |
52 | - name: Set OS specific variables
53 | ansible.builtin.include_vars: os_{{ ansible_os_family }}.yml
54 | tags:
55 | - always
56 |
57 | - ansible.builtin.import_tasks: ip-list.yml
58 | tags:
59 | - always
60 |
61 | - name: Preparation for Debian-based systems
62 | ansible.builtin.include_tasks: apt_prepare.yml
63 | when: ansible_pkg_mgr == 'apt'
64 | tags:
65 | - debian
66 | - install
67 |
68 | - name: Preparation for RPM based systems
69 | ansible.builtin.include_tasks: rpm_prepare.yml
70 | when: ansible_os_family == 'RedHat'
71 | tags:
72 | - centos
73 | - fedora
74 | - install
75 |
76 | - name: Preparation for OpenBSD systems
77 | ansible.builtin.include_tasks: openbsd_prepare.yml
78 | when: ansible_system == 'OpenBSD'
79 | tags:
80 | - openbsd
81 |
82 | - name: Preparation for FreeBSD based systems
83 | ansible.builtin.include_tasks: freebsd_prepare.yml
84 | when: ansible_system == 'FreeBSD'
85 | tags:
86 | - freebsd
87 |
88 | # we specifically opt for present over latest to improve performance
89 | - name: Ensure tor is installed
90 | become: true
91 | ansible.builtin.package:
92 | name: "{{ item }}"
93 | state: "{{ tor_package_state }}"
94 | with_items: "{{ tor_packages }}"
95 | # apt starts a tor client instance by default after installing the package
96 | # we do not need that
97 | notify:
98 | - stop-and-mask default tor instance
99 | - disable default tor instance FreeBSD
100 | tags:
101 | - openbsd
102 | - freebsd
103 | - debian
104 | - centos
105 | - fedora
106 | - install
107 |
108 | - ansible.builtin.meta: flush_handlers
109 | - ansible.builtin.import_tasks: configure.yml
110 | tags:
111 | - debian
112 | - centos
113 | - fedora
114 | - openbsd
115 | - freebsd
116 |
117 | - name: Linux service configuration
118 | ansible.builtin.include_tasks: linux_service.yml
119 | when: ansible_system == 'Linux'
120 | tags:
121 | - debian
122 | - centos
123 | - fedora
124 |
125 | - name: OpenBSD service configuration
126 | ansible.builtin.include_tasks: openbsd_service.yml
127 | when: ansible_system == 'OpenBSD'
128 | tags:
129 | - openbsd
130 |
131 | - name: FreeBSD service configuration
132 | ansible.builtin.include_tasks: freebsd_service.yml
133 | when: ansible_system == 'FreeBSD'
134 | tags:
135 | - freebsd
136 |
137 | - name: Generate CIISS rsa-fingerprint.txt proof file
138 | become: false
139 | ansible.builtin.shell: >
140 | set -o pipefail && for key in {{ tor_offline_masterkey_dir }}/*/keys/secret_id_key;
141 | do openssl rsa -in $key -outform DER -RSAPublicKey_out 2> /dev/null| openssl sha1 -r;
142 | done|cut -d" " -f1|sort > {{ tor_ciiss_proof_folder }}/rsa-fingerprint.txt
143 | args:
144 | executable: /bin/bash
145 | delegate_to: 127.0.0.1
146 | run_once: true
147 | changed_when: false
148 | when: tor_gen_ciiss_proof_files
149 | tags:
150 | - always
151 |
152 | - name: Generate CIISS ed25519-master-pubkey.txt proof file
153 | become: false
154 | ansible.builtin.shell: >
155 | set -o pipefail && cut -d" " -f2 {{ tor_offline_masterkey_dir }}/*/fingerprint-ed25519|
156 | sort > {{ tor_ciiss_proof_folder }}/ed25519-master-pubkey.txt
157 | args:
158 | executable: /bin/bash
159 | delegate_to: 127.0.0.1
160 | run_once: true
161 | changed_when: false
162 | when: tor_gen_ciiss_proof_files
163 | tags:
164 | - always
165 |
--------------------------------------------------------------------------------
/tasks/openbsd_prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Gather current system-wide file descriptor limits (OpenBSD)
3 | ansible.builtin.command: sysctl -n kern.maxfiles
4 | become: false
5 | register: tor_openbsd_maxfiles
6 | changed_when: false
7 |
8 | - name: Ensure system-wide runtime file descriptor limits are reasonable (OpenBSD)
9 | become: true
10 | ansible.builtin.command: sysctl kern.maxfiles=20000
11 | when: tor_openbsd_maxfiles.stdout|int < 20000
12 |
13 | - name: Ensure system-wide persistent file descriptor limits are reasonable (OpenBSD)
14 | become: true
15 | ansible.builtin.lineinfile:
16 | dest: /etc/sysctl.conf
17 | regexp: ^kern.maxfiles
18 | line: kern.maxfiles=20000
19 | create: true
20 | when: tor_openbsd_maxfiles.stdout|int < 20000
21 |
22 | # We rise openfiles limits for every tor instance separately.
23 | # An instance is identified by its rc.d file name.
24 | - name: Ensure Tor process file descriptor limits are reasonable (OpenBSD)
25 | become: true
26 | ansible.builtin.lineinfile:
27 | dest: /etc/login.conf
28 | line: "tor{{ item.0.ipv4| replace('.','_') }}_{{ item.1.orport }}::openfiles-max=13500::tc=daemon:"
29 | with_nested:
30 | - "{{ tor_ips }}"
31 | - "{{ tor_ports }}"
32 |
--------------------------------------------------------------------------------
/tasks/openbsd_service.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # OpenBSD section (uses service module)
3 | # This is basically a copy from the Linux
4 | # section, but it requires different service
5 | # names and additional arguments.
6 | # =====================================
7 |
8 | # OpenBSD does not support multi-instance rc.d
9 | # # so we link as many pseudo rc scripts as we need.
10 | # # OpenBSD does not like dots in rc filenames so
11 | # # we replace them with underscores.
12 | - name: Create links to the service files (OpenBSD)
13 | become: true
14 | ansible.builtin.file:
15 | src: /etc/rc.d/tor
16 | state: link
17 | path: /etc/rc.d/tor{{ item.0.ipv4| replace('.','_') }}_{{ item.1.orport }}
18 | with_nested:
19 | - "{{ tor_ips }}"
20 | - "{{ tor_ports }}"
21 |
22 | - name: Ensure Tor instances are enabled and started (OpenBSD)
23 | become: true
24 | ansible.builtin.service:
25 | name: tor{{ item.0.ipv4|replace('.','_') }}_{{ item.1.orport }}
26 | arguments: -f {{ tor_ConfDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}.torrc
27 | enabled: true
28 | state: started
29 | with_nested:
30 | - "{{ tor_ips }}"
31 | - "{{ tor_ports }}"
32 |
--------------------------------------------------------------------------------
/tasks/rpm_prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure EPEL repo is installed (yum)
3 | become: true
4 | ansible.builtin.yum:
5 | name: epel-release
6 | when: ansible_pkg_mgr == 'yum'
7 |
8 | - name: Ensure SELinux dependencies are installed
9 | become: true
10 | ansible.builtin.package:
11 | name: libselinux-python,libsemanage-python
12 | state: present
13 | notify: re-gather facts
14 |
15 | # re-gathering facts after installing ansible SELinux dependencies (libselinux-python)
16 | - ansible.builtin.meta: flush_handlers
17 | - name: Ensure SELinux boolean (tor_can_network_relay) is set appropriately
18 | become: true
19 | ansible.posix.seboolean:
20 | name: tor_can_network_relay
21 | state: true
22 | persistent: true
23 | when: ansible_selinux.status == 'enabled'
24 |
25 | - name: Ensure systemd drop-in folder is present (RPM)
26 | become: true
27 | ansible.builtin.file:
28 | path: /etc/systemd/system/tor@.service.d
29 | state: directory
30 | owner: root
31 | mode: "0755"
32 |
33 | # this is needed for a small service file modification (allow it to write to /var/lib/tor-instances)
34 | # without replacing the maintainer's file, for details see
35 | # http://www.freedesktop.org/software/systemd/man/systemd.unit.html#id-1.11.3
36 | - name: Ensure service file drop-in is present (RPM)
37 | become: true
38 | ansible.builtin.copy:
39 | src: local.conf
40 | dest: /etc/systemd/system/tor@.service.d/local.conf
41 | owner: root
42 | mode: "0644"
43 | notify: systemctl daemon-reload
44 |
45 | - ansible.builtin.meta: flush_handlers
46 |
--------------------------------------------------------------------------------
/templates/nginx-reverse-proxy-for-metricsport:
--------------------------------------------------------------------------------
1 | location /{{ lookup('password', '~/.tor/prometheus/metrics_path/' + inventory_hostname + ' length=10 chars=ascii_lowercase') }} {
2 | auth_basic "";
3 | auth_basic_user_file {{ tor_metricsport_htpasswd_file }};
4 | {% set c = namespace(i=0) %}
5 | {% for ip in tor_ips %}
6 | {% for port in tor_ports %}
7 | location /{{ lookup('password', '~/.tor/prometheus/metrics_path/' + inventory_hostname + ' length=10 chars=ascii_lowercase') }}/{{c.i}} {
8 | proxy_pass http://127.0.0.1:{{tor_MetricsPort_offset + c.i}}/metrics;
9 | }
10 | {% set c.i = c.i + 1 %}
11 | {% endfor %}
12 | {% endfor %}
13 | }
14 |
--------------------------------------------------------------------------------
/templates/prometheus-alert-rules:
--------------------------------------------------------------------------------
1 |
2 | groups:
3 | - name: ansible-relayor managed alert rules
4 | rules:
5 | {{ tor_prometheus_alert_rules | to_nice_yaml(indent=2,sort_keys=False) | indent(2,False) }}
6 | {% if tor_prometheus_custom_alert_rules is defined %}
7 | - name: user defined tor alert rules
8 | rules:
9 | {{ tor_prometheus_custom_alert_rules | to_nice_yaml(indent=2,sort_keys=False) | indent(2,False) }}
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/templates/prometheus-scrape-configs:
--------------------------------------------------------------------------------
1 | {% if tor_enableMetricsPort %}
2 | {% set c = namespace(i=0) %}
3 | {% for ip in tor_ips %}
4 | {% for port in tor_ports %}
5 | - job_name: 'tor-{{ansible_fqdn}}-{{c.i}}'
6 | metrics_path: '/{{ lookup('password', '~/.tor/prometheus/metrics_path/' + inventory_hostname + ' length=10 chars=ascii_lowercase') }}/{{c.i}}'
7 | scheme: 'https'
8 | basic_auth:
9 | username: "{{ tor_metricsport_user }}"
10 | password: "{{ lookup('password', tor_prometheus_scrape_password_folder + inventory_hostname ) }}"
11 | static_configs:
12 | - targets:
13 | - "{{ansible_fqdn}}:{{ tor_prometheus_scrape_port }}"
14 | labels:
15 | service: "torrelay"
16 | id: "{{ ip.ipv4 }}_{{ port.orport }}"
17 | {% if tor_nicknamefile is defined %}
18 | tor_nickname: "{{ lookup('csvfile', inventory_hostname~'-'~ip.ipv4~'_'~port.orport~' file='~tor_nicknamefile~' delimiter=,') |regex_replace('[^a-zA-Z0-9]', '') |truncate(19, True, '')}}"
19 | {% elif tor_nickname is defined %}
20 | tor_nickname: "{{ tor_nickname |regex_replace('[^a-zA-Z0-9]', '') |truncate(19, True, '') }}"
21 | {% endif %}
22 | {% if ((tor_ExitRelay == True and tor_ExitRelaySetting_file is not defined) or (tor_ExitRelay == True and tor_ExitRelaySetting_file is defined and (lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_ExitRelaySetting_file~' delimiter=,') == "exit"))) %}
23 | relaytype: "exit"
24 | {% else %}
25 | relaytype: "nonexit"
26 | {% endif %}
27 | {% for label, value in tor_prom_labels.items() %}
28 | {{ label }}: "{{ value }}"
29 | {% endfor %}
30 | {% set c.i = c.i + 1 %}
31 | {% endfor %}
32 | {% endfor %}
33 | {% endif %}
34 | {% if tor_blackbox_exporter_host is defined %}
35 | - job_name: 'tor-blackbox_tcp_connect_{{ansible_fqdn}}'
36 | metrics_path: /probe
37 | scheme: '{{ tor_blackbox_exporter_scheme }}'
38 | {% if tor_blackbox_exporter_username is defined %}
39 | basic_auth:
40 | username: "{{ tor_blackbox_exporter_username }}"
41 | password: "{{ tor_blackbox_exporter_password }}"
42 | {% endif %}
43 | params:
44 | module: [tcp_connect]
45 | static_configs:
46 | - targets:
47 | {% for ip in tor_ips %}
48 | {% for port in tor_ports %}
49 | - {{ ip.ipv4 }}:{{ port.orport }}
50 | {% if port.dirport != 0 %}
51 | - {{ ip.ipv4 }}:{{ port.dirport }}
52 | {% endif %}
53 | {% if ip.ipv6 != '' and tor_IPv6 %}
54 | - "[{{ ip.ipv6 }}]:{{ port.orport }}"
55 | {% if port.dirport != 0 %}
56 | - "[{{ ip.ipv6 }}]:{{ port.dirport }}"
57 | {% endif %}
58 | {% endif %}
59 | {% endfor %}
60 | {% endfor %}
61 | relabel_configs:
62 | - source_labels: [__address__]
63 | target_label: __param_target
64 | - source_labels: [__param_target]
65 | target_label: instance
66 | - target_label: __address__
67 | replacement: {{tor_blackbox_exporter_host}}
68 | {% endif %}
69 |
--------------------------------------------------------------------------------
/templates/tor-exit-notice.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | This is a Tor Exit Router
8 |
9 |
24 |
25 |
26 |
27 |
28 | This is a
29 | Tor Exit Router
30 |
31 |
32 | Most likely you are accessing this website because you had some issue with
33 | the traffic coming from this IP. This router is part of the Tor Anonymity Network, which is
35 | dedicated to providing
36 | privacy to people who need it most: average computer users. This
37 | router IP should be generating no other traffic, unless it has been
38 | compromised.
39 |
40 |
41 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Tor sees use by many
51 | important segments of the population, including whistle blowers,
52 | journalists, Chinese dissidents skirting the Great Firewall and oppressive
53 | censorship, abuse victims, stalker targets, the US military, and law
54 | enforcement, just to name a few. While Tor is not designed for malicious
55 | computer users, it is true that they can use the network for malicious ends.
56 | In reality however, the actual amount of abuse is quite low. This
58 | is largely because criminals and hackers have significantly better access to
59 | privacy and anonymity than do the regular users whom they prey upon. Criminals
60 | can and do build,
62 | sell, and trade far larger and more
64 | powerful networks than Tor on a daily basis. Thus, in the mind of this
65 | operator, the social need for easily accessible censorship-resistant private,
66 | anonymous communication trumps the risk of unskilled bad actors, who are
67 | almost always more easily uncovered by traditional police work than by
68 | extensive monitoring and surveillance anyway.
69 |
70 |
71 | In terms of applicable law, the best way to understand Tor is to consider it a
72 | network of routers operating as common carriers, much like the Internet
73 | backbone. However, unlike the Internet backbone routers, Tor routers
74 | explicitly do not contain identifiable routing information about the source of
75 | a packet, and no single Tor node can determine both the origin and destination
76 | of a given transmission.
77 |
78 |
79 | As such, there is little the operator of this router can do to help you track
80 | the connection further. This router maintains no logs of any of the Tor
81 | traffic, so there is little that can be done to trace either legitimate or
82 | illegitimate traffic (or to filter one from the other). Attempts to
83 | seize this router will accomplish nothing.
84 |
85 |
86 |
87 |
88 | Furthermore, this machine also serves as a carrier of email, which means that
89 | its contents are further protected under the ECPA. 18
91 | USC 2707 explicitly allows for civil remedies ($1000/account
92 | plus legal fees)
93 | in the event of a seizure executed without good faith or probable cause (it
94 | should be clear at this point that traffic originating from this IP address
95 | should not constitute probable cause to seize the
96 | machine). Similar considerations exist for 1st amendment content on this
97 | machine.
98 |
99 |
101 |
102 |
103 | If you are a representative of a company who feels that this router is being
104 | used to violate the DMCA, please be aware that this machine does not host or
105 | contain any illegal content. Also be aware that network infrastructure
106 | maintainers are not liable for the type of content that passes over their
107 | equipment, in accordance with DMCA
109 | "safe harbor" provisions. In other words, you will have just as much luck
110 | sending a takedown notice to the Internet backbone providers. Please consult
111 | EFF's prepared
112 | response for more information on this matter.
113 |
114 | For more information, please consult the following documentation:
115 |
116 |
117 | - Tor Overview
118 | - Tor Abuse FAQ
119 | - Tor Legal FAQ
120 |
121 |
122 | {% if tor_AbuseEmailAddress is defined %}
123 |
124 | That being said, if you still have a complaint about the router, you may
125 | email the maintainer. If
126 | complaints are related to a particular service that is being abused, I will
127 | consider removing that service from my exit policy, which would prevent my
128 | router from allowing that traffic to exit through it. I can only do this on an
129 | IP+destination port basis, however. Common P2P ports are
130 | already blocked.
131 | {% endif %}
132 |
133 |
134 | You also have the option of blocking this IP address and others on
135 | the Tor network if you so desire. The Tor project provides a web service
137 | to fetch a list of all IP addresses of Tor exit nodes that allow exiting to a
138 | specified IP:port combination.
139 | Please be considerate when using this option. It would be unfortunate to deny all Tor users access
140 | to your site indefinitely simply because of a few bad apples.
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/templates/torrc:
--------------------------------------------------------------------------------
1 | # ansible-relayor generated torrc configuration file
2 | # Note: manual changes will be OVERWRITTEN on the next ansible-playbook run
3 |
4 | OfflineMasterKey 1
5 | RunAsDaemon {{ tor_RunAsDaemon }}
6 | Log {{ tor_LogLevel }} syslog
7 | OutboundBindAddress {{ item.0.ipv4 }}
8 | SocksPort 0
9 | User _tor-{{ item.0.ipv4 }}_{{ item.1.orport }}
10 | DataDirectory {{ tor_DataDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}
11 | ORPort {{ item.0.ipv4 }}:{{ item.1.orport }}
12 | {% if item.0.ipv6 != "" and item.0.ipv6 != "False" and tor_IPv6 == True %}
13 | ORPort [{{item.0.ipv6}}]:{{item.1.orport}}
14 | OutboundBindAddress [{{item.0.ipv6}}]
15 | {% endif %}
16 |
17 | {% if item.1.dirport != 0 %}
18 | DirPort {{ item.0.ipv4 }}:{{ item.1.dirport }}
19 | {% endif %}
20 | {% if tor_Address is defined %}
21 | Address {{ tor_Address }}
22 | {% elif tor_maxPublicIPs > 1 and tor_available_public_ipv4s|length > 1 %}
23 | Address {{ item.0.ipv4 }}
24 | {% endif %}
25 |
26 | SyslogIdentityTag {{ item.0.ipv4 }}_{{ item.1.orport }}
27 | {% if ansible_os_family == 'FreeBSD' %}
28 | PidFile {{ tor_PidDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/pid
29 | {% endif %}
30 |
31 | {% if tor_enableMetricsPort %}
32 | MetricsPort 127.0.0.1:{{tor_MetricsPort_offset + loop_idx}}
33 | MetricsPortPolicy accept 127.0.0.1
34 | {% if tor_config.OverloadStatistics is not defined %}
35 | OverloadStatistics 0
36 | {% endif %}
37 | {% endif %}
38 |
39 | {% if tor_enableControlSocket %}
40 | ControlSocket {{ tor_PidDir }}/{{ item.0.ipv4 }}_{{ item.1.orport }}/control GroupWritable RelaxDirModeCheck
41 | {% elif tor_enableControlSocket == False and ansible_pkg_mgr == 'apt' %}
42 | ControlSocket 0
43 | CookieAuthentication 0
44 | {% endif %}
45 |
46 | {% if tor_nicknamefile is defined %}
47 | Nickname {{ lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_nicknamefile~' delimiter=,') |regex_replace('[^a-zA-Z0-9]', '') |truncate(19, True, '')}}
48 | {% elif tor_nickname is defined %}
49 | Nickname {{ tor_nickname |regex_replace('[^a-zA-Z0-9]', '') |truncate(19, True, '') }}
50 | {% endif %}
51 | {% if tor_ContactInfo is defined %}
52 | ContactInfo {{ tor_ContactInfo }}
53 | {% endif %}
54 |
55 | {% if ((tor_ExitRelay == True and tor_ExitRelaySetting_file is not defined) or (tor_ExitRelay == True and tor_ExitRelaySetting_file is defined and (lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_ExitRelaySetting_file~' delimiter=,') == "exit"))) %}
56 | # we are an exit relay!
57 | ExitRelay 1
58 | {% if item.0.ipv6 != "" and item.0.ipv6 != "False" and tor_IPv6 == True and tor_IPv6Exit == True %}
59 | IPv6Exit 1
60 | {% if item.1.dirport != 0 %}
61 | DirPort [{{ item.0.ipv6 }}]:{{ item.1.dirport }} NoAdvertise
62 | {% endif %}
63 | {% endif %}
64 | {% if tor_ExitNoticePage == True and tor_config.DirPortFrontPage is not defined %}
65 | DirPortFrontPage {{ tor_ConfDir }}/tor-exit-notice.html
66 | {% endif %}
67 |
68 | {% if tor_ExitPolicy_file is defined and (lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_ExitPolicy_file~' delimiter=;') != []) %}
69 | ExitPolicy {{ lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_ExitPolicy_file~' delimiter=;') }}
70 | {% else %}
71 | {% for entry in tor_ExitPolicy %}
72 | ExitPolicy {{entry}}
73 | {% endfor %}
74 | {% endif %}
75 | {% else %}
76 | ExitRelay 0
77 | ExitPolicy reject *:*
78 | {% endif %}
79 |
80 | {% if tor_RelayBandwidthRate_file is defined and (lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_RelayBandwidthRate_file~' delimiter=,') != "") %}
81 | RelayBandwidthRate {{ lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_RelayBandwidthRate_file~' delimiter=,') }}
82 | {% elif tor_RelayBandwidthRate is defined %}
83 | RelayBandwidthRate {{ tor_RelayBandwidthRate }}
84 | {% endif %}
85 | {% if tor_RelayBandwidthBurst_file is defined and (lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_RelayBandwidthBurst_file~' delimiter=,') != "") %}
86 | RelayBandwidthBurst {{ lookup('csvfile', inventory_hostname~'-'~item.0.ipv4~'_'~item.1.orport~' file='~tor_RelayBandwidthBurst_file~' delimiter=,') }}
87 | {% elif tor_RelayBandwidthBurst is defined %}
88 | RelayBandwidthBurst {{ tor_RelayBandwidthBurst }}
89 | {% endif %}
90 |
91 | {% if ansible_os_family == 'Debian' and tor_config.Sandbox is not defined %}
92 | Sandbox 1
93 | {% endif %}
94 | {% if tor_config.NoExec is not defined %}
95 | NoExec 1
96 | {% endif %}
97 |
98 | {% for config_item, value in tor_config.items() | sort() %}
99 | {% if config_item not in ["ContactInfo","OfflineMasterKey","RunAsDaemon","Log","SocksPort","OutboundBindAddress","User","DataDirectory","ORPort","OutboundBindAddress","DirPort","SyslogIdentityTag","PidFile","MetricsPort","MetricsPortPolicy","ControlSocket","CookieAuthentication","Nickname","ExitRelay","IPv6Exit","ExitPolicy","RelayBandwidthRate","RelayBandwidthBurst","SigningKeyLifetime"] %}
100 | {{ config_item }} {{ value }}
101 | {% endif %}
102 | {% endfor %}
103 |
104 | MyFamily {{ tor_family.stdout }}
105 | {% if tor_happy_family %}
106 | FamilyId {{ lookup('ansible.builtin.file', tor_local_happy_family_folder~'/'~tor_happy_family_basename~'.public_family_id') }}
107 | {% endif %}
108 | # end of torrc
109 |
--------------------------------------------------------------------------------
/test/integration/default/2publicIPs-exit-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_maxPublicIPs: 2
7 | - tor_ExitRelay: true
8 | roles:
9 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
10 |
--------------------------------------------------------------------------------
/test/integration/default/2publicIPs-guard-metricsport.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_maxPublicIPs: 2
7 | - tor_enableMetricsPort: true
8 | - tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
9 | - tor_blackbox_exporter_host: 127.0.0.1:9115
10 | - tor_prometheus_host: 127.0.0.1
11 | roles:
12 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
13 |
--------------------------------------------------------------------------------
/test/integration/default/2publicIPs-guard-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_maxPublicIPs: 2
7 | roles:
8 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
9 |
--------------------------------------------------------------------------------
/test/integration/default/2publicIPs-guard4-metricsport.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | tor_maxPublicIPs: 2
7 | tor_enableMetricsPort: true
8 | tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
9 | tor_blackbox_exporter_host: 127.0.0.1:9115
10 | tor_prometheus_host: 127.0.0.1
11 | tor_ports:
12 | - orport: 9000
13 | dirport: 9001
14 | - orport: 9100
15 | dirport: 9101
16 | - orport: 9200
17 | dirport: 9201
18 | - orport: 9300
19 | dirport: 9301
20 | roles:
21 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
22 |
--------------------------------------------------------------------------------
/test/integration/default/exit-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | tor_ExitRelay: true
7 | roles:
8 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
9 |
--------------------------------------------------------------------------------
/test/integration/default/exit-node4.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | tor_ExitRelay: true
7 | tor_ports:
8 | - orport: 9000
9 | dirport: 9001
10 | - orport: 9100
11 | dirport: 9101
12 | - orport: 9200
13 | dirport: 9201
14 | - orport: 9300
15 | dirport: 9301
16 | roles:
17 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
18 |
--------------------------------------------------------------------------------
/test/integration/default/exit-per-instance-exitpolicy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_ExitRelay: true
7 | - tor_ExitPolicy_file: vars/per-instance-exitpolicy
8 | roles:
9 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
10 |
--------------------------------------------------------------------------------
/test/integration/default/guard-alpha-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_alpha: true
7 | roles:
8 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
9 |
--------------------------------------------------------------------------------
/test/integration/default/guard-blackbox-exporter.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_blackbox_exporter_username: testuser
7 | - tor_blackbox_exporter_host: 127.0.0.1:9115
8 | - tor_prometheus_host: 127.0.0.1
9 | roles:
10 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
11 |
--------------------------------------------------------------------------------
/test/integration/default/guard-metricsport-blackbox-alertrules.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_enableMetricsPort: true
7 | - tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
8 | - tor_gen_prometheus_alert_rules: true
9 | - tor_blackbox_exporter_host: 127.0.0.1:9115
10 | - tor_prometheus_host: 127.0.0.1
11 | - tor_prom_labels:
12 | label1: "value1"
13 | label2: "value2"
14 | roles:
15 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
16 |
--------------------------------------------------------------------------------
/test/integration/default/guard-metricsport-blackbox-customalertrules.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_enableMetricsPort: true
7 | - tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
8 | - tor_gen_prometheus_alert_rules: true
9 | - tor_blackbox_exporter_host: 127.0.0.1:9115
10 | - tor_prometheus_host: 127.0.0.1
11 | - tor_prom_labels:
12 | label1: "value1"
13 | label2: "value2"
14 | - tor_prometheus_custom_alert_rules:
15 | - alert: TorOnionskinsDropped
16 | expr: 'rate(tor_relay_load_onionskins_total{action="dropped"}[15m]) > 0'
17 | for: 15m
18 | labels:
19 | severity: warning
20 | roles:
21 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
22 |
--------------------------------------------------------------------------------
/test/integration/default/guard-metricsport-blackbox.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_enableMetricsPort: true
7 | - tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
8 | - tor_blackbox_exporter_host: 127.0.0.1:9115
9 | - tor_prometheus_host: 127.0.0.1
10 | - tor_prom_labels:
11 | label1: "value1"
12 | label2: "value2"
13 | roles:
14 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
15 |
--------------------------------------------------------------------------------
/test/integration/default/guard-nightly-happy-families.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_nightly_builds: true
7 | - tor_happy_family: true
8 | roles:
9 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
10 |
--------------------------------------------------------------------------------
/test/integration/default/guard-nightly-metricsport-blackbox.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_nightly_builds: true
7 | - tor_enableMetricsPort: true
8 | - tor_metricsport_nginx_config_file: /tmp/web-reverse-proxy-{{ inventory_hostname }}
9 | - tor_blackbox_exporter_host: 127.0.0.1:9115
10 | - tor_prometheus_host: 127.0.0.1
11 | roles:
12 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
13 |
--------------------------------------------------------------------------------
/test/integration/default/guard-nightly-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | - tor_nightly_builds: true
7 | roles:
8 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
9 |
--------------------------------------------------------------------------------
/test/integration/default/guard-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | roles:
6 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
7 |
--------------------------------------------------------------------------------
/test/integration/default/guard-node4.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | tor_ports:
7 | - orport: 9000
8 | dirport: 9001
9 | - orport: 9100
10 | dirport: 9101
11 | - orport: 9200
12 | dirport: 9201
13 | - orport: 9300
14 | dirport: 9301
15 | roles:
16 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
17 |
--------------------------------------------------------------------------------
/test/integration/default/mixed-node.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars_files:
4 | - vars/dry-run-vars.yml
5 | vars:
6 | tor_ExitRelay: true
7 | tor_ExitRelaySetting_file: vars/exit-conf
8 | roles:
9 | - "{{ playbook_dir | regex_replace('test/integration/default$') }}"
10 |
--------------------------------------------------------------------------------
/test/integration/default/vars/dry-run-vars.yml:
--------------------------------------------------------------------------------
1 | ---
2 | tor_offline_masterkey_dir: ~/.tor/test/offlinemasterkeys/{{ inventory_hostname }}
3 | tor_apt_update_cache: false
4 | tor_ContactInfo: "ansible-relayor test-kitchen (you should never see this on a public relay) https://github.com/nusenu/ansible-relayor"
5 | tor_config:
6 | DisableNetwork: 1
7 | PublishServerDescriptor: 0
8 | ShutdownWaitLength: 0
9 |
--------------------------------------------------------------------------------
/test/integration/default/vars/exit-conf:
--------------------------------------------------------------------------------
1 | mixed-freebsd-10.0.2.15_9000,exit
2 | mixed-freebsd-10.0.2.15_9100,
3 | mixed-debian-stable-10.0.2.15_9000,exit
4 | mixed-debian-stable-10.0.2.15_9100,
5 | mixed-ubuntu-lts-10.0.2.15_9000,exit
6 | mixed-ubuntu-lts-10.0.2.15_9100,
7 |
--------------------------------------------------------------------------------
/test/integration/default/vars/per-instance-exitpolicy:
--------------------------------------------------------------------------------
1 | exit-per-instance-exitpolicy-debian-stable-10.0.2.15_9100;reject *4:25,reject *6:12
2 | exit-per-instance-exitpolicy-ubuntu-lts-10.0.2.15_9100;reject *4:25,reject *6:123
3 | exit-per-instance-exitpolicy-freebsd-10.0.2.15_9100;reject *4:25,reject *6:123
4 |
--------------------------------------------------------------------------------
/test/vagrant_provisions/freebsd_vagrant_provision.rb:
--------------------------------------------------------------------------------
1 | Vagrant.configure("2") do |config|
2 | config.vm.provision "shell", inline: <<-SHELL
3 | echo 'FreeBSD: { url: "https://pkg.FreeBSD.org/${ABI}/quarterly" , signature_type: "fingerprints", fingerprints: "/usr/share/keys/pkg", enabled: yes }' > /etc/pkg/FreeBSD.conf
4 | sudo pkg install -y python
5 | SHELL
6 | end
7 |
--------------------------------------------------------------------------------
/test/vagrant_provisions/openbsd_vagrant_provision.rb:
--------------------------------------------------------------------------------
1 | Vagrant.configure("2") do |config|
2 | config.vm.provision "shell", inline: <<-SHELL
3 | sudo pkg_add python3
4 | sudo ln -s /usr/local/bin/python3 /usr/bin/python
5 | SHELL
6 | end
7 |
--------------------------------------------------------------------------------
/vars/os_Debian.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tor_user: debian-tor
4 | tor_DataDir: /var/lib/tor-instances
5 | tor_ConfDir: /etc/tor/instances
6 | tor_RunAsDaemon: 0
7 | tor_packages:
8 | - deb.torproject.org-keyring
9 | - tor
10 | - tor-geoipdb
11 | tor_htpasswd_dependency: python3-passlib
12 |
--------------------------------------------------------------------------------
/vars/os_FreeBSD.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tor_DataDir: /var/db/tor-instances
4 | tor_ConfDir: /usr/local/etc/tor/enabled
5 | tor_htpasswd_dependency: py39-passlib
6 | tor_metricsport_htpasswd_file_owner: www
7 |
--------------------------------------------------------------------------------
/vars/os_OpenBSD.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tor_DataDir: /var/tor-instances
4 | tor_ConfDir: /etc/tor/enabled
5 | tor_htpasswd_dependency: py3-passlib
6 | tor_metricsport_htpasswd_file_owner: www
7 |
--------------------------------------------------------------------------------
/vars/os_RedHat.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tor_user: toranon
4 | tor_ConfDir: /etc/tor
5 | tor_RunAsDaemon: 0
6 | tor_DataDir: /var/lib/tor-instances
7 |
--------------------------------------------------------------------------------
/vars/private_IPv4_only.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | tor_v4ips: "{{ ansible_all_ipv4_addresses[0:tor_maxPrivateIPs] | ansible.utils.ipv4('address') }}"
4 | tor_ipv4_count: "{{ tor_v4ips | length | int }}"
5 |
--------------------------------------------------------------------------------