2025-10-22 23:58:38 +02:00
- name : Deploy headscale and configure Caddy reverse proxy
2025-11-03 16:51:53 +01:00
hosts : spacey
2025-10-22 23:58:38 +02:00
become : no
vars_files :
- ../../infra_vars.yml
- ./headscale_vars.yml
vars :
headscale_domain : "{{ headscale_subdomain }}.{{ root_domain }}"
2025-11-03 16:51:53 +01:00
headscale_base_domain : "tailnet.{{ root_domain }}"
2025-10-22 23:58:38 +02:00
tasks :
- name : Install required packages
become : yes
apt :
name :
- wget
- gnupg
state : present
update_cache : yes
- name : Download headscale DEB package
get_url :
url : "https://github.com/juanfont/headscale/releases/download/v{{ headscale_version }}/headscale_{{ headscale_version }}_linux_amd64.deb"
dest : /tmp/headscale.deb
mode : '0644'
- name : Install headscale package
become : yes
apt :
deb : /tmp/headscale.deb
state : present
- name : Remove temporary DEB file
file :
path : /tmp/headscale.deb
state : absent
2025-11-03 16:51:53 +01:00
- name : Ensure headscale user exists
become : yes
user :
name : headscale
system : yes
shell : /usr/sbin/nologin
home : /var/lib/headscale
create_home : yes
state : present
2025-10-22 23:58:38 +02:00
- name : Create headscale data directory
become : yes
file :
path : /var/lib/headscale
state : directory
owner : headscale
group : headscale
mode : '0750'
- name : Create headscale run directory
become : yes
file :
path : /var/run/headscale
state : directory
owner : headscale
group : headscale
2025-11-03 16:51:53 +01:00
mode : '0770'
2025-10-22 23:58:38 +02:00
- name : Ensure headscale user owns data directory
become : yes
file :
path : /var/lib/headscale
owner : headscale
group : headscale
recurse : yes
2025-11-03 16:51:53 +01:00
mode : '0750'
- name : Add counterweight user to headscale group
become : yes
user :
name : counterweight
groups : headscale
append : yes
2025-10-22 23:58:38 +02:00
- name : Create ACL policies file
become : yes
copy :
dest : /etc/headscale/acl.json
content : |
{
"ACLs": [ ] ,
"Groups": {},
"Hosts": {},
"TagOwners": {},
"Tests": [ ]
}
owner : headscale
group : headscale
2025-11-03 16:51:53 +01:00
mode : '0640'
2025-10-22 23:58:38 +02:00
notify : Restart headscale
- name : Deploy headscale configuration file
become : yes
copy :
dest : /etc/headscale/config.yaml
content : |
server_url : https://{{ headscale_domain }}
listen_addr : 0.0 .0 .0 : {{ headscale_port }}
grpc_listen_addr : 0.0 .0 .0 : {{ headscale_grpc_port }}
grpc_allow_insecure : false
private_key_path : /var/lib/headscale/private.key
noise :
private_key_path : /var/lib/headscale/noise_private.key
prefixes :
v4 : 100.64 .0 .0 /10
v6 : fd7a:115c:a1e0::/48
derp :
server :
enabled : true
region_id : 999
region_code : "headscale"
region_name : "Headscale Embedded DERP"
verify_clients : true
stun_listen_addr : "0.0.0.0:3478"
private_key_path : /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region : true
urls :
- https://controlplane.tailscale.com/derpmap/default
database :
type : sqlite3
sqlite :
path : /var/lib/headscale/db.sqlite
unix_socket : /var/run/headscale/headscale.sock
unix_socket_permission : "0770"
log :
level : info
format : text
policy :
path : /etc/headscale/acl.json
dns :
2025-11-03 16:51:53 +01:00
base_domain : {{ headscale_base_domain | quote }}
2025-10-22 23:58:38 +02:00
magic_dns : true
search_domains :
2025-11-03 16:51:53 +01:00
- {{ headscale_base_domain | quote }}
2025-10-22 23:58:38 +02:00
nameservers :
global :
- 1.1 .1 .1
- 1.0 .0 .1
owner : root
2025-11-03 16:51:53 +01:00
group : headscale
mode : '0640'
2025-10-22 23:58:38 +02:00
notify : Restart headscale
- name : Test headscale configuration
become : yes
command : headscale configtest
register : headscale_config_test
failed_when : headscale_config_test.rc != 0
- name : Display headscale config test results
debug :
msg : "{{ headscale_config_test.stdout }}"
- name : Enable and start headscale service
become : yes
systemd :
name : headscale
enabled : yes
state : started
2025-11-03 16:51:53 +01:00
- name : Wait for headscale unix socket to be ready
become : yes
wait_for :
path : /var/run/headscale/headscale.sock
state : present
timeout : 60
delay : 2
- name : Create headscale namespace if it doesn't exist
become : yes
command : headscale users create {{ headscale_namespace }}
register : create_namespace_result
failed_when : create_namespace_result.rc != 0 and 'already exists' not in create_namespace_result.stderr and 'UNIQUE constraint' not in create_namespace_result.stderr
changed_when : create_namespace_result.rc == 0
2025-10-22 23:58:38 +02:00
- name : Allow HTTPS through UFW
become : yes
ufw :
rule : allow
port : '443'
proto : tcp
- name : Allow HTTP through UFW (for Let's Encrypt)
become : yes
ufw :
rule : allow
port : '80'
proto : tcp
- name : Allow STUN through UFW (for DERP server)
become : yes
ufw :
rule : allow
port : '3478'
proto : udp
- name : Ensure Caddy sites-enabled directory exists
become : yes
file :
path : "{{ caddy_sites_dir }}"
state : directory
owner : root
group : root
mode : '0755'
- name : Ensure Caddyfile includes import directive for sites-enabled
become : yes
lineinfile :
path : /etc/caddy/Caddyfile
line : 'import sites-enabled/*'
insertafter : EOF
state : present
backup : yes
- name : Create Caddy reverse proxy configuration for headscale
become : yes
copy :
dest : "{{ caddy_sites_dir }}/headscale.conf"
content : |
{{ headscale_domain }} {
reverse_proxy localhost:{{ headscale_port }}
}
owner : root
group : root
mode : '0644'
- name : Reload Caddy to apply new config
become : yes
command : systemctl reload caddy
handlers :
- name : Restart headscale
become : yes
systemd :
name : headscale
state : restarted