Commit Graph

11041 Commits

Author SHA1 Message Date
Rollie Ma
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 Proctor
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 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-sm05
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 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 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
Saleh
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-Ami
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-Ami
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-Ami
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 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 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 Jensen
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 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 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 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 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 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 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 Creane
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 Updater
c130a9b520 licenses: update license notices
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
2026-07-20 12:37:39 -07:00
Brad 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 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 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øl
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 Bond
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 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 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 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 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 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 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 Rossi
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 Creane
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
Mike Jensen
689c6c2e6d ipn/ipnlocal: reject SrcCaps-based packet filter rules for unsigned peers (#20513)
This change ensures `packetFilterPermitsUnlockedNodes` also considers SrcCaps-based grants when checking for unsigned peer access.

Fixes tailscale/corp#45116

Change-Id: I0ac938367888f67ed6f355fc19959cc8c31722a2

Signed-off-by: Mike Jensen <mikej@tailscale.com>
2026-07-17 15:58:06 -06:00
Brendan Creane
bab3f5fce7 wgengine/router/osrouter: remove orphaned tailnet addrs on cleanup (#20304)
* net/tsaddr: unmap IPv4-mapped IPv6 addrs in IsTailscaleIP

IsTailscaleIP branched on ip.Is4() before checking the CGNAT range, so an
IPv4-mapped IPv6 address (e.g. ::ffff:100.64.0.1) took the IPv6 path and was
tested only against the ULA range, wrongly returning false for a Tailscale
CGNAT address. Unmap at the top so both forms are classified identically;
Unmap is cheap and IsTailscaleIPv4 stays IPv4-only for callers that need it.

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

* wgengine/router/osrouter: remove orphaned tailnet addrs on cleanup

The orphan-address sweep added in #20199 ran only inside Router.Set, so the
teardown path (tailscaled --cleanup, and the unconditional cleanup at daemon
start) never removed stale Tailscale addresses a previous instance left on a
persistent tailscale0 -- it only flushed iptables/nftables.

Wire address removal into cleanUp: with no desired config, every Tailscale-range
address on the interface is an orphan, so enumerate and delete them all (IPv4
and IPv6, best-effort) in removeOrphanedAddrsForCleanup.

tailscaleInterfaceAddrs now yields the interface's addresses as an
iter.Seq[netip.Prefix], and the filters compose lazily over it: tailscaleAddrs
(every Tailscale-range address; used by cleanup), deletableAddrs (isDeletableAddr:
Tailscale-range and deletable now, i.e. excluding v6 when v6 is unavailable; used
by the live Set sweep), and orphanedAddrs (drops the desired addresses). The Set
sweep ranges the composed iterator directly, so no throwaway slices are built.
delAddress is made idempotent: it attempts both the loopback-rule teardown and
the address delete and joins their errors, so a missing firewall rule can't leak
the address, and it no longer no-ops on v6 (cleanup relies on that to remove v6
orphans even when this process never brought IPv6 up).

The Set-time sweep is otherwise unchanged; re-running it on network changes
(netmon) for late orphans remains a follow-up (tailscale/corp#43882).

Updates #19974
Fixes tailscale/corp#44173

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

---------

Signed-off-by: Brendan Creane <bcreane@gmail.com>
2026-07-17 14:18:09 -07:00
Brad Fitzpatrick
0433cc6929 feature/syslog, cmd/tailscaled, logpolicy: add optional --syslog flag
Add a new modular syslog feature providing a tailscaled --syslog flag
that sends the daemon's logs to the system syslog daemon instead of
stderr, which is useful when running as a daemon without a service
manager that captures stderr (e.g. OpenWrt's procd).

The feature package registers two new hooks: one to register its flag
before flag parsing, and one that tailscaled calls early in main to
redirect the standard library's default logger. Because logpolicy later
points the default logger at logtail, whose local console copy writes
to stderr, logpolicy now also consults the hook and sends its console
copy to the same sink (with timestamps disabled, as syslog records its
own).

The feature is linked by default only on Linux, FreeBSD, and OpenBSD,
and can be removed with the ts_omit_syslog build tag. If connecting to
the syslog daemon fails at startup, tailscaled logs a warning and
continues logging to stderr.

Fixes #16270

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I8f3a92d4c1e6b70a5d29e4f61b3c874250a9de13
2026-07-17 13:55:23 -07:00
yaruk-byte
def265083b tstest/integration: run a smoke test against a Windows tailscaled service (#20382)
* tstest/integration: run a test against a real Windows tailscaled service

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

* tstest/integration: serialize Windows service tests and clean up state

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

* tstest/integration: address review feedback on Windows service tests

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

* tstest/integration: use background context for service teardown

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

* tstest/integration: run Windows service test via the normal windows CI run

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

* tstest/integration: address review feedback on Windows service tests

Updates #20381

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>

---------

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>
2026-07-17 12:29:12 -07:00
Alex Chan
11a6255b22 scripts/installer.sh: remove an unused PACKAGE_NAME variable
This variable wasn't used in the commit when it was introduced (bd5c509).

Fixes #19841

Change-Id: I82a2ba613c71eb99d98c5e7063e8cd077ba03ece
Signed-off-by: Alex Chan <alexc@tailscale.com>
2026-07-17 20:12:50 +01:00
Claus Lensbøl
9175fe2675 net/tstun: drop TSMP messages injected into TUN (#20511)
Enforce that TSMP messages are only accepted for transmission over the
wireguard connection from within the client.

Updates tailscale/corp#45059

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2026-07-17 15:10:08 -04:00
Claus Lensbøl
82a381e54b control/controlclient,net/tstun,wgengine/magicsock: fix handling of zero keys in TSMP (#20508)
Updates tailscale/corp#45042

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2026-07-17 14:02:53 -04:00
Brendan Creane
c1edf7f458 wgengine/router/osrouter: sanitize interfaceV6UsableForTun path with os.OpenInRoot (#20505)
interfaceV6UsableForTun interpolates the interface name into a /proc path.
A plain filepath.Join + os.Open only cleans the path, so a tunname with
".." (or a symlinked component) could read outside /proc/sys/net/ipv6/conf.
Open under that fixed directory with os.OpenInRoot, which rejects any path
escaping the root (openat-based, so also TOCTOU-resistant), still using
filepath.Join to build the relative name. See https://go.dev/blog/osroot.

Updates #20447

Signed-off-by: Brendan Creane <bcreane@gmail.com>
2026-07-17 10:17:06 -07:00
Jordan Whited
cc0b3ddbbe net/packet: add TSMPType docs
Updates #cleanup

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2026-07-17 09:56:25 -07:00
Jordan Whited
3076698cdf net/packet: fix TSMPType docs
Updates #cleanup

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2026-07-17 09:28:49 -07:00
Jordan Whited
94381a191a disco: fix UDPRelayEndpoint.AddrPorts slice cap math
Fixes tailscale/corp#45066

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2026-07-17 09:11:12 -07:00
Michael Ben-Ami
7ec9b7ffa3 feature/conn25: preserve TTL on DNS rewrites
We were accidentally hardcoding TTL 0 before.

Fixes tailscale/corp#45025

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
2026-07-17 10:27:14 -04:00
Brendan Creane
cfd101f9d7 configure DNS even when router.Set fails (#20488)
* wgengine: configure DNS even when router.Set fails

Reconfig configured the router first and returned on any router.Set error,
before the DNS block ran. On a host where router config fails on every
reconfig -- e.g. a tun MTU below 1280 that breaks IPv6, or a kernel missing
netfilter features -- the OS resolver was never told about MagicDNS or the
tailnet search domain, so tailnet names failed to resolve with no DNS error
in the logs.

Record the router error and continue instead of returning on it, still
attempt dns.Set, and join the router, DNS, and VPN-reconfigure errors into
the return value. DNS stays after router config (still needed: some DNS
managers refuse to apply settings before the device has an address); only
the error coupling is broken. Fixes a regression from 84430cdfa (v1.8.0).

Updates #20447

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

* wgengine/router/osrouter: gate IPv6 on per-interface support, not just global

getV6Available reported IPv6 usable whenever the netfilter runner reported
global IPv6 support, missing the case where the kernel has IPv6 but has not
enabled it on tailscale0 specifically -- e.g. when the tun MTU is below the
1280-byte IPv6 minimum, so /proc/sys/net/ipv6/conf/tailscale0 never exists
and the v6 address and route adds fail, aborting the whole Set. See #20447.

AND a per-interface check into getV6Available, evaluated per call so a later
Set picks up v6 if the interface gains it. All v6-gated operations funnel
through getV6Available, so Set now skips v6 gracefully instead of erroring.
Also remove the dead r.v6Available field that masked this with its global
name.

Updates #20447

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

---------

Signed-off-by: Brendan Creane <bcreane@gmail.com>
2026-07-16 14:53:30 -07:00