Publish Headscale via Pangolin
Some checks failed
secret-guardrails / artifact-secret-scan (push) Has been cancelled
secret-guardrails / gitleaks (push) Has been cancelled

This commit is contained in:
Fizzlepoof
2026-05-26 01:50:51 +00:00
parent 9826548fd7
commit 09232f7d70
5 changed files with 295 additions and 11 deletions

View File

@@ -1,7 +1,7 @@
TZ=America/Chicago
# Private DNS names for the pilot. Add matching records in Technitium before device enrollment.
HEADSCALE_SERVER_URL=http://headscale.home.paccoco.com:8084
# Public control-plane hostname for off-LAN clients.
HEADSCALE_SERVER_URL=https://headscale.paccoco.com
HEADPLANE_BASE_URL=http://headplane.home.paccoco.com:3005
# Must be replaced with an actual 32-character secret before deployment.

View File

@@ -21,22 +21,26 @@ Self-hosted Headscale + Headplane pilot stack for replacing the Tailscale free-t
- `/mnt/docker-ssd/docker/appdata/headplane`
- No OIDC on day one
- No subnet-router or exit-node rollout on day one
- No public Pangolin exposure on day one
- Headscale can be published later as a dedicated public control-plane hostname without exposing Headplane the same way
## URLs for the pilot
- Headscale control plane: `http://headscale.home.paccoco.com:8084`
- Headplane UI: `http://headplane.home.paccoco.com:3005/admin`
- Headscale control plane for clients: `https://headscale.paccoco.com`
- Headplane admin UI (restricted/LAN-only): `http://headplane.home.paccoco.com:3005/admin`
These are the intended LAN/private-DNS endpoints for the pilot. If you later want Traefik/Pangolin exposure, use `examples/traefik-routes.yml` as a starting point instead of changing the pilot shape first.
This is the recommended **Option C** shape:
- publish **Headscale** on a stable public HTTPS hostname so phones and laptops can enroll/use it off-LAN
- keep **Headplane** off the public internet by default; treat it as an admin surface reached on LAN/private DNS (or a separately restricted admin path later)
The internal Traefik example in `examples/traefik-routes.yml` is still useful for private-DNS/LAN convenience, but it is not the public control-plane path for off-LAN device enrollment.
### Current live pilot note
The live PD pilot is currently using direct-IP URLs instead:
Before the public hostname is wired, the live PD pilot uses direct-IP URLs:
- Headscale: `http://10.5.30.6:8084`
- Headplane: `http://10.5.30.6:3005/admin`
Reason: the `home.paccoco.com` records for these pilot names have not yet been added in Technitium, and the current Technitium admin flow is 2FA-gated. Once those DNS records exist, flip the live stack back to the hostname-based URLs above.
Once `https://headscale.paccoco.com` is created and verified through Pangolin, off-LAN clients should use that public Headscale URL. Headplane can stay on the restricted LAN/private-DNS path above.
## Initial users
@@ -78,9 +82,15 @@ Before first deploy, replace the placeholder values in:
Required changes:
- replace `CHANGE_ME_TO_EXACTLY_32_CHARS` with a real 32-character cookie secret
- confirm the hostnames/IP-backed DNS records exist in Technitium
- confirm the restricted Headplane LAN/private-DNS record exists if you want the nicer admin hostname
- for off-LAN clients, create/verify the public Pangolin hostname for Headscale
- if you want different ports or hostnames, update both the config files and `.env`
Important live-ops note:
- the repo-tracked `config/headplane/config.yaml` intentionally keeps a placeholder `cookie_secret`
- before restarting the live Headplane container, render or restore the real 32-character secret from the live secret-bearing `.env`
- blindly syncing the tracked placeholder over the live config will make Headplane fail startup with `server.cookie_secret must be exactly length 32`
## Deploy on PD
1. Sync this directory into the live compose tree:
@@ -132,6 +142,26 @@ docker logs headplane --tail=100
11. Register one tagged service node.
12. Enroll one Manndra device.
## Public Headscale via Pangolin
For the off-LAN/mobile path, publish only Headscale itself.
Desired public hostname:
- `https://headscale.paccoco.com`
Desired Pangolin target shape:
- resource name: `headscale`
- site: `Plausible Deniability` (siteId `4`)
- target: `headscale:8080`
- healthcheck path: `/health`
- Pangolin auth/SSO: **disabled** for this route
Why:
- Headscale clients need a plain reachable control-plane endpoint; adding browser SSO in front of it is the wrong shape
- Headplane is an admin UI and should stay restricted/admin-only instead of being published like a normal household app
Use `automation/bin/pangolin_upsert_headscale.py` to create or reconcile the public Pangolin resource.
## Policy reload
After editing `config/headscale/policy.hujson`:

View File

@@ -9,7 +9,7 @@ server:
headscale:
url: "http://headscale:8080"
public_url: "http://headscale.home.paccoco.com:8084"
public_url: "https://headscale.paccoco.com"
config_path: "/etc/headscale/config.yaml"
config_strict: true

View File

@@ -1,4 +1,4 @@
server_url: http://headscale.home.paccoco.com:8084
server_url: https://headscale.paccoco.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443