00_core.ymlΒΆ

---
- 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