The four ports were the only entries in services_config.yml with a real
justification: each is read twice, by the role that deploys the service on its
own box AND by a socket-proxy or Caddy play that runs on the EDGE host and
publishes it. A role default is invisible to that second play.
But the shape was wrong in two ways. The file had to be named in vars_files: by
30 plays - opt-in configuration that someone will eventually forget - and five
role defaults silently interpolated service_settings.*, so bitcoin_knots,
fulcrum, datum_gateway and mempool were not self-contained: using any of them
without that one vars_file entry broke it.
Each port now lives in host_vars/<owning box>/main.yml:
host_vars/knots_box_local/main.yml bitcoin_p2p_port, datum_gateway_api_port,
datum_gateway_stratum_port
host_vars/fulcrum_box_local/main.yml fulcrum_ssl_port
host_vars/mempool_box_local/main.yml mempool_frontend_port
host_vars auto-loads and outranks role defaults, so the owning role picks the
value up with no vars_files at all, and the edge play reads the same single
definition as hostvars['<host>'].<name>. The role defaults keep the protocol
standard (8333, 50002, ...) so each role still works standalone, with the live
deployment's value in host_vars winning.
Also fixed a fourth copy of an inventory identity: the mempool Caddy play had
"mempool-box:{{ ... }}" hardcoded in the upstream. It now derives the host from
hostvars['mempool_box_local'].ansible_host, so inventory is the only place any
box's name is written down.
services_config.yml is deleted, with 25 more vars_files entries across 19
playbooks. Between this and the previous commit, 87 vars_files entries are gone
and every variable in the repo now comes from group_vars/all, host_vars,
inventory, a role default, or that service's own *_vars.yml.
Verification: an edge-host probe resolves all eight ports and hostnames to
byte-identical values to the ones services_config.yml used to supply. Each
owning host resolves its own port through host_vars. All 37 playbooks'
--list-tasks output is unchanged. The four edge plays that consume these values
all check-diff changed=0 - the socket-proxy and Caddy units on vipy are
byte-identical, which is the direct proof the rewiring landed on the same
values. fulcrum and datum-gateway check-diff exactly as before (ok=28/changed=1
and ok=15/changed=1, both the known timer re-arm).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
146 lines
5.3 KiB
YAML
146 lines
5.3 KiB
YAML
- name: Join machine to headscale mesh network
|
|
hosts: managed
|
|
become: yes
|
|
vars:
|
|
headscale_host_name: "spacey"
|
|
headscale_subdomain: "{{ subdomains.headscale }}"
|
|
headscale_domain: "https://{{ headscale_subdomain }}.{{ root_domain }}"
|
|
|
|
tasks:
|
|
- name: Set facts for headscale server connection
|
|
set_fact:
|
|
headscale_host: "{{ hostvars.get(headscale_host_name, {}).get('ansible_host', headscale_host_name) }}"
|
|
headscale_user: "{{ hostvars.get(headscale_host_name, {}).get('ansible_user', 'counterweight') }}"
|
|
headscale_key: "{{ hostvars.get(headscale_host_name, {}).get('ansible_ssh_private_key_file', '') }}"
|
|
headscale_port: "{{ hostvars.get(headscale_host_name, {}).get('ansible_port', 22) }}"
|
|
|
|
- name: Get user ID for namespace from headscale server via lapy
|
|
delegate_to: "{{ groups['lapy'][0] }}"
|
|
become: no
|
|
vars:
|
|
ssh_args: "{{ ('-i ' + headscale_key + ' ' if headscale_key else '') + '-p ' + headscale_port|string }}"
|
|
shell: >
|
|
ssh {{ ssh_args }}
|
|
{{ headscale_user }}@{{ headscale_host }}
|
|
"sudo headscale users list -o json"
|
|
register: users_list_result
|
|
changed_when: false
|
|
failed_when: users_list_result.rc != 0
|
|
|
|
- name: Extract user ID from users list
|
|
set_fact:
|
|
headscale_user_id: "{{ (users_list_result.stdout | from_json) | selectattr('name', 'equalto', headscale_namespace) | map(attribute='id') | first }}"
|
|
failed_when: headscale_user_id is not defined or headscale_user_id == ''
|
|
|
|
- name: Generate pre-auth key from headscale server via lapy
|
|
delegate_to: "{{ groups['lapy'][0] }}"
|
|
become: no
|
|
vars:
|
|
ssh_args: "{{ ('-i ' + headscale_key + ' ' if headscale_key else '') + '-p ' + headscale_port|string }}"
|
|
shell: >
|
|
ssh {{ ssh_args }}
|
|
{{ headscale_user }}@{{ headscale_host }}
|
|
"sudo headscale preauthkeys create --user {{ headscale_user_id }} --expiration 10m --output json"
|
|
register: preauth_key_result
|
|
changed_when: true
|
|
failed_when: preauth_key_result.rc != 0
|
|
|
|
- name: Extract auth key from preauth result
|
|
set_fact:
|
|
auth_key: "{{ (preauth_key_result.stdout | from_json).key }}"
|
|
failed_when: auth_key is not defined or auth_key == ''
|
|
|
|
- name: Install required packages for Tailscale
|
|
apt:
|
|
name:
|
|
- curl
|
|
- ca-certificates
|
|
- gnupg
|
|
state: present
|
|
update_cache: yes
|
|
|
|
- name: Create directory for GPG keyrings
|
|
file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
mode: '0755'
|
|
|
|
- name: Download Tailscale GPG key
|
|
get_url:
|
|
url: https://pkgs.tailscale.com/stable/debian/bookworm.gpg
|
|
dest: /etc/apt/keyrings/tailscale.gpg
|
|
mode: '0644'
|
|
|
|
- name: Add Tailscale repository
|
|
apt_repository:
|
|
repo: "deb [signed-by=/etc/apt/keyrings/tailscale.gpg] https://pkgs.tailscale.com/stable/debian {{ ansible_distribution_release }} main"
|
|
state: present
|
|
update_cache: yes
|
|
|
|
- name: Install Tailscale
|
|
apt:
|
|
name: tailscale
|
|
state: present
|
|
update_cache: yes
|
|
|
|
- name: Enable and start Tailscale service
|
|
systemd:
|
|
name: tailscaled
|
|
enabled: yes
|
|
state: started
|
|
|
|
- name: Configure Tailscale to use headscale server
|
|
command: >
|
|
tailscale up
|
|
--login-server {{ headscale_domain }}
|
|
--authkey {{ auth_key }}
|
|
--accept-dns=true
|
|
--hostname={{ ansible_hostname }}
|
|
--reset
|
|
register: tailscale_up_result
|
|
changed_when: "'already authenticated' not in tailscale_up_result.stdout"
|
|
failed_when: tailscale_up_result.rc != 0 and 'already authenticated' not in tailscale_up_result.stdout
|
|
|
|
- name: Wait for Tailscale to be fully connected
|
|
pause:
|
|
seconds: 2
|
|
|
|
- name: Get node ID from headscale server
|
|
delegate_to: "{{ groups['lapy'][0] }}"
|
|
become: no
|
|
vars:
|
|
ssh_args: "{{ ('-i ' + headscale_key + ' ' if headscale_key else '') + '-p ' + headscale_port|string }}"
|
|
shell: >
|
|
ssh {{ ssh_args }}
|
|
{{ headscale_user }}@{{ headscale_host }}
|
|
"sudo headscale nodes list -o json"
|
|
register: nodes_list_result
|
|
changed_when: false
|
|
failed_when: nodes_list_result.rc != 0
|
|
|
|
- name: Extract node ID for this host
|
|
set_fact:
|
|
headscale_node_id: "{{ (nodes_list_result.stdout | from_json) | selectattr('given_name', 'equalto', ansible_hostname) | map(attribute='id') | first }}"
|
|
failed_when: headscale_node_id is not defined or headscale_node_id == ''
|
|
|
|
- name: Tag node with its hostname
|
|
delegate_to: "{{ groups['lapy'][0] }}"
|
|
become: no
|
|
vars:
|
|
ssh_args: "{{ ('-i ' + headscale_key + ' ' if headscale_key else '') + '-p ' + headscale_port|string }}"
|
|
shell: >
|
|
ssh {{ ssh_args }}
|
|
{{ headscale_user }}@{{ headscale_host }}
|
|
"sudo headscale nodes tag --tags tag:{{ ansible_hostname }} -i {{ headscale_node_id }}"
|
|
register: tag_result
|
|
changed_when: true
|
|
failed_when: tag_result.rc != 0
|
|
|
|
- name: Display Tailscale status
|
|
command: tailscale status
|
|
register: tailscale_status
|
|
changed_when: false
|
|
|
|
- name: Show Tailscale connection status
|
|
debug:
|
|
msg: "{{ tailscale_status.stdout_lines }}"
|