---
- include_tasks: utils/once/flag.yml
- name: "β
Assert the Tor network flavor is public or chutney"
ansible.builtin.assert:
that:
- TOR_FLAVOR in ['public', 'chutney']
fail_msg: "services.tor.flavor must be 'public' or 'chutney' (got '{{ TOR_FLAVOR }}')"
quiet: true
- name: "π€ Dispatch Tor network flavor '{{ TOR_FLAVOR }}'"
ansible.builtin.include_tasks: "flavor/{{ TOR_FLAVOR }}/main.yml"
- name: "π§
Ensure Tor config directory exists"
ansible.builtin.file:
path: "{{ TOR_CONFIG_DIR }}"
state: directory
owner: root
group: root
mode: "0755"
become: true
- name: "π§
Ensure Tor data + HiddenServiceDir exist"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0700"
loop:
- "{{ TOR_DATA_DIR }}"
- "{{ TOR_HS_DIR }}"
become: true
- name: "π Install authoritative hidden-service keys (must match DOMAIN_PRIMARY)"
ansible.builtin.copy:
src: "{{ [ TOR_HS_SRC_DIR, item.name ] | path_join }}"
dest: "{{ [ TOR_HS_DIR, item.name ] | path_join }}"
owner: root
group: root
mode: "{{ item.mode }}"
force: true
loop:
- { name: "hostname", mode: "0600" }
- { name: "hs_ed25519_public_key", mode: "0600" }
- { name: "hs_ed25519_secret_key", mode: "0600" }
become: true
register: _tor_hs_keys
- name: "π Re-gather the docker0 fact so torrc and dnsmasq bind the live bridge"
ansible.builtin.setup:
gather_subset:
- "!all"
- network
filter:
- ansible_docker0
- name: "π Render torrc"
ansible.builtin.template: # nocheck: stack-host-template (per-host torrc under DIR_VAR_LIB; tor is a node-local host-network sidecar)
src: torrc.j2
dest: "{{ TOR_TORRC_HOST }}"
owner: root
group: root
mode: "0644"
become: true
register: _tor_torrc
- name: "π Ensure the Tor compose instance dir exists (build context)"
ansible.builtin.file:
path: "{{ TOR_INSTANCE_DIR }}"
state: directory
owner: root
group: root
mode: "0755"
become: true
- name: "π Stage the Tor container entrypoint into the build context"
stack_host_copy:
src: "{{ role_path }}/files/shell/entrypoint.sh"
dest: "{{ TOR_ENTRYPOINT_HOST }}"
mode: "0755"
become: true
- name: "π©Ί Stage the Tor container healthcheck into the build context"
stack_host_copy:
src: "{{ role_path }}/files/shell/healthcheck.sh"
dest: "{{ TOR_HEALTHCHECK_HOST }}"
mode: "0755"
become: true
- name: "π§© Load the container CLI the Tor run probe calls"
include_role:
name: sys-svc-container
public: false
when: run_once_sys_svc_container is not defined
- name: "π Check whether Tor already runs from an earlier deploy"
ansible.builtin.command: >-
{% if IS_SWARM_MODE %}
container service ps -q --filter desired-state=running
{{ lookup('container_service', application_id, 'tor') }}
{% else %}
container ps -q --filter status=running --filter name=^{{ TOR_CONTAINER }}$
{% endif %}
register: _tor_running_before
changed_when: false
failed_when:
- _tor_running_before.rc != 0
- "'no such service' not in (_tor_running_before.stderr | lower)"
become: true
when: IS_STACK_HOST | bool
- name: "π§© For '{{ application_id }}': Load sys-svc-compose"
include_role:
name: sys-svc-compose
vars:
compose_repository_pull: false
compose_handlers_flush: true
application_id: svc-net-tor
- name: "π Restart Tor to apply hidden-service key / torrc changes"
ansible.builtin.shell: |
set -euo pipefail
compose up -d --force-recreate --remove-orphans
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
become: true
when: IS_COMPOSE_MODE and (TOR_RESTART_REQUIRED | bool)
- name: "π Force Tor service redeploy to apply hidden-service key / torrc changes"
ansible.builtin.command: >-
container service update --force
{{ lookup('container_service', application_id, 'tor') }}
register: _tor_swarm_update
changed_when: _tor_swarm_update.rc == 0
become: true
when:
- IS_SWARM_MODE
- IS_STACK_HOST | bool
- TOR_RESTART_REQUIRED | bool
- name: "π§
Bring up the transparent .onion egress router"
when: TOR_EGRESS_ROUTER_ENABLED | bool
block:
- name: "π§
Wire the host-level transparent .onion egress router"
include_tasks:
file: router.yml
apply:
become: true
when: IS_COMPOSE_MODE or (IS_STACK_HOST | bool)
- name: "π§
Verify the node onion is reachable"
ansible.builtin.include_tasks: onion_reach.yml
when: TOR_NODE_ONION | length > 0