A public IP permits exit defaults; private-only rules do not. Replay six
accepted and two refused SaaS captures with range-equivalence checks,
preserving provenance and the literal-default-route ACL validation gap.
Write cleared user columns, discard OIDC email the provider no longer
vouches for, refresh NodeStore users, and notify clients from the callback.
Fixes#3471
The flag moves from the key to the node, so a login with another key no
longer changes it. A user claiming a tagged node still clears it. v1
nodes carry the flag.
Fixes#3640
Reserve extra DNS names, fit collision suffixes within 63 bytes, and repair
duplicate startup names in memory. Persist registration names without
losing a created node on rename failure; remove unused DB renaming.
As on Tailscale SaaS, the node's own entry keeps Hostname, RequestTags,
RoutableIPs and Services; peers get Hostname, OS, Services without
peerapi-dns-proxy, SSH host keys and AppConnector, and resend on those.
Every destination overlaps /0, so any rule offered a peer's exit routes.
They now need a rule to the internet, the test exit-node visibility
uses; via steering defers them to a regular grant only on that test.
Binary-search immutable policy ranges and normalize node routes once.
Large misses and self/via scans improve; small or early matches can slow,
and global policies pay extra normalization and allocation costs.
One verdict per session, so one waiter: the newest. A displaced one
returns at once (register error, SSH re-delegate), not at session end.
A waiter keeps only the follow-up URL and the cached registration.
Fixes ANT-2026-NM50ZP4N
The rest arrives with the first MapRequest. A relogin keeps the node's
current Hostinfo, endpoints and disco key; an oversized merge is a 400.
Fixes ANT-2026-RQS7JAPP
Keep the first 256 endpoints and 4096 routes; refuse longer hostnames,
too many tags, zoned endpoints and >64 KiB NetInfo or other Hostinfo.
The body limit counted wire bytes, not what NodeStore and peers keep.
Fixes ANT-2026-RQS7JAPP
Fixes ANT-2026-5B6KE87K
Fixes ANT-2026-KFCFFM34
handleRegister, the OIDC auth path and SetNodeExpiry refuse it. Expiries
are stored in UTC, which SQLite can read back, and loaded in UTC, as
PostgreSQL returns the server's zone. The v1 API answers 400.
Fixes ANT-2026-QMCAFJ1Q
json.Marshal refuses a year outside [0,9999], so one stored expiry
failed every map with that node. TailNode and key patches send UTC, the
builder drops what fails and the boot scan names the node.
Fixes ANT-2026-QMCAFJ1Q
Only a key that speaks for a tagged node re-authenticates it: a tagged
key, or one of the user it was tagged from. Another user's key or login
adds that user's own device, or only wakes the node under its node key.
Fixes ANT-2026-AWGRB7O1
Such a route names a node, not a subnet: it stays approved and listed
but no viewer gets it. Via inclusions skip the election, so the rule
sits on everything RoutesForPeer returns.
Fixes ANT-2026-MLP8ZR5T
Optional Steps, NodeIDs, error data and login state; v1 files unchanged.
Register log and raw map frame parsers; Write refuses auth keys and
login URLs.
* db: quote postgres connection parameters
The DSN was built by string concatenation, so a password containing a
space, quote or backslash truncated it or injected extra parameters.
Build it with libpq keyword/value quoting and keep the password out of
the startup log line.
* db: mark the DSN test fixture password as not a credential
gosec G101 flags the literal assigned to Pass in TestBuildPostgresDSN.
The value exists to exercise quoting of spaces, quotes and backslashes
and is not a secret.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* types, db: build the postgres DSN as a URL from PostgresConfig
Review feedback: move the DSN construction onto types.PostgresConfig as
DSN(), so the database layer calls cfg.Postgres.DSN() and logs
redacted.DSN(), and build it as a postgres:// URL with net/url instead
of hand-rolled libpq quoting. Unix socket directories travel as the
host query parameter; bare IPv6 literals are bracketed. Tests parse the
URL back and check each component rather than the encoded string.
* types: silence gosec on the whole DSN test fixtures
gosec reports G101 on the composite literal that holds a Pass value, not
on the Pass line itself, so the previous nolint did not cover it. Place
the directive before each fixture element.
* db: drop imports left unused by the merge with main
The merge of main reintroduced the slices and strconv imports in db.go,
but the postgres DSN is now built by PostgresConfig.DSN and nothing in
the file uses them, so the package failed to compile and every CI check
failed on typecheck.
Also link the CHANGELOG entry to the PR.
* db: restore tests and formatting lost in the rebase onto main
Resolving the rebase conflicts in db_test.go kept this branch's side and
dropped the credential tests main had added, including the
requireCredentialConstraints helper another test still calls, so the db
test package no longer compiled. Restore the file from main; this branch
does not change it.
The resolution also indented the package clause in db.go and removed the
blank line between the standard library and module imports.
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Adds DNS-01 for headscale's own certificate through dns.challenge.provider,
with the same propagation check as node certificates. Storage format
changes: first start gets a new account and certificate.
Updates #2527
Updates #504
tls.cert_path, tls.key_path and tls.acme.* replace the flat keys, which
still work. Warn on them only when they turn TLS on: NixOS always sets them.
Updates #2527
_acme-challenge is CNAMEd once to an acme-dns registration, so headscale
needs no DNS credentials. Node certificates need one registration per
name; CNAME chains are followed and writes never evict a live value.
Updates #2137
A node may publish only its own _acme-challenge TXT value; any other
request is rejected before the provider is called. Answers once all
authoritative nameservers serve the value; 503 while shutting down.
Fixes#2137Fixes#1921Fixes#2527
Both gate tailscale cert and serve HTTPS on the client; httpsEnabled
follows, as in Tailscale. No certificate when an operator extra record
sits at or below the name: a wildcard would cover it.
Updates #2137
Updates #1921
dns.challenge.provider builds a libdns provider at load, so configtest
catches typos. dns.certificates gates node certs; zone defaults to base_domain.
Updates #2137