[009] Add Headscale stack behind Caddy at remote.waldson.com.br
Headscale (the self-hosted Tailscale control plane) runs as a new stack on proxy_net, following the house pattern of syncthing/forgejo/ntfy: own directory, own compose file, committed config, git-ignored bind-mounted state, CLI-only admin via docker exec. - headscale/config.yaml: server_url https://remote.waldson.com.br, MagicDNS with base domain ts.waldson.com.br, embedded DERP relay with the upstream Tailscale DERP map dropped so relayed traffic stays on the droplet, HA subnet-router route failover, SQLite state under /var/lib/headscale. The CLI unix socket is relocated into the data dir so the UID 1000 container can use it. - headscale/compose.yaml: image pinned to the v0.29 minor, runs as 1000:1000, publishes only STUN on 3478/udp; gRPC not exposed. - Caddyfile: remote.waldson.com.br reverse-proxies to headscale-headscale-1:8080. - README: Headscale stack section (deploy, upgrade, user/node CLI runbook), firewall table row for 3478/udp, DNS row for remote.waldson.com.br.
This commit is contained in:
parent
04df6b9c09
commit
6da5b3a8ab
4 changed files with 320 additions and 5 deletions
125
README.md
125
README.md
|
|
@ -24,6 +24,8 @@ forgejo/ the Forgejo stack (own compose file); web UI behind Caddy,
|
|||
git-over-SSH published on 2222
|
||||
ntfy/ the ntfy stack (own compose file); push server behind Caddy,
|
||||
nothing published (pure HTTP through the proxy)
|
||||
headscale/ the Headscale stack (own compose file); control plane behind
|
||||
Caddy, STUN for the embedded DERP relay published on 3478/udp
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
|
@ -312,6 +314,113 @@ docker compose pull && docker compose up -d
|
|||
|
||||
A major bump is a deliberate tag edit; read the ntfy release notes first.
|
||||
|
||||
## Headscale stack
|
||||
|
||||
A self-hosted [Headscale](https://headscale.net) coordination server (the
|
||||
open-source Tailscale control plane) for the owner's mesh VPN (Virtual Private
|
||||
Network) at `remote.waldson.com.br`. It lives in `headscale/` and deploys to
|
||||
`/home/waldson/central-proxy/headscale` on the droplet. Every device (mercurio,
|
||||
hermes, quicksilver, the phone, the Home Assistant box) joins as a stock
|
||||
Tailscale client pointed at this login URL, so remote access to the home LAN
|
||||
never depends on Tailscale Inc.'s servers.
|
||||
|
||||
The control plane and login endpoints ride Caddy's existing 80/443: the central
|
||||
`Caddyfile` carries the `remote.waldson.com.br` block reverse-proxying to
|
||||
`headscale-headscale-1:8080`, and Caddy transparently upgrades the long-lived
|
||||
client control connections (the same handling ntfy relies on). The one thing
|
||||
published on the host is **STUN on `3478/udp`** for the embedded DERP
|
||||
(Designated Encrypted Relay for Packets) server; the relay itself rides Caddy's
|
||||
443. The whole posture (server URL, MagicDNS, embedded DERP) is a committed
|
||||
`headscale/config.yaml`, so it is reproduced on every recreate rather than
|
||||
depending on runtime state.
|
||||
|
||||
The embedded DERP relay replaces Tailscale Inc.'s DERP map entirely
|
||||
(`derp.urls: []`), so even traffic that cannot go peer-to-peer is relayed
|
||||
through this droplet. MagicDNS is enabled with base domain `ts.waldson.com.br`
|
||||
(deliberately different from the login hostname, which Headscale requires, and
|
||||
never published to public DNS), so devices reach each other by name
|
||||
(`ssh mercurio`). The gRPC remote-CLI port is not exposed; administration is
|
||||
CLI-only via `docker exec ... headscale`, like every other stack.
|
||||
|
||||
### Where data lives
|
||||
|
||||
The SQLite database (`db.sqlite`), the auto-generated Noise and DERP private
|
||||
keys, and the CLI unix socket all live under the host bind mount `headscale/data`
|
||||
(mounted at `/var/lib/headscale` in the container), so state - the node list and
|
||||
keys - survives container recreation and is inspectable over plain SSH. The
|
||||
directory is git-ignored via the repo's `data/` rule. The container runs as
|
||||
`1000:1000`; if the droplet's deploy user has a different id, adjust `user:` in
|
||||
`compose.yaml` so the state files stay owned by that user. Headscale's default
|
||||
unix socket lives under root-only `/var/run`, so `config.yaml` relocates it into
|
||||
this data dir to keep the non-root container's CLI working.
|
||||
|
||||
### Deploy
|
||||
|
||||
From `headscale/` on the droplet:
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The container comes up as `headscale-headscale-1` on `proxy_net`, which is the
|
||||
name Caddy reverse-proxies to. Recreating it preserves the node list and keys:
|
||||
|
||||
```sh
|
||||
docker compose up -d --force-recreate
|
||||
```
|
||||
|
||||
### First-run setup (CLI)
|
||||
|
||||
There is no web sign-up. All devices register under a single Headscale user,
|
||||
created once via the CLI after the container is first up:
|
||||
|
||||
```sh
|
||||
docker exec headscale-headscale-1 headscale users create waldson
|
||||
docker exec headscale-headscale-1 headscale users list
|
||||
```
|
||||
|
||||
### Enrolling a device
|
||||
|
||||
Registration is manual one-time approval per device (no pre-shared keys). On the
|
||||
client, point Tailscale at this login server:
|
||||
|
||||
```sh
|
||||
tailscale up --login-server https://remote.waldson.com.br
|
||||
```
|
||||
|
||||
The client prints a registration URL ending in an auth id; approve the node
|
||||
under the user on the droplet with that id:
|
||||
|
||||
```sh
|
||||
docker exec headscale-headscale-1 \
|
||||
headscale auth register --user waldson --auth-id <auth-id-from-client-url>
|
||||
docker exec headscale-headscale-1 headscale nodes list
|
||||
```
|
||||
|
||||
Subnet routes advertised by a node (e.g. the Home Assistant box and mercurio
|
||||
advertising the home LAN) are approved per node:
|
||||
|
||||
```sh
|
||||
docker exec headscale-headscale-1 headscale nodes approve-routes --identifier <id> --routes <cidr>
|
||||
```
|
||||
|
||||
### Upgrade
|
||||
|
||||
The image is pinned to the current Headscale minor (`headscale/headscale:v0.29`).
|
||||
Because Headscale is pre-1.0, the minor is the breaking-change axis; patch
|
||||
updates within it are safe:
|
||||
|
||||
```sh
|
||||
docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
A minor bump (editing the tag to `v0.30`) can change the config schema and may
|
||||
run database migrations: diff `config.yaml` against that release's
|
||||
`config-example.yaml`, back up `headscale/data` first, and read the release
|
||||
notes. Headscale also trails the newest Tailscale client capabilities, so review
|
||||
client compatibility when bumping - if a client refuses to connect after an
|
||||
upgrade, version skew is the first suspect.
|
||||
|
||||
## Droplet groundwork
|
||||
|
||||
Baseline host configuration for the droplet (`wloud`, 174.138.36.232,
|
||||
|
|
@ -352,15 +461,19 @@ firewall blocks them. Ports in use:
|
|||
| 80, 443 | tcp | Caddy (all HTTP/HTTPS traffic) |
|
||||
| 2222 | tcp | Forgejo git-over-SSH |
|
||||
| 22000 | tcp+udp | Syncthing sync protocol |
|
||||
| 3478 | udp | Headscale STUN (embedded DERP) |
|
||||
|
||||
ntfy adds no row here: it is reached only through Caddy on 80/443. If ufw
|
||||
rules are ever tightened, keep the four rows above allowed. To
|
||||
re-verify reachability from any machine outside the droplet:
|
||||
ntfy adds no row here: it is reached only through Caddy on 80/443. Headscale is
|
||||
also fronted by Caddy on 80/443, but its embedded DERP relay needs STUN on
|
||||
3478/udp for NAT traversal, which is the one row it adds. If ufw rules are ever
|
||||
tightened, keep the rows above allowed. To re-verify reachability from any
|
||||
machine outside the droplet:
|
||||
|
||||
```sh
|
||||
nc -vz 174.138.36.232 2222
|
||||
nc -vz 174.138.36.232 22000
|
||||
nc -vzu 174.138.36.232 22000
|
||||
nc -vzu 174.138.36.232 3478
|
||||
```
|
||||
|
||||
"Connection refused" means the port is reachable but nothing listens yet;
|
||||
|
|
@ -378,12 +491,14 @@ and point at the droplet:
|
|||
| git.waldson.com.br | A | 174.138.36.232 | Forgejo |
|
||||
| sync.waldson.com.br| A | 174.138.36.232 | Syncthing GUI |
|
||||
| push.waldson.com.br| A | 174.138.36.232 | ntfy push server |
|
||||
| remote.waldson.com.br | A | 174.138.36.232 | Headscale control plane |
|
||||
|
||||
personaledson.com.br and nazareonline.com.br also resolve here for the
|
||||
existing stacks. Check resolution with:
|
||||
existing stacks. `ts.waldson.com.br` (the MagicDNS base domain) deliberately has
|
||||
**no** public record: it exists only inside the tailnet. Check resolution with:
|
||||
|
||||
```sh
|
||||
dig +short waldson.com.br www.waldson.com.br git.waldson.com.br sync.waldson.com.br
|
||||
dig +short waldson.com.br www.waldson.com.br git.waldson.com.br sync.waldson.com.br remote.waldson.com.br
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue