Commit Graph
11054 Commits
Author SHA1 Message Date
Brad FitzpatrickandBrad Fitzpatrick b93d9ba1ff cmd/containerboot: return context error when canceled during tailscale up/set
TestContainerBoot/kube_shutdown_during_state_write flaked with exit
code 1 instead of 0 when SIGTERM arrived while "tailscale up" was
still running. Two problems combined:

tailscaleUp and tailscaleSet wrapped errors with %v, flattening the
error chain, so main's errors.Is(err, context.Canceled) check could
not recognize a graceful shutdown.

Even with %w, cmd.Run under a canceled context usually reports the
death of the killed subprocess ("signal: killed") rather than the
context error that caused it, since Wait prefers the process error.

Check ctx.Err() explicitly and return it (wrapped with %w) so that
a shutdown-driven cancellation is recognized wherever it lands
relative to the subprocess lifetime.

Before: the exit-code failure reproduced 4 times in 808 stress runs
under CPU starvation. After: 0 in 1195 runs.

Fixes #19380

Change-Id: Ie15ca722d2d5ac2a3f79b2d0ab01fb71d4b9220d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-07-27 05:42:29 -07:00
chaosinthecrd 97a75c837d cmd/k8s-operator,ipn/store/kubestore,kube/kubetypes: share ACME account key per tailnet
Introduce a per-tailnet shared ACME account key so that all ingress
ProxyGroup replicas on a tailnet present the same account identity to
Let's Encrypt. This lets renewals claim the ARI "replaces" exemption
from the 50-certs-per-week rate limit, surviving Pod restarts,
ProxyGroup recreation, and cluster migrations.

The operator provisions a "tailscale-acme-accounts" Secret in its
namespace, guarded by a finalizer and a deletion warning event, and
watched so it is recreated promptly if removed. Proxies migrate any
pre-existing per-pod key into the shared Secret on first boot, adopt
the shared key on subsequent boots, and restore it on cert writes if
the Secret was recreated empty. Certs are stamped with the fingerprint
of the issuing account so renewals skip the "replaces" claim when the
account doesn't match.

Opt-in per-ProxyGroup via the tailscale.com/share-acme-account
annotation, or operator-wide via OPERATOR_SHARED_ACME_ACCOUNT_KEY.

Updates #18251
Updates #20288

Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
2026-07-27 12:06:41 +01:00
Alex ChanandAlex Chan 2900f3494a types/persist: add a comment explaining "NetworkLockKey"
Changing the name to "TailnetLockKey" would be clearer but introduces
more risk; this is an easy and low-stakes improvement.

Updates tailscale/corp#37904

Change-Id: I38d804202538b8670a80e744eb4dcb689f0002df
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-07-27 09:55:36 +01:00
Will NorrisandWill Norris cd34d441be cmd/tailscale: add tailnet info to whoami output
Updates #14375

Change-Id: Ia101a4a3005adb9118051b3416f5a64a4a45987d
Signed-off-by: Will Norris <will@tailscale.com>
2026-07-24 11:39:53 -07:00
Alex ChanandAlex Chan aec6f8bf13 tka/sync: improve the signature of SeedAUMs
I wrote this function two hours ago, tried to use it in corp, and
immediately found myself confused about the meaning of the arguments.
Time for named parameters!

Updates tailscale/corp#40404

Change-Id: Ic2866e052ccc9f6361b8d529233df54d63abbaa1
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-07-24 19:16:46 +01:00
Alex ChanandAlex Chan b5fb042501 tka/sync: add regression test for compacted nodes on forked chains
We previously identified sync failures that occur when a node falls behind
the remote, and compacts away most its local state. We fixed the underlying
issue in #19444, but that PR only tested the basic scenario where the
local chain is a direct ancestor of the remote chain.

This patch adds an explicit regression test for the case where a node is on
a fork (that is, its HEAD is not part of the remote's active chain).

Although #19444 happened to cover this case, other proposed patches did not
handle the forked state. Adding this test locks in the behaviour and prevents
future sync regressions in this area.

Also, add a shared helper for writing this sort of TKA sync test.

Updates tailscale/corp#40404

Change-Id: I78fdc6beaf71392edf11806197f126db48886f93
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-07-24 16:42:42 +01:00
Brad FitzpatrickandBrad Fitzpatrick 8c98d2a417 misc/git_hook: reject pushes that add large files
Add a large blob check to the pre-push hook, using the same git tree
diff logic as corp's check-file-size CI workflow (the
check-git-accidental-large-file GitHub Action): diff the pushed tree
against the remote's old tree (or the merge base with the remote's
default branch for new refs) and reject any new or changed blob over
1.5 MB. Unlike the CI check, which only guards PRs into main, the hook
runs before pushing to any branch, catching mistakes before they
permanently bloat the remote repo.

