ViaRoutesForPeer runs for every peer of every map response. Before it
looks at Via, it converts every ACL to grants and resolves each grant's
sources and destinations against the whole node set. Only via grants
can add to the result and ACLs never carry via, so without a via grant
all of that work was discarded. On large tailnets it dominated map
generation. Return early instead.
BenchmarkViaRoutesForPeer, mean per peer (Apple M3 Pro):
policy nodes before after
global 1000 21.8us 9ns, 0 allocs
self 1000 45.0us 9ns, 0 allocs
via 1000 8.1us unchanged
via-mixed 1000 69.0us unchanged
Fixes#3512
Removals derived from a policy change, full update or reconnect rode a
response whose DNSConfig, SSHPolicy, Node or Peers force a full rebuild.
Updates tailscale/tailscale#15660
API keys, pre-auth keys and OAuth clients/tokens share one table and verify
path. Secrets carry 256 bits of crypto/rand entropy, so a SHA-256 digest
needs no stretching; bcrypt/argon2id rows rehash on use until 0.32.
The error only said the user still has nodes, which the CLI prompt did
not mention at all. Wrap ErrUserStillHasNodes with the ID and hostname
of every blocking node so the operator knows what to remove. Run the
DestroyUser test table on Postgres as well as SQLite, since the two
schemas define different foreign-key actions; the Postgres variant
skips without a local server.
resolveSingleUser reported every non-single result as "multiple users
match query", including zero matches. An explicit --identifier 0 was
sent to the API, which treats id=0 as no filter, so it listed every
user and failed as ambiguous. Return a not-found error for zero matches,
list the matching users when several match, and reject a non-positive
identifier before calling the API.
#3472 and #3487 merged without a changelog entry, and the #3409 and
peer map entries landed under 0.30.0 although they ship in 0.29.4.
Sets the release date.
The filter lived in ACL.UnmarshalJSON, so grants, ssh and nodeAttrs still
hit RejectUnknownMembers. Strip the members in the HuJSON AST instead, at
the single decode entrypoint. Grant "app" payloads are left untouched.
Fixes#3479
acmeLogger drained and closed the body of every ACME error response before
handing the response back to golang.org/x/crypto/acme. The client parses that
body to classify errors, so badNonce was no longer recognised: isBadNonce
returned false, clearNonces was never called and Client.post treated the 400 as
non-retriable.
One badNonce reply therefore stops certificate renewal for good. autocert
retries every 30-60 minutes, each attempt reuses a nonce stored from the
previous failed response, that nonce has already expired, and the loop repeats
until the process is restarted or the certificate expires.
Restore the body with a fresh reader after logging it.