Skip to content

Commit 575d8ec

Browse files
committed
changelog: normalise 0.29.0 BREAKING and Changes sections
Move HA subnet router health probing above BREAKING so the layout matches every other release. Drop **User deletion**: / **Node Expiry**: bold prefixes redundant with the #### subgrouping. Fill missing PR refs: #3202 (hostname rewrite), #3263 (sshTests + SSH rule validation), #3194 (HA probe), #3251 (randomize_client_port removal), #3268 (trusted_proxies).
1 parent e4e742c commit 575d8ec

1 file changed

Lines changed: 28 additions & 41 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@ policy and reload.
6161

6262
This feature is **beta** while behavioural coverage against Tailscale SaaS broadens.
6363

64+
[#3263](https://github.com/juanfont/headscale/pull/3263)
65+
6466
### SSH rule validation
6567

6668
SSH rule parsing now trims surrounding whitespace on `action`, `users`, `src`, and `dst`,
6769
rejects empty or wildcard entries in `users`, rejects empty `acceptEnv`, and rejects negative
6870
`checkPeriod`. `hosts:` aliases are rejected as SSH destinations, non-ASCII tag names are
6971
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
72+
[#3263](https://github.com/juanfont/headscale/pull/3263)
7073

7174
### Grants
7275

@@ -77,9 +80,9 @@ field steers traffic through specific tagged subnet routers or exit nodes. The `
7780
an ACL rule. Grants can be mixed with ACLs in the same policy file.
7881
[#2180](https://github.com/juanfont/headscale/pull/2180)
7982

80-
As part of this, we added `autogroup:danger-all`. It resolves to `0.0.0.0/0` and `::/0` all IP
83+
As part of this, we added `autogroup:danger-all`. It resolves to `0.0.0.0/0` and `::/0`, all IP
8184
addresses, including those outside the tailnet. This replaces the old behaviour where `*` matched
82-
all IPs (see BREAKING below). The name is intentionally scary: accepting traffic from the entire
85+
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
8386
internet is a security-sensitive choice. `autogroup:danger-all` can only be used as a source.
8487

8588
### Node attributes (`nodeAttrs`)
@@ -103,7 +106,7 @@ Frequently requested capabilities this unlocks include `magicdns-aaaa`,
103106
`disable-relay-server`, `disable-captive-portal-detection`,
104107
`nextdns:<profile>` / `nextdns:no-device-info`, `randomize-client-port`,
105108
and the Taildrive `drive:share` / `drive:access` pair. The set is not
106-
limited to these any string-only cap an operator places in policy
109+
limited to these, any string-only cap an operator places in policy
107110
reaches clients unchanged.
108111

109112
`randomizeClientPort` also lands as a top-level policy field that toggles
@@ -150,28 +153,9 @@ mode:
150153
A wildcard `nodeAttrs` (`"target": ["*"]`) hands the caps to every
151154
node when fine-grained control is not needed.
152155

153-
### Hostname handling (cleanroom rewrite)
154-
155-
The hostname ingest pipeline has been rewritten to match Tailscale SaaS byte-for-byte.
156-
Headscale previously had three overlapping regexes and two disagreeing entry points
157-
(registration vs map-request update), which caused a recurring class of bugs: names
158-
containing apostrophes, spaces, dots, or non-ASCII characters were alternately rejected
159-
(dropping updates with log spam) or stored as `invalid-<rand>` surrogates
160-
([#3188](https://github.com/juanfont/headscale/issues/3188),
161-
[#2926](https://github.com/juanfont/headscale/issues/2926),
162-
[#2343](https://github.com/juanfont/headscale/issues/2343),
163-
[#2762](https://github.com/juanfont/headscale/issues/2762),
164-
[#2177](https://github.com/juanfont/headscale/issues/2177),
165-
[#2121](https://github.com/juanfont/headscale/issues/2121),
166-
[#2449](https://github.com/juanfont/headscale/issues/2449),
167-
[#363](https://github.com/juanfont/headscale/issues/363)).
168-
169-
What changed:
170-
171-
- Sanitisation and validation now come directly from
172-
`tailscale.com/util/dnsname.SanitizeHostname` / `ValidLabel`.
173-
- Admin rename (`headscale nodes rename`) now validates via `dnsname.ValidLabel` and
174-
rejects labels already held by another node (previously coerced invalid input silently).
156+
### Hostname sanitisation
157+
158+
Hostnames are now santised using Tailscales `magicdns` sanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.
175159

176160
Examples that previously regressed and now work:
177161

@@ -184,11 +168,24 @@ Examples that previously regressed and now work:
184168
| `My-PC!` | `My-PC!` | `my-pc` |
185169
| `我的电脑` | `我的电脑` | `node` |
186170

171+
[#3202](https://github.com/juanfont/headscale/pull/3202)
172+
173+
### HA subnet router health probing
174+
175+
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
176+
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
177+
control channel and fails over to a healthy standby if the primary stops responding. This is
178+
enabled by default (`node.routes.ha.probe_interval: 10s`, `probe_timeout: 5s`) and only
179+
active when HA routes exist (2+ nodes advertising the same prefix). Set `probe_interval` to
180+
`0` to disable. This complements the existing disconnect-based failover, catching "zombie
181+
connected" routers that maintain their control session but cannot route packets.
182+
[#3194](https://github.com/juanfont/headscale/pull/3194)
183+
187184
### BREAKING
188185

189186
#### Hostname handling
190187

191-
- The `GivenName` collision policy changed from an 8-char random hash suffix (`laptop-abc12xyz`) to a monotonic numeric suffix (`laptop`, `laptop-1`, `laptop-2`, …), matching Tailscale SaaS. Empty / all-non-ASCII hostnames now fall back to the literal `node` instead of `invalid-<rand>`. MagicDNS names change on upgrade for any node whose previous label was a random-suffix form; the raw `Hostname` column is unchanged.
188+
- The `GivenName` collision policy changed from an 8-char random hash suffix (`laptop-abc12xyz`) to a monotonic numeric suffix (`laptop`, `laptop-1`, `laptop-2`, …), matching Tailscale SaaS. Empty / all-non-ASCII hostnames now fall back to the literal `node` instead of `invalid-<rand>`. MagicDNS names change on upgrade for any node whose previous label was a random-suffix form; the raw `Hostname` column is unchanged. [#3202](https://github.com/juanfont/headscale/pull/3202)
192189

193190
#### ACL Policy
194191

@@ -214,7 +211,7 @@ Examples that previously regressed and now work:
214211

215212
- The `randomize_client_port` server-config key was removed; the
216213
toggle now lives in the policy file as a top-level
217-
`randomizeClientPort` field, matching the Tailscale-hosted schema.
214+
`randomizeClientPort` field, matching the Tailscale-hosted schema. [#3251](https://github.com/juanfont/headscale/pull/3251)
218215
Headscale refuses to start when the old key is set. Move it to the
219216
policy file referenced by `policy.path`:
220217

@@ -236,16 +233,6 @@ Examples that previously regressed and now work:
236233
- `headscale nodes register` is deprecated in favour of `headscale auth register --auth-id <id> --user <user>` [#1850](https://github.com/juanfont/headscale/pull/1850)
237234
- The old command continues to work but will be removed in a future release
238235

239-
### HA subnet router health probing
240-
241-
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
242-
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
243-
control channel and fails over to a healthy standby if the primary stops responding. This is
244-
enabled by default (`node.routes.ha.probe_interval: 10s`, `probe_timeout: 5s`) and only
245-
active when HA routes exist (2+ nodes advertising the same prefix). Set `probe_interval` to
246-
`0` to disable. This complements the existing disconnect-based failover, catching "zombie
247-
connected" routers that maintain their control session but cannot route packets.
248-
249236
### Changes
250237

251238
#### ACL Policy
@@ -286,7 +273,7 @@ connected" routers that maintain their control session but cannot route packets.
286273
- `headscale policy check --bypass-grpc-and-access-database-directly` validates `user@` tokens against the live user database [#3160](https://github.com/juanfont/headscale/issues/3160)
287274
- Remove deprecated `--namespace` flag from `nodes list`, `nodes register`, and `debug create-node` commands (use `--user` instead) [#3093](https://github.com/juanfont/headscale/pull/3093)
288275
- Remove deprecated `namespace`/`ns` command aliases for `users` and `machine`/`machines` aliases for `nodes` [#3093](https://github.com/juanfont/headscale/pull/3093)
289-
- **User deletion**: Fix `DestroyUser` deleting all pre-auth keys in the database instead of only the target user's keys [#3155](https://github.com/juanfont/headscale/pull/3155)
276+
- Fix `DestroyUser` deleting all pre-auth keys in the database instead of only the target user's keys [#3155](https://github.com/juanfont/headscale/pull/3155)
290277
- `headscale policy check` evaluates the `tests` block when invoked with `--bypass-grpc-and-access-database-directly`; without the flag it warns instead of running the tests against empty data [#1803](https://github.com/juanfont/headscale/issues/1803)
291278

292279
#### API
@@ -306,7 +293,7 @@ connected" routers that maintain their control session but cannot route packets.
306293
- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
307294
- `oidc.expiry` has been removed; use `node.expiry` instead (applies to all registration methods including OIDC)
308295
- `ephemeral_node_inactivity_timeout` is deprecated in favour of `node.ephemeral.inactivity_timeout`
309-
- Add `trusted_proxies` to gate `True-Client-IP` / `X-Real-IP` / `X-Forwarded-For` (previously honoured from any client)
296+
- Add `trusted_proxies` to gate `True-Client-IP` / `X-Real-IP` / `X-Forwarded-For` (previously honoured from any client) [#3268](https://github.com/juanfont/headscale/pull/3268)
310297

311298
#### Debug
312299

@@ -318,9 +305,9 @@ connected" routers that maintain their control session but cannot route packets.
318305

319306
- Remove old migrations for the debian package [#3185](https://github.com/juanfont/headscale/pull/3185)
320307
- Install `config-example.yaml` as example for the debian package [#3186](https://github.com/juanfont/headscale/pull/3186)
321-
- **Node Expiry**: Fix user owned re registration with zero client expiry and no default storing `0001-01-01 00:00:00` in the database instead of NULL [#3199](https://github.com/juanfont/headscale/pull/3199)
308+
- Fix user-owned re-registration with zero client expiry and no default storing `0001-01-01 00:00:00` in the database instead of `NULL` [#3199](https://github.com/juanfont/headscale/pull/3199)
322309
- Pre-existing rows with `0001-01-01 00:00:00` are not backfilled; they clear themselves the next time the node re-registers
323-
- **Node Expiry**: Fix tailscaled restart on a node with no expiry resetting `NULL` to `0001-01-01 00:00:00` in the database, affecting both tagged and untagged nodes [#3197](https://github.com/juanfont/headscale/pull/3197)
310+
- Fix `tailscaled` restart on a node with no expiry resetting `NULL` to `0001-01-01 00:00:00` in the database, affecting both tagged and untagged nodes [#3197](https://github.com/juanfont/headscale/pull/3197)
324311

325312
## 0.28.0 (2026-02-04)
326313

0 commit comments

Comments
 (0)