Set TS_SKIP_LARGE_FILE_CHECK=1 to push a large file intentionally,
mirroring the skip-large-file-check commit message tag honored by CI.

This folds the go.mod replace check and the new check into a single
CheckPrePush entry point so both share one read of the hook's stdin;
corp's git-hook.go needs the matching call site update when it next
bumps its tailscale.com dependency.

Updates tailscale/corp#9863

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I1c8cf2a277ce854d45c0ea809bed7c06b3295374
2026-07-24 08:22:19 -07:00
Michael Ben-Amiandmzbenami b062abb1ea appc,ipn/ipnlocal: install conn25 DNS routes when using exit node
We were early-returning when the node was using an exit node, before
Connectors 2025 split DNS routes were calculated and installed.

Now we assemble the routes first, then install them in both exit node
and non-exit-node contexts. The returned resolvers set UseWithExitNode
to true even though as of today, we believe they should be installed in
all cases without regard to that boolean value. With the boolean, we
preserve the flexibility to toggle behavior without touching ipnlocal.

We also add a TODO to turn the extra split DNS route gathering into a
feature hook (tailscale/corp#37125).

This does not affect appc connectors, which receive split DNS routes,
and the UseWithExitNode value directly from control.

Updates #16384

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
2026-07-24 09:39:40 -04:00
David BondandGitHub 9535e3b99b cmd/k8s-operator: include peer relay CRD in generation (#20593)
This commit modifies the generation command for the kubernetes operator
to include the CRD for peer relays in the helm chart and static
manifests

Updates: #fixup

Signed-off-by: David Bond <davidsbond93@gmail.com>
2026-07-24 14:01:41 +01:00
Alex ChanandAlex Chan f6fa294635 tka/sync: send checkpoints to ensure far-behind nodes can catch up
Previously there was a mismatch between how nodes store AUMs and what
the control plane would offer during sync:

- Client compaction: Nodes aggressively compact their TKA state -- they
  keep the last 24 AUMs, every AUM received in the last two weeks, and
  then everything from there back to the last checkpoint. Depending on
  when it compacts, a node may only have ~50 AUMs.
- Exponential sampling: To save bandwidth, the control plane would send
  a SyncOffer containing ancestors at exponentially increasing intervals
  (4th, 16th, 64th, 256th...).

If a node has been offline for too long, the exponential sampling skips
the node's smaller window. When the SyncOffer and local state are disjoint,
the node cannot find a common ancestor to use for synchronisation.
It enters a failure loop where it keeps polling for new TKA state, but
it cannot catch up and has an increasingly-outdated view of the tailnet.

This patch replaces the exponential sampling with a SyncOffer that sends
every checkpoint ancestor of the current HEAD. Since every node is
guaranteed to keep at least one checkpoint after compaction, we're more
likely to have an intersection for the sync process.

This patch also increases `maxSyncHeadIntersectionIter`, which in
practice means the control plane will send every checkpoint in the
current chain. This means all affected nodes will be able to find an
intersection and catch up immediately, without requiring a client update.

It's still possible for a node to be unable to sync, but these edge cases
become less likely with this change. (For example, if a node is 1000+ AUMs
behind, or if it creates a local branch and then compacts away the
intersection with the main chain.)

This patch includes a regression test with synthetic data, and I
verified the fix with customer data.

Updates https://github.com/tailscale/corp/issues/40404

Change-Id: I2174011bb23a2b5972f6d1591aadcc016e3cba35
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-07-24 09:01:38 +01:00
Fran Bull 1d2baa12fe tailcfg: bump tailcfg.CapabilityVersion for conn25
We have some client builds on the unstable track where the conn25 code
doesn't run if the TAILSCALE_USE_WIP_CODE env var is not set. But the
split DNS routes for conn25 configured domains do get installed. This
means that users running those builds would get traffic for configured
domains black holed if the env var is not set.

This issue was fixed in 425a916ce.

Bump tailcfg.CapabilityVersion, and then a corresponding change to the
control server to not send conn25 config to lower versions will
avoid this issue for those users.

Updates tailscale/corp#45363

Signed-off-by: Fran Bull <fran@tailscale.com>
2026-07-23 14:30:22 -07:00
Tom ProctorandGitHub 682005aaa6 cmd/cigocacher,go.mod: add logging for canceled PUTs (#20584)
Pulls in bradfitz/go-tool-cache#43 and:

* Add logging for canceled PUTs to ensure we have some visibility.
* Scale PUT timeouts with object size.
* Control the Shutdown timeout separately from the PUT timeout.

Updates tailscale/corp#45334

Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
2026-07-23 20:15:41 +01:00
Nick O'NeillandGitHub ac33a4cc1a VERSION.txt: this is v1.103.0 (#20583)
Signed-off-by: Nick O'Neill <nick@tailscale.com>
2026-07-23 12:06:08 -07:00
Rollie MaandGitHub f4978b4b9b feature,client: add serviceclientprefs for desktop client service launch (#20501)
Add serviceclientprefs, an optional feature that stores and loads the
desktop clients' saved service launch preferences, one file per login
profile.

- Add GET|POST /localapi/v0/prefs/service-clients to load and save the
  current profile's service client prefs.
- Add local client GetServiceClientPrefs and SetServiceClientPref that
  call the new local api endpoint.
- Store the prefs with the ipn/store FileStore at
  TailscaleVarRoot()/profile-data/<profileID>/service-client-prefs/<hex-encoded-key>,
  so DeleteProfile cleans them up for free. Fall back to an in-memory
  store when there's no var root.
- Register the feature and its local api route from build tagged files
  so the whole thing drops out under ts_omit_serviceclientprefs.
- Add the serviceclient package holding Pref and Prefs (saved client,
  username, database name, and last used time), so the local api client
  and desktop apps can import the types without the feature machinery.

Change-Id: I340a99c1b332d181fb1556fbf3e8003bb3b95a08
Updates: https://github.com/tailscale/tailscale/issues/20429

Signed-off-by: Rollie Ma <rollie@tailscale.com>
2026-07-22 20:36:05 -07:00
Tom ProctorandGitHub 66bb4ac61f go.mod,cmd/cigocacher: make PUTs async (#20578)
Pull in bradfitz/go-tool-cache#40, and configure PUTs to be async so we
never slow down the build or extend the build time for the sake of
writing to the remote cache.

Updates tailscale/corp#45334

Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
2026-07-22 22:01:39 +01:00
Brad FitzpatrickandBrad Fitzpatrick b3c259bd5c net/netutil: add test coverage for ipForwardingEnabledLinux per-interface reads
The existing test only exercised the not-found-interface path. Now that
ipForwardingEnabledLinux opens its sysctl key with os.OpenInRoot
(840c6e3d3, #20572), also verify that the global keys and the
per-interface keys for every interface actually present on the machine
can be read without error, for both IPv4 and IPv6.

Updates #20572

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ie204a163ab9f8670abedd79a4ac81e400f71aab7
2026-07-22 13:33:20 -07:00
basavaraj-sm05andBrad Fitzpatrick 840c6e3d3d net/netutil: confine ipForwardingEnabledLinux read with os.OpenInRoot
Signed-off-by: basavaraj-sm05 <basavaraj@digiscrypt.com>
2026-07-22 12:36:27 -07:00
Brad FitzpatrickandBrad Fitzpatrick 5384d23690 cmd/derper: add opt-in support for LetsEncrypt IP address certificates
LetsEncrypt made certificates for bare IP addresses generally
available in January 2026. They require the short-lived ACME
certificate profile and are valid for about six days.

Add a new --acme-ip-certs flag. When set (with the default
--certmode=letsencrypt), connections that arrive by IP address (no
TLS SNI, or an IP address SNI matching the connection's destination
address) get a LetsEncrypt cert for that IP, obtained on demand using
the "shortlived" profile and the HTTP-01 challenge served on derper's
plaintext HTTP port. Because the certificate is requested for
whatever address the connection actually arrived on, it works for
both IPv4 and IPv6 with no per-address configuration, and a client
can never make us request a certificate for an address that isn't
ours. Connections with a DNS name in the SNI keep using the regular
autocert manager for --hostname.

autocert can't do any of this itself, as it neither orders IP address
identifiers nor serves connections without SNI, so this adds a small
dedicated cert manager using tailscale.com/tempfork/acme instead.

Clients can then connect to https://<IP> without the DERPMap CertName
pinning that self-signed certs from --certmode=manual require.

Updates tailscale/corp#45167
Updates #11776

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I8e2d5b0a7c4f9e1b3d6a8c2f5e0b9d4a7c1f3e6d
2026-07-22 12:33:42 -07:00
Brad FitzpatrickandBrad Fitzpatrick fdd81c68b3 go.mod: bump some deps to match corp
Updates tailscale/corp#43243
Updates tailscale/corp#45354

Change-Id: I810a8107641f16619c2036b3c8bd0d7293d1943c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-07-22 11:00:06 -07:00
SalehandBrad Fitzpatrick 1a14668a31 feature/acme: trim trailing dot from domain before cert lookup
An SNI ServerName with a trailing dot (e.g. "host.ts.net.") failed
cert lookup because stored cert names have no trailing dot. Per RFC
6066 section 3 the SNI HostName carries no trailing dot, but some
clients send a fully-qualified name with one.

Trim the trailing dot at the boundary in getCertPEMWithValidity so all
lookup paths (the GetCertificate hook, Serve, and the localapi) resolve
the dotted and dotless forms to the same certificate.

Fixes #10233

Signed-off-by: Saleh <root@lr0.org>
2026-07-22 10:52:40 -07:00
Michael Ben-Amiandmzbenami 4d846b5501 feature/conn25: add and register c2n handler for /conn25/state
Gets the active state just like the LocalAPI endpoint does. See #20471.

Updates tailscale/corp#40125

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
2026-07-22 13:04:41 -04:00
Michael Ben-Amiandmzbenami 8f89d4fb55 feature/conn25: rename LocalAPI endpoint to conn25/state
Previously it was conn25-state. The new name prepares for the ability to
add new endpoints behind the conn25/ prefix, and prepares for parity for
an upcoming c2n endpoint with the same name.

Updates tailscale/corp#40125

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
2026-07-22 13:04:41 -04:00
Michael Ben-Amiandmzbenami 2820b5e99d feature/conn25: rename localapi.go to api.go
And rename serveStateGet to serveLocalAPIStateGet to prepare for adding
a c2n handler that is backed by the same methods as the LocalAPI
handler.

Updates tailscale/corp#40125

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
2026-07-22 13:04:41 -04:00
Kristoffer DalbyandKristoffer Dalby c802c3ff05 go.mod: revert tailscale/breakglass fork require
Revert the direct fork dependency and its regenerated depaware/flake
manifests; not ready to ship yet.

This reverts commit 745bb8507.

Updates #1866

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-07-22 16:39:24 +02:00
Kristoffer DalbyandKristoffer Dalby a19f8f290e gokrazy/tsapp: revert breakglass access lockdown
Revert the tailscale/breakglass fork and its access-control flags;
not ready to ship yet.

This reverts commit 1d82c1b3d.

Updates #1866

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-07-22 16:39:24 +02:00
Mike JensenandGitHub 0eb38dc2e5 ipn,magicsock: deny peer capabilities to unsigned peers (#20561)
Unsigned peers aren't covered by tailnet lock, so they must never hold peer capabilities even if the packet filter grants them. This change extends the check for unsigned-peers to ensure full coverage in capabilities.

Fixes tailscale/corp#45116

Change-Id: I918af24f0b9855e55921cbdad109cc68e745e125

Signed-off-by: Mike Jensen <mikej@tailscale.com>
2026-07-22 08:27:15 -06:00
Kristoffer DalbyandKristoffer Dalby 1d82c1b3d0 gokrazy/tsapp: lock down breakglass access
Point tsapp at the tailscale/breakglass fork, fetch SSH keys from EC2
IMDSv2, restrict to the sec-scan user and internal CIDRs, start on
boot, and stop after 120s idle.

Updates #1866

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-07-22 16:03:07 +02:00
Kristoffer DalbyandKristoffer Dalby 745bb85072 go.mod: require tailscale/breakglass fork for tsapp
Depend on the tailscale/breakglass fork directly for its new
access-control flags. The fork renamed its module path so no replace
directive (disallowed here) is needed. Upstream gokrazy/breakglass
stays for the arm64 appliances.

Regenerate depaware manifests and nix flake hashes for the pkg/sftp
bump pulled in by the fork.

Updates #1866

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2026-07-22 16:03:07 +02:00
Mario MinardiandMario Minardi c8ae72b537 various: change OAuth and WIF auth key resolvers to take struct args
Change signature of OAuth and identityfederation auth key resolution
hooks to take in structs instead of lists of args as they were getting
unwieldily.

Updates https://github.com/tailscale/tailscale/issues/20339

Signed-off-by: Mario Minardi <mario@tailscale.com>
2026-07-21 15:44:45 -06:00
Brad FitzpatrickandBrad Fitzpatrick 3ccc7725a3 tstest, util/testenv: drop tstest's dependency on the testing package
Change tstest's exported functions (AssertNotParallel, Replace,
Parallel, RequireRoot, SkipOnKernelVersions, MinAllocsPerRun, FixLogs,
UnfixLogs, CheckIsZero, ResourceCheck) to take testenv.TB instead of
testing.TB or *testing.T, so importing tstest from non-test code no
longer links the testing package and its flag registration side
effects into the binary. Add testenv.Verbose to replace the one use of
testing.Verbose, and a deptest check to keep testing out of tstest's
dependency graph.

Callers are unaffected: *testing.T and testing.TB both satisfy
testenv.TB.

Updates tailscale/corp#45223

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ib373ff66ceff638d071582baf8367245987e9155
2026-07-21 13:03:23 -07:00
Adriano Sela AvilesandAdriano Sela Aviles d11757863d cmd/tailscale/cli: remove wip-code gate for service list cmd
Updates #20166

Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
2026-07-21 11:33:36 -07:00
Fran Bull 425a916ce2 ipn/ipnlocal: check WIP env var before doing conn25
registering of split dns routes.

Updates tailscale/corp#43680

Signed-off-by: Fran Bull <fran@tailscale.com>
2026-07-21 09:02:47 -07:00
Brad FitzpatrickandBrad Fitzpatrick 4ad1243332 util/testenv: add ArtifactDir, Attr, Output methods to TB
The TB interface exists to mirror testing.TB without importing the
testing package, but it had fallen behind: Go 1.25 added Attr and
Output, and Go 1.26 added ArtifactDir. Add the missing methods and a
reflection-based test that TB has every exported method of testing.TB,
so future additions to testing.TB fail a test instead of silently
diverging. It can't be a compile-time assertion because testing.TB has
an unexported method.

Updates #16330
Updates #18682

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I9ba093afefdf3a6311ef4648bc1a13add9af453d
2026-07-20 21:20:11 -07:00
Brendan CreaneandGitHub a7cb5745a2 tstest/natlab/vmtest: add Fedora + DNS-backend coverage, harden non-KVM boot (#20409)
* tstest/natlab/vmtest: make cloud VM boot robust without KVM

Adding heavier distro images (Fedora) surfaced several ways the cloud VM
boot path breaks under TCG software emulation (no /dev/kvm), especially
with multiple concurrent VMs on few cores.

- Add a virtio-rng device to the cloud path so early boot doesn't block in
  getrandom() waiting for the CRNG to seed.
- When no hardware acceleration is available, relax the stuck-console
  watchdog (tuned for KVM's ~1-2s first output) and serialize VM boots so a
  heavy guest doesn't starve its siblings' emulation threads.
- Bound the bring-up context to the test deadline and dump each VM's console
  on failure, so a hang surfaces as a diagnosable Fatalf instead of an
  opaque `go test -timeout` panic (which skips cleanups).

Fixes tailscale/corp#44794
Updates tailscale/corp#44793

Signed-off-by: Brendan Creane <bcreane@gmail.com>

* tstest/natlab/vmtest: add Fedora and DNS-backend test coverage

Add the first RHEL-family distro and the machinery to assert and provision
distinct DNS backends, so adding a distro isn't "basically equivalent" to
the others.

- Add a Fedora 43 image (NetworkManager + systemd-resolved, SELinux
  enforcing). restorecon-relabel the curl'd binaries so they exec under
  enforcing mode.
- Add DNSBackend/AssertDNSBackend, reading the dns_manager_linux_mode_*
  clientmetric to assert which backend tailscaled selected.
- Add a WithDNSMode node option. WithDNSMode(DNSDirect) masks
  systemd-resolved and writes a plain resolv.conf pointing at natlab's fake
  DNS, forcing the direct backend -- so one image covers multiple backends.

Fixes tailscale/corp#44796
Updates tailscale/corp#44793

Signed-off-by: Brendan Creane <bcreane@gmail.com>

---------

Signed-off-by: Brendan Creane <bcreane@gmail.com>
2026-07-20 12:41:25 -07:00
License UpdaterandWill Norris c130a9b520 licenses: update license notices
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2026-07-20 12:37:39 -07:00
Brad FitzpatrickandBrad Fitzpatrick a84a264228 tempfork/acme: sync with tailscale/golang-x-crypto, add profiles support
This bumps go.mod to the current tailscale/golang-x-crypto, picking up
its rebase onto current upstream golang.org/x/crypto and its
cherry-pick of the pending upstream change
https://go-review.googlesource.com/c/crypto/+/788000, which adds ACME
certificate profile support: a new WithOrderProfile order option and
profile discovery via the directory metadata. That change has not yet
been submitted upstream and is subject to final API changes before it
lands there.

It then re-vendors that fork's acme package into tempfork/acme as
usual (per the TestSyncedToUpstream workflow), except for upstream's
pebble_test.go, which is now excluded from the sync: its
TestWithPebble downloads the Pebble module from outside our go.mod,
then builds and runs its binaries during tests.

Profile support is needed to request LetsEncrypt IP address
certificates, which require the "shortlived" profile.

Updates tailscale/corp#45167

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I3f7c2a91e5d8b4a6c0e2f9d1b7a3c8e6f4d0a2b9
2026-07-20 11:37:37 -07:00
Brad FitzpatrickandBrad Fitzpatrick de0553be66 util/httpm: exempt tempfork from TestUsedConsistently
Files under tempfork are vendored copies of upstream code that we
want to keep as close to upstream as possible, so don't require them
to use httpm constants. An upcoming tempfork/acme sync brings in
upstream test files using net/http's method constants.

Updates tailscale/corp#45167

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: If2a90b1d7c5e8f3a6b4d0c9e2a7f5b8d1c4e6a3f
2026-07-20 11:37:37 -07:00
Brad FitzpatrickandBrad Fitzpatrick 172124da8c client/local, ipn, tailcfg: document which LocalAPI client APIs are stable
The client/local package doc said its API is not necessarily stable, but
that caveat was easy to miss and only a few cert methods said anything
explicit either way. People have been surprised by IPN bus changes
between releases.

Add explicit "API maturity" notes, matching the existing wording on the
cert methods, marking stable: BugReport, BugReportWithOpts, CertDomains,
CheckUpdate, CurrentDERPMap, DialTCP, UserDial, DisconnectControl,
GetPrefs, EditPrefs, Status, StatusWithoutPeers, SetUseExitNode,
SwitchProfile, UserProfile, and the WhoIs* methods. Mark unstable:
ipn.Notify, WatchIPNBus, DoLocalRequest, the Debug*, Drive*, Check*,
EventBus*, and Stream* methods, SetComponentDebugLogging,
TailDaemonLogs, ShutdownTailscaled, GetDNSOSConfig, GetEffectivePolicy,
GetServeConfig, and GetAppConnectorRouteInfo.

Also note on tailcfg.DERPMap that the type is subject to minor changes
over time though its general shape is stable, document that
ipn.Prefs.CorpDNS is the internal name for "tailscale set --accept-dns",
and add a package doc paragraph to client/local saying that methods
without an explicit API maturity note should be assumed unstable.

Updates #20406

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I9333c58ae312e392c61d7de77987282e84ce2aeb
2026-07-20 10:41:59 -07:00
Claus LensbølandGitHub b14f7b7543 wgengine/magicsock: properly clean up peer disco maps (#20543)
Updates tailscale/corp#45124
Updates tailscale/corp#45128

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2026-07-20 13:12:58 -04:00
David BondandGitHub be0e460a20 cmd/k8s-operator,k8s-operator: Kubernetes Peer Relays (#20495)
This commit contains the Kubernetes implementation of peer relays via the new `PeerRelay` CRD. It's a mega branch consisting of the commits of other PRs gone into this work:

1. https://github.com/tailscale/tailscale/pull/20211
2. https://github.com/tailscale/tailscale/pull/20329
3. https://github.com/tailscale/tailscale/pull/20423
4. https://github.com/tailscale/tailscale/pull/20503

An instance of the `PeerRelay` CRD deploys a `StatefulSet` of containerboot instances configured to advertise themselves as peer relays using the IP addresses configured via `LoadBalancer` services on each cloud provider (with some AWS specifics as it's less automatic than its competing cloud providers). 

Per replica, a `LoadBalancer` type `Service` resource is provisioned and its IP address is used to configure the respective relay.

This has been tested with success in AWS, GCP & Azure and provides additional modification to `Service` resources via the CRD for any other kinds of deployment environments. It also contains some work that may appear to be duplication of what already exists within `cmd/k8s-operator` so we can start building an appropriate migration path for `Connector`, `ProxyGroup` etc into respective `k8s-operator/reconciler/*` packages.

Closes https://github.com/tailscale/corp/issues/34524
2026-07-20 16:37:15 +01:00
Brad FitzpatrickandBrad Fitzpatrick 2dd5d82f56 tsnet: fix data race in chanTUN test device
The wireguard-go receive path could be in chanTUN.Write, selecting to
send on the Inbound channel, while test cleanup called chanTUN.Close,
which closed that same channel. The select on the closed channel in
Write did not synchronize with Close, so the race detector flagged
the send racing with the close. It could also have panicked with a
send on a closed channel.

Add a mutex serializing Write and Close. Write now checks for closed
under the lock before doing a non-blocking send, so Close can't close
Inbound mid-send.

Fixes #20541

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I8d8d10250cef0c1931753c78eebff6e8286f7201
2026-07-20 08:25:22 -07:00
Brad FitzpatrickandBrad Fitzpatrick 246c82a658 derp, wgengine: let clients advertise an opaque app name to DERP servers
Add an AppName field to the DERP ClientInfo so DERP servers can
attribute connections to the application making them, primarily for
best effort stats purposes. The value is plumbed per engine instance
rather than via a process global, so a process hosting multiple stacks
can attribute each one's DERP connections separately:
wgengine.Config.DERPAppName flows through magicsock.Options and
derphttp.Client into the naclbox-sealed ClientInfo JSON. Old servers
ignore the unknown field.

There are no callers in the tree yet setting the name.

Updates tailscale/corp#24454

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ia7d3e9c2b6f8140e5a9d7c3b2e6f1a8d4c0b5e9f
2026-07-20 07:33:44 -07:00
Fran Bull 37e175a032 feature/conn25: send ICMP errors back to sources
if we can't find a mapping for the magic IP they're sending traffic to.

Fixes tailscale/corp#34257

Signed-off-by: Fran Bull <fran@tailscale.com>
2026-07-20 07:33:00 -07:00
Fran Bull 8df4816be4 net/packet: generate ICMP destination unreachable packets
Updates tailscale/corp#34257
Signed-off-by: Fran Bull <fran@tailscale.com>
2026-07-20 07:33:00 -07:00
Brad FitzpatrickandBrad Fitzpatrick f65372c9ba net/bakedroots: add LetsEncrypt Generation Y roots (YE, YR)
LetsEncrypt announced its new "Generation Y" root hierarchy on
2025-11-24 and switched its default ACME profile to issue from the new
roots in May 2026. Our baked-in fallback root store only contained the
Generation X roots (ISRG Root X1 and X2), so chains terminating at the
new ISRG Root YE (ECDSA P-384) or ISRG Root YR (RSA 4096) roots failed
to verify when the system roots were also missing them.

Add both new self-signed roots, fetched from
https://letsencrypt.org/certificates/ (valid 2025-09-03 to 2045-09-02).

Also add a live network test, run by default in CI only (or with
--run-live-lets-encrypt-test), that verifies the baked-in roots alone
are sufficient to validate LetsEncrypt's per-root test endpoints for
X1, X2, YE, and YR.

Fixes #20527

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: Ic69076d8ae5aae08db167095745e92c53357afe3
2026-07-20 07:23:35 -07:00
Brad FitzpatrickandBrad Fitzpatrick 82cfea90ca all: fix JSON serialization under Go 1.27's finalized encoding/json/v2
Go 1.27 enables GOEXPERIMENT=jsonv2 by default: encoding/json is now
backed by the json/v2 machinery, and github.com/go-json-experiment/json
compiles as a thin alias of the standard library's encoding/json/v2.
Several tag options and behaviors we relied on did not make the cut for
the final Go 1.27 API, breaking tailscaled at runtime and four packages'
tests. This change adapts to the final API while keeping the wire format
byte-for-byte identical on all Go versions.

First, the `format` tag option was demoted to experimental. Its mere
presence in a struct tag now makes marshaling and unmarshaling fail at
runtime. tailcfg.SSHAction.SessionDuration had `format:nano` (added in
a2dc517d7 to pin the v1 representation), so on Go 1.27 any netmap
containing an SSH policy failed to decode, breaking every PollNetMap.
Remove the option here and in net/speedtest; time.Duration still
marshals as int64 nanoseconds under encoding/json on all Go versions
(Go 1.27's v1 mode sets FormatDurationAsNano by default), so old
clients and servers are unaffected. Add a regression test locking in
the exact wire format.

Consequently, invert the cmd/vet jsontags rule: it previously required
an explicit `format` tag on time.Duration fields, which is now exactly
wrong. It now rejects any `format` tag option, which would have caught
this bug in CI.

Second, the `inline` tag option was renamed to `embed`. The standard
library silently ignores `inline`, while the pinned go-json-experiment
module (used on Go 1.26) only knows `inline`. Specify both options in
types/prefs and logtail; each implementation ignores the option it does
not know, producing identical output. Drop `inline` once we require
Go 1.27.

Third, encoding/json (v1) now dispatches to MarshalJSONTo and
UnmarshalJSONFrom methods and its v1 options flow into nested
jsonv2.MarshalEncode calls. Types whose v1 methods deliberately
routed through jsonv2 for v2 semantics (types/opt.Value, the
types/prefs preference types) would silently change wire format
(e.g. nil slices becoming null). Pin jsonv2.DefaultOptionsV2 in
their jsonv2 methods so the representation is the same regardless
of the entry point.

Finally, json.Marshal costs one more allocation under Go 1.27,
tripping the types/logger.AsJSON alloc test. Switch its fmt.Formatter
to jsonv2.MarshalWrite with explicit v1 options, which writes directly
to the fmt.State: one allocation on both toolchains with unchanged
output. Depaware files pick up the go-json-experiment/json/v1 options
shim as a new dependency of types/logger.

With this change, go test ./... passes with both Go 1.26.5 and
go1.27rc2.

Updates #20220
Fixes #20528
Fixes #20254

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I694c7d57fd81e55a579c579e9be10032bca569d4
2026-07-19 09:58:50 -07:00
Brad FitzpatrickandBrad Fitzpatrick ece1b12ebf cmd/tsconnect/wasm: don't return non-nil net.Conn interface on dial error
The NetstackDialTCP/UDP hooks returned the result of DialContextTCP/UDP
directly, so on error they returned a non-nil net.Conn interface holding
a nil *gonet.TCPConn or *gonet.UDPConn pointer, tripping up callers that
check the interface against nil and then call Close, crashing the wasm
worker. Apply the same fix that 46bdbb387 made for tailscaled and tsnet.

Fixes #20529

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I4fd66bb7615ee9b2d204256a43288ed7b7a12f35
2026-07-19 06:48:06 -07:00
Brad FitzpatrickandBrad Fitzpatrick 7be0054a7b words: redress historical wrongs against the tuatara
b5a41ff381 originally added both tuatara and mispelled tautara,
one as a tail and one as a scale.

f174ecb6fd added tuatara as a scale, not noticing the tautara
imposter.

Fixes #20522

Change-Id: Ie4fcb262ac705c766f55d406835de419856bb170
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-07-18 15:34:33 -07:00
Nick RossiandGitHub b91e844014 util/def,cmd/containerboot: add LookupEnv, simplify env parsing (#20277)
Simplifies cmd/containerboot env var parsing. Most of the private helpers did
not earn their abstraction: defaultEnv(name, "") is just os.Getenv(name), and
the rest collapse into cmp.Or and the existing def.Bool. defaultEnv,
defaultEnvs and defaultBool are gone.

Adds def.LookupEnv, the env companion to def.Bool, for the one case that needs
it: TS_KUBE_SECRET, where an explicit "" disables Kubernetes secret storage and
must stay distinct from unset (cmp.Or cannot express that).

Updates #20018

Signed-off-by: Nick Rossi <nrossi0530@gmail.com>
2026-07-17 18:32:52 -07:00
Brendan CreaneandGitHub d2af6a4d39 net/dns/publicdns: don't upgrade Control D port-53-only addresses to DoH (#20463)
DoHEndpointFromIP mapped the entire 2606:1a40::/48 range to a
dns.controld.com/<id> DoH URL, but the ID-encoded addresses in that range
are legacy plaintext-DNS endpoints that refuse :443. They now fall through
as ordinary port-53 resolvers; the free anycast freedns.controld.com/pN
addresses still upgrade via exact match.

Fixes #20433

Signed-off-by: Brendan Creane <bcreane@gmail.com>
2026-07-17 15:33:17 -07:00