The e2e suite covered the operator's in-process API server proxy but
not the ProxyGroup-based one. Add tests for both proxy modes that
drive a ConfigMap through its lifecycle via the proxy, verify a
forbidden request is rejected, and check that deleting the ProxyGroup
cleans up its StatefulSet and Tailscale Service.
Fixestailscale/corp#38009
Change-Id: Ifc0be47ce32dd96f8daa748af6785a8b82ec19a7
Signed-off-by: David Bond <davidsbond93@gmail.com>
To decide whether we can bump GOAMD64 for our production binaries, we
first need to know what x86-64 microarchitecture levels the fleet's
CPUs actually support. Add two gauges to the default metric set on
linux/amd64: goamd64_capable, the maximum GOAMD64 level the host CPU
supports per /proc/cpuinfo, and goamd64_compiled, the level the
running binary was built with per its build info. Both are computed
once at init and report 0 on error.
The per-level feature lists match the checks in Go's
runtime/asm_amd64.s.
Updates tailscale/corp#47099
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I3b4e5431ae1501f0b7d584c12d9af805e9ab6125
Instead of accessing the key and string directly, put it behind a method
to make it easier to use that as a proxy when we add multiple keys
(control and TSMP origin).
Instead of only updating a single origin for disco keys, teach peermap
how to work with multiple disco key sources so a key can originate from
either control or TSMP.
This sets the work up for switching dynamically between disco keys
later. As of this commit, the keys still arrive for the most part via
the controlClient, but this commit sets us up for:
- Switching dynamically between received keys.
- Route TSMP keys directly into magicsock, not via controlClient.
- Potentially revert controlClient to the state before any TSMP
changes, making it single threaded.
- Use switching keys as trigger for optimistic WG handshakes, instead
of tearing down the full connection and setting it up again.
Updates #20494
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
Invalidate the trust window so we re-run path discovery
instead of coasting on the dead path until trust lapses on its own.
Partially reverts 85bb5f8 removal of resetLocked within updateFromNode.
Fixes#20268
Change-Id: Ib18b31e4329513294b682e698d1a9f426a6a6964
Signed-off-by: Alex Valiushko <alexvaliushko@tailscale.com>
Clients can advertise an opaque app name in their ClientInfo but the
server previously did nothing with it.
Constrain app names to at most 32 bytes of printable ASCII, enforced
both in derp.NewClient and by the server when it parses the ClientInfo.
Extend the peerPresent frame, following its existing pattern of
appending optional fields, with a length-prefixed app name after the
flags byte, so trusted mesh watchers (other DERP nodes and stats
tools) can attribute connections by app. Old clients ignore the extra
bytes; old servers send frames without them.
Also add a derper --disallow-app-names flag taking a comma-separated
list of app names whose connections are refused, except for trusted
mesh peers.
Updates tailscale/corp#24454
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I6e721258675145833aafa1355fabf7fc05a5a204
Test that the client parses peerPresent frames from servers of various
eras: old servers that send fewer fields than the client knows about,
and newer servers that send trailing fields the client doesn't know
about, which it must ignore. This matters during rollouts of new DERP
servers, when a region's meshed nodes and watchers run a mix of
versions.
This is in advance of a following commit that extends the frame with a
new trailing field.
Updates tailscale/corp#24454
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I0d46126a6003a9eb07a4da46dc291872149b1a07
Clock.Advance previously fired each Ticker once per period between the
current time and the advance target, even though all but the first few
sends were dropped on the Ticker's full channel. A test that advances
the clock 180 days with a 5 second ticker registered did 3.1 million
fires, each taking a mutex, attempting a channel send, and fixing the
event heap. Under the race detector, where synchronization operations
are 20-30x more expensive, this dominated test runtime.
Now, when a tick is dropped because the channel is full, skip the
remaining missed ticks in one step, staying aligned to the original
schedule and still firing the final tick at or before the advance
target. This matches time.Ticker, which drops ticks it cannot deliver,
and is observably identical behavior. Ticks that fit in the channel are
still all delivered.
In the internal corp repo, this takes controlclient.TestExpiry under
race from 6.0s to 2.2s (its no-race time is 0.5s), and the new
benchmark improves from 449µs to 90ns per 24h advance:
BenchmarkTickerLargeAdvance-16 100 449313 ns/op (before)
BenchmarkTickerLargeAdvance-16 13311910 90.22 ns/op (after)
Updates tailscale/corp#47035
Change-Id: I4826df212415eb2ea7ceb5bc46848e816f8ccde1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(bumping in oss mostly to get it into corp, and we require them to be in sync
for now. But we do use this in derpserver too.)
Updates tailscale/corp#46884
Change-Id: If01e3e91787e9ec4331eb9c997292239c6b379f1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
There's a deadlock in x/net/http2 with Go 1.27 that's
fixed with v0.58. This is prep for switching to Go 1.27.
Updates #20220
Change-Id: I8e81eafc0c2f78e1760b36e9d39ac4f6046565af
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The set-config loop uses uint16 endpoints. When Last is 65535, the increment wraps to zero and file or Unix targets continue indefinitely.
Break after applying Last so every closed range terminates without changing ordinary range behavior.
Fixes#20873
Signed-off-by: Bonobo <github@in9.at>
Pulls in https://github.com/tailscale/wireguard-go/pull/85 which
fixes an unbounded memory leak in mkIPInCIDRsTestFunc. It used a
package-level placeholder Peer, and AllowedIPs.Insert threads every
trie node onto that peer's trieEntries list, so each call leaked
its whole trie. SetAllowedIPs calls it on every netmap update, so
peer churn accumulated trie nodes until the client OOMed.
Fixestailscale/corp#47010
Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
When a map poll's context is canceled mid-read, the error that
surfaces from the response body read depends on the transport and Go
version: Go 1.26's http2 returned the context error, while Go 1.27's
returns the underlying "use of closed network connection". Normalize
the read-error paths in sendMapRequest to report ctx.Err() when the
context was canceled so callers see a stable, meaningful error
regardless of Go version.
Updates #20220
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I5898d3f452a2e61cfdf3ee015386365943c08e70
The service-pg-reconciler reconciles Services annotated for an ingress
ProxyGroup, but its ProxyGroup watch reused ingressProxyGroupFilter,
which is ingressesFromIngressProxyGroup. That handler lists Ingresses
and returns Ingress keys, so when a ProxyGroup became Available the
requests it produced never matched a Service and the reconciler's Get
just came back NotFound.
This fixes this by adding servicesFromIngressProxyGroup, which lists the
Services indexed for the ProxyGroup and returns their keys, matching what
the egress path already does with egressSvcsFromEgressProxyGroup.
Fixes#20944
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
Only allow TransitIPs to be allocated when the client has permission to
access the requested app.
Updates tailscale/corp#40076
Change-Id: Ib4b37afa25ffbdd220ba2c0fd9cfec8d5df5311f
Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
Also reports when shard results are missing, unreadable, duplicated, or
empty, so the pass count and percentage can't overstate what actually ran.
Updates #20931
Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>
Guard optional annotations maps in generated CEL expressions before
looking up expose or proxy-group annotations. This lets ordinary
annotationless Services and Ingresses pass admission while preserving
deny-all and allowlist behavior for present proxy-group annotations.
RELNOTE: Kubernetes ProxyGroupPolicy now permits annotationless resources.
Fixes#20906
Change-Id: I8b9475d34c003ca6d233246c021ac656e0530fb1
Signed-off-by: joshrzemien <joshua.k.rzemien@gmail.com>
Historically, when DERP regions were switched away from strings to
numeric identifiers in PR #14641, tailcfg.Node.HomeDERP was declared
as an int instead of its own type.
This PR declares a new tailcfg.DERPRegionID type, represented by an
int64, and converts the following fields to use this type:
- netcheck.Report.PreferredDERP
- netcheck.Report.RegionLatency
- netcheck.Report.RegionV4Latency
- netcheck.Report.RegionV6Latency
- tailcfg.DERPHomeParams.RegionScore
- tailcfg.DERPMap.Regions
- tailcfg.DERPNode.RegionID
- tailcfg.DERPRegion.RegionID
- tailcfg.NetInfo.PreferredDERP
- tailcfg.Node.HomeDERP
- tailcfg.PeerChange.DERPRegion
- tailcfg.PingResponse.DERPRegionID
Note that the original field was an int, while the new field is backed
by an int64. This change makes DERPRegionID the same size on both
32-bit and 64-bit architectures.
Fixes: #20165
Change-Id: Ic6f795a6d791dd16f756f246d5a02085443e212f
Signed-off-by: Simon Law <sfllaw@tailscale.com>
After a netmap delta is applied, we scan the mutations for affected peers and
update the cache (if enabled) for those peers. For removals in particular, we
were relying on the node backend to resolve node IDs (provided by the delta
mutation) to stable IDs.
Prior to 65fd320a this happened to work because the node backend would hold on
to all the peers mentioned by the previous full netmap, even after applying
deltas. But that was essentially accidental, and once we fixed it not to do
that, these lookups no longer worked. We need the stable ID, since that is how
the cache is keyed, and now that they're no longer pinned, we were not properly
evicting removed peers from the cache.
To fix this, capture removed peer stable IDs while applying mutations to the
node backend, instead of trying to look them up afterward.
Updates #20796
Change-Id: I14ded78eaf9657645f0869a52460fd3cd86edba6
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
The packet filter only sees a via address's outer ULA, so netstack unconditionally dialed whatever IPv4 was embedded in it.
This change refuses TCP, UDP, and ping relays to host-scoped destinations after UnmapVia.
Credit to the Anthropic infrastructure security team for finding and reporting.
Fixes https://github.com/tailscale/corp/issues/46646
Change-Id: I93d8e27a2eddbce7eb3f8c5fa4677f8de3a8ed9e
Signed-off-by: Mike Jensen <mikej@tailscale.com>
The websocket upgrade path bound the returned conn's reads and writes
to the context passed to AcceptHTTP via wsconn.NetConn. That context is
typically an http.Request context, which net/http cancels once the
calling handler returns, so a caller that served the accepted conn
beyond its handler's lifetime had the conn killed out from under it.
The hijack-based HTTP/1 upgrade path has no such binding.
Make the two paths consistent: ctx now only bounds the handshake (its
deadline, if any, is applied to the conn by controlbase.Server) and the
returned conn's lifetime is the caller's responsibility. Document that
contract on AcceptHTTP and add a regression test that uses a
websocket-accepted conn after the accepting handler has returned.
Updates tailscale/corp#46806
Updates tailscale/corp#29053
Change-Id: I4fc4ea5cddc2c6174fdf21f8d832f2e0984a7533
Co-authored-by: Adriano Sela Aviles <adriano@tailscale.com>
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Bump five dependencies to resolve the following advisories;
GO-2026-6238, GO-2026-5764, GO-2026-5597, GO-2026-5490, GO-2026-5496,
GO-2026-5105.
Updates tailscale/corp#9497
Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
Let clients pin favorite devices, exit nodes, and services so GUIs can
surface & change them. Pins are stored per login profile in the new
favorites feature module, keyed per category; devices and exit nodes by
StableNodeID, services by ServiceName.
The item types live in a leaf package feature/favorites/pintype,
keeping them out of the core ipn hierarchy. Each category has
its own type (pintype.Device, pintype.ExitNode, pintype.Service).
Exposed over LocalAPI at GET/POST /localapi/v0/pins, where POST replaces
only the categories named in the request so a client can update one
category without clobbering the others. Pins are local to the device and
are not synced across a user's devices.
updates tailscale/corp#44836
Signed-off-by: Will Hannah <willh@tailscale.com>
Co-authored-by: Will Hannah <wph@Wills-Virtual-Machine.local>
Our tests are inconsistent in how they set environment variables and cleanup.
Missing cleanup logic can leak environment variable state across tests and
change the behaviour of subsequent tests.
This recently caused flakes in feature/acme, where `TestGetCertPEMWithValidity`
leaked `TS_CERT_SHARE_MODE` and `TestAsyncRenewalDedup` to fail inconsistently.
To fix the immediate flake and prevent future leaks, introduce a `SetenvForTest`
helper that handles setting and cleaning up environment variables in tests.
Fixes#20902
Change-Id: I9f8ef45ec875d66034cfd0054311bd69b67e2243
Signed-off-by: Alex Chan <alexc@tailscale.com>
containerboot waits up to 60s for the initial map response before
failing. Slow map responses (>60s seen in production) leaves
containerboot to timeout and fail.
Add a TS_BOOT_TIMEOUT env var to override the default. Falls back to
60s when unset.
Fixes#20912
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
Port some tiny testcases that apply to our corp impl of the rdv hasher
to OSS as well. This is in preparation for cutting over to the OSS
impl exclusively.
Updates tailscale/corp#46471
Signed-off-by: Amal Bansode <amal@tailscale.com>
This registers per-user RSOP policy stores when a user logs into a Windows session and ensures that per-user poicy settings are delivered to clients via IPN bus.
The store lifecycle is tied to Windows session lifetime via desktopSessionExt's SessionInitCallback, which handles unattended mode (per-user policies stay enforced after gui disconnection) and multi-user (refcounted across sessions for the same user).
Updates tailscale/corp#42259
Signed-off-by: kari-ts <kari@tailscale.com>
The connector struct holds a map of peer+transitIP -> destinationIP that
it uses for routing traffic. The client registers new entries in the map
over the peer API.
Stop the transitIPs map from growing indefinitely by expiring entries
after 1 hour.
Updates tailscale/corp#38261
Signed-off-by: Fran Bull <fran@tailscale.com>
In cases where cigocacher is using a proxy that handles the auth, it's
possible to fetch stats without knowing our own access token. Don't fail
early if the access token isn't passed. If there's no proxy, it will
fail with the error from the gocached server.
Updates tailscale/corp#45427
Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
Deploy k8s nameserver during e2e test setup, and point the cluster resolver
(CoreDNS or kube-dns) to it so tests can resolve MagicDNS names inside the
cluster.
Add a test to verify singleton L7 Ingress is reachable from inside the
cluster using its MagicDNS Name.
Update existing egress tests to use a dedicated tailnet target per test (to
avoid conflicts). Egress tests now also verify that an egress target is
reachable from within the cluster using both its Service and MagicDNS name.
To successfully curl using the target's MagicDNS name, publish test CAs as a
ConfigMap to the cluster, and mount these for each curl pod.
Fixestailscale/corp#38027
Signed-off-by: Becky Pauley <becky@tailscale.com>
kubeconfigPath dereferences FileInfo for non-ENOENT stat errors even though os.Stat returns nil FileInfo. Preserve the failing list entry so checkKubeconfigWritable reports the existing access error instead of panicking.
Updates #11604
Signed-off-by: Bonobo <github@in9.at>
Static size checks (iossize) catch binary dirty-page growth but nothing
covered runtime heap cost, which is what actually consumes the iOS
Network Extension's 50 MiB jetsam budget. Bring up a tsnet backend
(with the full condregister feature set, matching shipping clients)
against an in-process testcontrol server and assert live post-GC heap
budgets for (a) backend startup with zero peers and (b) marginal cost
per netmap peer.
The startup test measures 1.3 MiB today and fails loudly on the
conn25 flow-table pre-allocation regression (17 MiB) that jetsam-killed
the iOS extension on large tailnets.
Budgets are deliberately generous (6-12x current measurements) to stay
flake-free while still catching the multi-MiB regressions that matter
for mobile.
A new debugknob enables us to constrain the GSO/GRO batch size to 1 for
these tests so as to avoid the memory allocation associated with those
buffers, which are a known issue with their own work stream.
Updates tailscale/corp#46408
Updates tailscale/corp#18514
Signed-off-by: James Tucker <james@tailscale.com>
Currently we serialise ACME account setup and ACME issuances for domains
which don't yet have a valid certificate, but not for async renewals of
still-valid certificates.
This patch adds a check that we only have one async renewal for a domain
in-flight at a time, and a test that ensures we de-dupe these renewals.
Updates tailscale/corp#46420
Change-Id: Ibbaa537cd28b8adf238b5c9396403e845f95b7e7
Signed-off-by: Alex Chan <alexc@tailscale.com>
Our desktop detection was using "wayland-1" as a search string for
detecting wayland desktops, but many desktops use other indexes for the
session. Additionally we did not detect mir or gamescope.
Add a new detection method that leans on systemd-logind (if available)
and fall back to using the existing method of looking for open unix
sockets, but add search strings and tail the index off of the wayland
session detection.
Fixes#20847
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This change contains the protocol changes needed to support
describing authorization for conn25 apps. As a temporary transition
measure during development, app configurations can disable authorization
enforcement.
Updates tailscale/corp#40076
Change-Id: I3183c10374aacb0048f6632c384f71f758c20f2f
Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
Move away from mutating global, exported variables in wireguard-go. Use
device.Option's passed to device.NewDevice, instead. No functional
changes, just API cleanup in preparation of future changes.
Updates tailscale/corp#22467
Updates tailscale/corp#46396
Updates tailscale/corp#37878
Signed-off-by: Jordan Whited <jordan@tailscale.com>
This adds a new ring buffer implementation that aims to replace
logtail.Buffer and the on-disk implementation in filch.Filch.
There are several problems with filch.Filch:
* Filching stderr should not be done at the buffer layer.
This makes structured representation within the buffer difficult
as arbitrary stderr data may unexpectedly appear,
which hinders attempts at more structured data.
* Log messages are assumed to be discreet lines rather than arbitrary bytes.
This makes it harder to switch the structured representation (e.g., using CBOR instead).
* Data that appears asynchronously through stderr never triggers a wake-up within logtail.
Consequently logs may never be uploaded.
* Relatedly, there is no mechanism for notifying that data has newly arrived in the buffer.
* There is no two-stage exfiltration. The TryReadLine method may or may not persist
the fact that the data was read. It arbitrarily depends on whether we cross
a magical file boundary in the dual-file approach.
A failed upload followed by a restart results in dropped logs.
A successful upload followed by a restart results in duplicated logs.
The new Buffer interface and VolatileBuffer implementation are
a step in the direction to resolving these problems.
* In the future, filching will output to a separate pipe
that we explicitly process the data for,
before putting it into the log buffer.
By processing the data, we can protect against stderr garbage being inserted
into the buffer unexpectedly breaking any structure.
* The Buffer.Peek and Buffer.DiscardUntil methods provide a way
to exfiltrate in a two-step manner.
When uploading, we peek at a chunk of data to upload.
When successful, we discard the data, ensuring that the buffer knows
not to provide that data again. The Len method can be used to suggest
to the logging service the amount of back pressure that exists.
Updates tailscale/corp#21363
Signed-off-by: Joe Tsai <joetsai@digital-static.net>
We stop waiting to see if registry keys come available; this causes bad
interactions with the LocalBackend watchdog.
Instead we check the network interface for availability of AF_INET,
AF_INET6, and AF_NETBIOS. If no IP families are available on the
interface, we fail with an error. Otherwise we only attempt to configure
DNS for the address families that are actually enabled.
We also avoid changing any NetBIOS settings if AF_NETBIOS is disabled.
Fixes#46276
Change-Id: Ic7ae8a3dc810f4c428085f8b3ad905c6c32ae351
Signed-off-by: Aaron Klotz <aaron@tailscale.com>
Since #17567, FS.ChildAUMs scans and decodes every active AUM file.
Authority reconstruction and compaction call it repeatedly, making
filesystem work quadratic in the number of AUMs.
Build per-FS indexes of active AUM hashes and parent-to-child
relationships in one scan. Use the indexes for graph queries while
continuing to decode returned AUM values from disk. Invalidate the
indexes after mutations, but retain them for empty purges.
A production-sized 825-AUM benchmark improves from 124 seconds to
4 seconds on macOS with Defender and from 5.9 seconds to 34 milliseconds
on Linux ext3. The checked-in benchmark exercises the 2,000-AUM maximum
linear history accepted by the traversal guardrail.
RELNOTE: Avoid Tailnet Lock startup failures with large authority histories.
Fixes#20735
Change-Id: I088136bc2e9d1b6bfdecb223c069d42400c9f63d
Signed-off-by: Michael Renner <terrorobe@github.com>
The rendezvous hasher for traffic steering loadbalancing was flawed.
By plainly using the FNV-1a hash value, the result often reflected the
magnitude of the most significant bits in the hash seed, meaning the
hash function was not diffusive (aka missing the Avalanche Effect).
Popular wisdom seems to be that the output of FNV-1a should be mixed
with some large numbers to perturb more output bits. Borrow concepts
from other (Rust, Java) libraries by using the mix13 variant of 64-bit
finalizers by David Stafford.
Modify the fuzz test that asserts this fairness. Adjust a few
constants like client count and candidate count to more closely
reflect real-world scenarios and practical probabilities. Tighten
the bounds for distribution from 50% to +-20%.
Updates tailscale/corp#46471
Signed-off-by: Amal Bansode <amal@tailscale.com>
Previously, a delta update that drops a peer would not invalidate the digest
cache for that peer after deleting its cache entry. If a subsequent (later)
delta re-adds that peer with the same content, the digest cache would prevent
us from updating the persistent entry. Add a test to exercise this, and fix
the bug.
Updates #20795
Change-Id: I4a9ef03e8787a330d6395629251ee61ca2221fdd
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>