- name: Join machine to headscale mesh network hosts: all become: yes vars_files: - ../infra_vars.yml - ../services/headscale/headscale_vars.yml vars: headscale_domain: "https://{{ headscale_subdomain }}.{{ root_domain }}" tasks: - name: Set headscale host set_fact: headscale_host: "{{ groups['spacey'][0] }}" - name: Set facts for headscale server connection set_fact: headscale_user: "{{ hostvars[headscale_host]['ansible_user'] }}" headscale_key: "{{ hostvars[headscale_host]['ansible_ssh_private_key_file'] | default('') }}" headscale_port: "{{ hostvars[headscale_host]['ansible_port'] | default(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 1m --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_lsb.codename }} 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 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: Display Tailscale status command: tailscale status register: tailscale_status changed_when: false - name: Show Tailscale connection status debug: msg: "{{ tailscale_status.stdout_lines }}"