This repository was archived by the owner on Jul 13, 2025. It is now read-only.
Fork Sync: Update from parent repository - #36
Open
github-actions[bot] wants to merge 1969 commits into
Open
Conversation
ExecQueue.Shutdown does not wait for a function that is already executing, so Close could tear down magicConn, dns, wgdev, and tundev while a queued linkChange was still using them, panicking during shutdown. Add ExecQueue.ShutdownAndWait, which discards queued functions that have not started and waits for the in-flight one, and use it in Close with a bounded context before tearing anything down. The eventbus client is closed first and is the queue's only producer, so no new work can arrive after the drain. Updates #17641 Change-Id: I0350bcb59c1ee4b0dcac88cf66b93828466c8c98 Signed-off-by: Adel-Ayoub <adelayoub.maaziz@gmail.com>
Add a new Prefs.RemoteConfig bool. When true, a c2n endpoint at
/remoteapi/localapi/* proxies into this node's LocalAPI at
/localapi/* with full read/write permission, giving the tailnet
admin the same API surface a local root/admin user has via the
tailscale CLI. All LocalAPI versions (v0, v1, ...) proxy through.
RemoteConfig is an alternative to Tailscale's default per-feature
double opt-in, in which both the tailnet admin and the local machine
owner must consent to each individual setting change. It is a single
client-side "I trust the tailnet admin" switch that, once on, hands
over full remote management of this node's settings and LocalAPI
without any further local prompt or confirmation.
This is only appropriate when the tailnet admin already owns the
machine (e.g. a corporate fleet device) or the local user has
explicitly delegated full control. It should never be enabled on a
personal/BYOD device with an untrusted tailnet admin. The trust
model is documented on the pref, on the hidden --remote-config CLI
flag, and on the feature/remoteconfig package.
The node advertises its RemoteConfig state to the control plane via
a new Hostinfo.RemoteConfig bool. This is only true when the feature
is both compiled in (buildfeatures.HasRemoteConfig) and its init
actually ran (feature.IsRegistered("remoteconfig")); tsnet builds
have the former but not the latter and correctly report false.
The handler lives in feature/remoteconfig and can be omitted with the
ts_omit_remoteconfig build tag. tsnet's TestDeps guards against
accidentally pulling it in.
Updates tailscale/corp#18043
Change-Id: I72ce10a90a0e4e738c72c940af3af64c986160b2
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
…pliance This is a variant of "tailscale configure flash-appliance" but for running on Proxmox PVE hosts to make a Proxmox VM running the experimental Tailscale Appliance. This also makes the "Esc" key make the fbstatus GUI open up a terminal, instead of Control-Alt-F2 which is hard to type over NoVNC. And make gafpush unidirectional, to not require a local port be opened locally, which I hit while working on this. And make fbstatus included in all appliance variants, but bail out early and stop respawing if the machine has no framebuffer (e.g. AWS VMs). Updates #1866 Change-Id: I18ec2a16e4d5ff5574e16fe55c0e8d06cf4fab7f Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
PeerStatus.AllowedIPs is only populated when a peer has allowed IPs, so it is nil for peers whose backing nodes are offline or not yet approved, such as a kube-apiserver ProxyGroup with no healthy nodes. When the argument to "tailscale configure kubeconfig" resolved to a Tailscale Service ExtraRecord, nodeOrServiceDNSNameFromArg iterated AllowedIPs of every peer without a nil check and panicked with SIGSEGV. Skip peers with no AllowedIPs so the command reports the existing "is in MagicDNS, but is not currently reachable on any known peer" error instead of crashing. Fixes #20255 Signed-off-by: Salih Muhammed <root@lr0.org>
) * ipn/localapi,ipnlocal,feature/acme,client/local: honour Retry-After on cert rate-limit serveCert now responds with 429 + Retry-After when the underlying ACME error is a rate limit, instead of a generic 500. client/local surfaces this as a typed RateLimitedError with the parsed hint so callers can back off intelligently. Updates tailscale/corp#42164 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> * tsweb,feature/acme,ipn/localapi,ipnlocal: generalise cert error → HTTP mapping via tsweb.HTTPStatuser Introduces a tsweb.HTTPStatuser interface, any error can implement to describe its intended HTTP response (code, message, headers). Moves CertRateLimitedError from ipnlocal to feature/acme where it's constructed, and it now uses HTTPStatuser to return 429 + Retry-After. serveCert now checks for tsweb.HTTPStatuser rather than the specific error type, so it no longer needs to know about the ACME rate-limit type. Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> --------- Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
…le (#20347) EndpointSlices were created in provision(), which was called only if certain fields on the ExternalName Service had changed. If an EndpointSlice was deleted, it was never re-created (because the owning Service had not changed). Move EndpointSlice provisioning after this gated provision step so that it runs on every reconcile. Fixes #20322 Change-Id: I416fb5e4b40f2029efb97aa6ca7ceb3e31b0d52d Signed-off-by: Becky Pauley <becky@tailscale.com>
Signed-off-by: License Updater <noreply+license-updater@tailscale.com>
Bump the Go toolchain to 1.26.5. Updates #cleanup Signed-off-by: Patrick O'Doherty <patrick@tailscale.com>
The extension's acmeMu was a single lock around getCertPEM. Any in-flight ACME flow blocked every other domain. With many domains (ProxyGroup ingress) the queue would back up and per-call timeouts started firing while we were just waiting on the lock -- the cert loop treated that as a failure. Replace with one mutex per domain. Different domains run at the same time. Same domain still queues so the first run fills the cache and the rest read from it. The old global lock also kept ACME account setup safe by accident. Two goroutines could both find no account key, both generate one, both write -- last one wins on disk but each carries on with its own. Add acmeAccountMu around acmeKey and ensureACMEAccount to keep that path single-file. Otherwise two first-time issuances for different domains end up with separate accounts at LE. Updates #20288 Updates tailscale/corp#42164 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
Nothing uses them. DNS and MTU are handled elsewhere. This is pulled out of a future change that removes wgcfg.Config.Peers, to make that PR smaller. Updates #12542 Change-Id: I2ec8ae38dc6cce08bcc44e6c1f9177311202af89 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
…ture hook wgcfg.Config.NetworkLogging carried the network flow logging identity inside the WireGuard config, where it was unrelated to WireGuard; it lived there mainly so that identity changes would defeat Reconfig's ErrNoChanges check and reach the netlog startup/shutdown logic. Remove the field and move the whole netlog lifecycle into a new feature/netlog package, installed on the engine via the new wgengine.HookNewNetLogger hook, like other feature/* packages. The logging identity now comes from LocalBackend's current netmap via the widened NetLogSource interface (replacing Engine.SetNetLogNodeSource), so nmcfg no longer parses audit log IDs into the config. The engine still calls the hook before its ErrNoChanges return and before router.Set (to capture initial packets), and again after router.Set (to capture final packets), preserving the previous ordering. Core wgengine no longer imports wgengine/netlog, so minimal builds drop it entirely. tailscaled keeps netlog via feature/condregister, and tsnet imports feature/condregister/netlog explicitly to keep netlog enabled by default in tsnet-based binaries (tsidp, k8s-operator). This is pulled out of a future change that removes wgcfg.Config.Peers, to make that PR smaller. Updates #12542 Updates #12614 Change-Id: I41ca7dfe43c51e977c41b5f8e934bd1f0e6e6e24 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
We had an internal Google doc about this (Tailscalars: http://go/clientmod) but that doesn't help open source contributors or agents. So move the docs to git. Updates #12614 Change-Id: I0b0e9f0286b23b4fb1b51ff3d41eba75edf62cdf Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This change updates vulnerable dependencies with a direct fix path. Updated: * github.com/prometheus/prometheus@v0.311.3 - Direct dependency addressing https://pkg.go.dev/vuln/GO-2026-5710 and https://pkg.go.dev/vuln/GO-2026-5662 * github.com/go-openapi/swag@v0.27.0 - Needed to fix mutal dependency on github.com/go-openapi/testify after prometheus update * github.com/go-git/go-git/v5@v5.19.1 - Addresses https://pkg.go.dev/vuln/GO-2026-5496 * helm.sh/helm/v3@v3.21.1 - Root update to address most containerd CVEs * github.com/containerd/containerd@v1.7.33 - Addresses remaining container CVEs, in total: https://pkg.go.dev/vuln/GO-2026-5758 https://pkg.go.dev/vuln/GO-2026-5475 https://pkg.go.dev/vuln/GO-2026-5378 Updates #cleanup Signed-off-by: Mike Jensen <mikej@tailscale.com>
The netmap.NetworkMap type is deprecated and going away, and reconfigAppConnectorLocked only needed its SelfNode field anyway. Take a tailcfg.NodeView instead and check its validity in place of the old nil netmap check. Updates #12542 Change-Id: Id617845b67416404500cca438ce4ac0372cd8a8e Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
…d in
Like the earlier RemoteConfig change, gate Hostinfo.AllowsUpdate on
feature.IsRegistered("clientupdate") in addition to the
buildfeatures.HasClientUpdate build-tag const. tsnet binaries don't
import feature/clientupdate even though ts_omit_clientupdate isn't
set, so they shouldn't tell control they can be remotely updated.
Add the previously missing feature.Register call to
feature/clientupdate, document the binary-support requirement on
tailcfg.Hostinfo.AllowsUpdate, and make tsnet's dep test verify it
doesn't depend on feature/clientupdate.
Updates #12614
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I526ef11f2a4141f5fce161b1f77263324014b5c4
For studio-b12/gowebdav#87 Fixes #20295 Change-Id: I8ae6ff6969c84fcd510f0e15e0487fbfe9f7c821 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Fixes #19941 Change-Id: I69e63a8036f50cfee2ed770a88f92ce344412f4d Signed-off-by: scientificworld <scientificworld@users.noreply.github.com>
Export the machine's boot time (the btime line from Linux's /proc/stat) as node_boot_time_seconds, named to match what Prometheus's node exporter uses for the same value. Combined with process_start_unix_time, this can be used to distinguish process restarts from whole node restarts. The value is parsed once per process lifetime, not per scrape, and the metric is only published when a value is available, so non-Linux systems don't export a bogus zero. Updates tailscale/corp#44743 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I5f53186b97bb1482bd1a5387c0910b0ae26544ff
Previously cloner only handled literal slices for values, like `map[string][]int`. This adds support for named types with an underlying type of slice, like `map[string]IntSlice` with `type IntSlice []int`. Updates tailscale/corp#44077 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
…ally [This commit is pulled out of a branch that ultimately removes the wgcfg.Config.Peers field and removes all O(n peers) processing when handling deltas] magicsock.Conn.UpdatePeers existed so wgengine.Reconfig could tell magicsock the set of WireGuard peers from cfg.Peers, used only to garbage collect the derpRoute and peerLastDerp maps and to ReSTUN when the first peers appear. magicsock already learns the full peer list directly from LocalBackend via SetNetworkMap, UpsertPeer, and RemovePeer, so do that bookkeeping there and delete the API and its cfg.Peers use. Updates #12542 Change-Id: Id07551fc1950239f08a73a9ab02d69ce78d0de0c Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
A connection to a Tailscale Service IP on a port the service does not serve was forwarded to the underlying host. `acceptTCP` fell through to the isTailscaleIP case (a VIP is in the Tailscale IP range), which rewrote the dial target to 127.0.0.1:<port> and forwardTCP'd the connection onto whatever unrelated listener happened to be on the host's loopback at that port. This is reachable through the service IP by any peer which was granted access only to the service (dst: svc:foo), so it exposes host ports the peer has no ACL access to via the machine's regular IP. This happens when there tailscaled has a Tun interface and the forward bits are set. In this commit, we added a guard in acceptTCP, before the isTailscaleIP case that RSTs connections to a VIP service IP on a port with no serve handler. Served ports return earlier via TCPHandlerForDst, so only unserved ports reach the guard. Layer 3 services are unaffected: their traffic is released to the host in injectInbound and never reaches acceptTCP. Fixes #20362 Signed-off-by: kevinliang10 <kevinliang@tailscale.com>
Due to a customer issue, I investigated the Windows Dnscache service more intensively. I learned that the only time it attempts to read the NRPT from group policy is in response to a group policy change notification. Under the hypothesis that policy refresh is not effectively delivering GP notifications due to its dependency on reaching a DC, I replaced our use of the RefreshPolicyEx with the quasi-documented GenerateGPNotification API. Tests have been updated to ensure they check that they are running as LocalSystem, which is required for GenerateGPNotification. Fixes #20187 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
Updates #cleanup Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
The guidelines here provide a written version of common guidance around our CLI evolution that designers/implementors should consider as they propose/implement new or evolving CLI surfaces. Updates #engdocs Change-Id: Idcbc0900a4fda98bd2b29ac8bbc26dc1cb1be48f Signed-off-by: James Tucker <james@tailscale.com>
Add a new RouteManager type that tracks per-peer self addresses and advertised routes and incrementally maintains two read-only snapshots: an IP-to-outbound-peer bart table carrying the per-peer attributes the data plane needs (jailed state, masquerade addresses), and a coarsened OS route set (including OneCGNAT consolidation). Mutations are staged in a transaction (Begin/Commit) and applied to the snapshots via bart's Persist methods, which path-copy only the few trie nodes along the affected prefix, so a single-peer delta costs a bounded amount of work independent of the number of peers, instead of the O(n) full-world rebuild done today. Snapshots are published via atomic pointer swap for lock-free reads on the hot path, and Commit reports which peers' allowed IPs changed so callers can sync wireguard-go incrementally. This is the same immutable value snapshot pattern as used in the recent containerboot change, 364b952. Nothing uses it yet; this is pulled out of a future change that wires it into ipnlocal and wgengine, to make that PR smaller. Updates #12542 Change-Id: Iccc5258024e6f90311835b79fd2d83b2adb0d09d Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Updates #cleanup Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
PeerByStableID did an O(n peers) scan, and an upcoming change needs the same StableNodeID-to-NodeID resolution whenever prefs change (to resolve the selected exit node for the route manager, which keys peers by NodeID because that is the identity netmap delta mutations carry). Maintain a nodeByStableID index alongside the existing nodeByAddr and nodeByKey indexes, updated on full netmaps and on delta mutations. Updates #12542 Change-Id: Id1e5105a7470b02312533f0f46b69e6945cd62f0 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
FreeBSD guests downloaded their test binaries from vnet's
files.tailscale VIP at roughly 250 kB/s in CI, and transfers sometimes
wedged outright for many minutes, which is why TestSubnetRouterFreeBSD
timed out in about a third of its runs. Locally the same path moves
data at 100+ MB/s, so the problem was never CPU; it was TCP behavior
under two independent constraints, both diagnosed with a new
throughput harness (TestVnetPerfFreeBSDDownload), a VNET_TCP_DEBUG
endpoint sampler, and pcaps:
First, throughput is capped at receive-window/RTT. FreeBSD starts its
receive window at 64 kB and autoscales it in slow 16 kB steps, and on
an oversubscribed CI runner the effective RTT of the userspace vnet
data path reaches hundreds of milliseconds, giving almost exactly the
observed 250 kB/s. Fix: raise the FreeBSD guest's TCP buffer sysctls
in cloud-init before the downloads, and raise netstack's receive
buffer sizing for the reverse (upload) direction.
Second, the outright wedge: when netstack bursts more data than the
QEMU socket plus the guest's virtio RX ring can absorb, a wide swath
of segments is dropped downstream of vnet, and netstack's loss
recovery then crawls, retransmitting one or two segments per 200 ms
RTO for minutes at a time (a 33 MB transfer was observed taking 526
seconds against an otherwise idle receiver). Rather than depending on
recovery from mass loss, make the path effectively lossless by keeping
the maximum in-flight data (the 1 MB netstack send buffer) below the
downstream buffering: grow the guests' virtio RX rings from 256 to
1024 descriptors, enlarge the vnet-QEMU unix socket buffers, and grow
the netstack link endpoint queue from 512 to 4096 packets so a send
burst can't overflow it.
Also fixed along the way, found while chasing the above:
* pcapWriter fsync'd after every packet, serializing all traffic
behind disk writes when a test enables pcap; a pcap-enabled run
was capped at about 290 kB/s. Keep the per-packet Flush but drop
the per-packet fsync.
* Traffic originating from vnet's own netstack (control plane,
DERP, file servers) bypassed conditionedWrite, so SetLatency and
SetPacketLoss silently didn't apply to it.
* writeEthernetFrameToVM held one global mutex (and a shared
scratch buffer) across writes to all VMs, so one guest slow to
drain its socket stalled traffic to every VM on the server. The
write lock is now per-VM-connection.
TestSubnetRouterFreeBSD now passes locally in 31s (down from 4.5
minutes), still passes with the vnet simulating a 100 ms RTT
(downloads at 2-8 MB/s, previously 250-600 kB/s), and passes in 65s
with KVM disabled while pinned to two host CPUs, a harsher environment
than the CI runners. The benchmark test is opt-in via
--run-perf-tests (in addition to --run-vm-tests) so CI doesn't spend
a matrix job re-measuring it on every run. VMTEST_NO_KVM=1
forces TCG for reproducing slow-host behavior.
Fixes tailscale/corp#44805
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I1a7945a7e9c7d083b0ea2a3530eda0e9757dff18
This reverts commit ca9f697. The dependency updates broke the K8s E2E tests. Reverting so the updates can be re-landed with the tests passing. flake.nix, shell.nix, and flakehashes.json were regenerated with tool/updateflakes rather than reverted, since a later commit (6fdffd9) also updated them for the gowebdav bump. Change-Id: Id4afd7788d305a674841168e2a66a0009212ffd3 Signed-off-by: Fernando Serboncini <fserb@tailscale.com>
LocalBackend.Start previously shut down the previous control client in a goroutine, letting it run concurrently with the new one. An in-flight lite map update carrying stale Hostinfo.RequestTags could then be processed by the control plane after the new client had already changed the node's tags. Control treats such a request as an invalid tag transition and expires the node key to force a reauth, so retagging a node with "tailscale up --advertise-tags" intermittently logged the machine out. Instead, detach the old client under b.mu and shut it down synchronously with the lock released, before creating the new client. Shutdown cancels the old client's in-flight requests and waits for its goroutines to exit, so the cancellation of any stale update reaches the server before the new client sends its first request. Per the deadlock history in #18052, Shutdown must not be called with b.mu held; this uses the same pattern as DisconnectControl. Also teach the testcontrol server to model the control plane's tag transition handling (including expiring the node key on an invalid transition and ignoring updates from canceled requests), add an integration test reproducing the race, and add an ipnlocal test verifying that Start waits for the old client to shut down. Updates #20365 Updates #18052 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: If8c8e145bdadcef1b1b8fe6209453cf5f5a8d616
) The sysctler init container shells out to sysctl to turn on IP forwarding for non-userspace proxies. That binary ships in the procps-ng package, which Alpine has but Red Hat's UBI does not, so on UBI the init container exits 127 and every proxy Pod is stuck in PodInitializing and never registers a device. Updates: tailscale/corp#45981 Updates: tailscale/corp#44443 Signed-off-by: David Bond <davidsbond93@gmail.com>
) * net/dns: scope quad-100 on macOS so DoH profiles aren't shadowed On sandboxed macOS, an uncovered control ExtraRecord forced quad-100 to be the primary resolver, proxying all public DNS and shadowing a user's DoH system profile. Scope quad-100 to its match domains instead, adding the uncovered host records to MatchDomains so they still resolve while public names fall through to the OS resolver. quad-100 remains primary only without a usable base resolver or with non-enumerable MagicDNS host records. fixes tailscale/corp#45534 Signed-off-by: Will Hannah <willh@tailscale.com> * net/dns: move scoped DNS behind an envknob updates tailscale/corp#45534 Given the sensitivity of this change, let's stuff it behind a control knob for a release. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com> --------- Signed-off-by: Will Hannah <willh@tailscale.com> Signed-off-by: Jonathan Nobels <jonathan@tailscale.com> Co-authored-by: Jonathan Nobels <jonathan@tailscale.com>
…r own packages (#20639) Package tailcfg defines the types and constants used by the Tailscale protocol, but since everything is all in one package, it’s difficult to sift through the docs: https://pkg.go.dev/tailscale.com/tailcfg We define and enumerate capabilities as string constants for tailcfg.NodeCapability and tailcfg.PeerCapability. This PR extracts them into their own packages: - tailcfg.CapabilityFileSharing becomes nodecap.FileSharing - tailcfg.NodeAttrOnlyTCP443 becomes nodecap.OnlyTCP443 - tailcfg.PeerCapabilityTaildrive becomes peercap.Taildrive We originally intended for CapabilityFoo to grant an entitlement or permission for Foo, and for NodeAttrBar to configure Bar in the nodeAttrs section of the policy file. However, there was no technical enforcement of this convention, so new capabilities have used the NodeAttr prefix regardless of meaning. Therefore, this PR unifies tailcfg.CapabilityFoo and tailcfg.NodeAttrBar into a single package as nodecap.Foo and nodecap.Bar. Ran `go fix -inline ./...` and committed the changes that replaced uses of the tailcfg aliases with the authoritative ones. Updates #20259 Change-Id: Ieb7e7e6c8247c39faf42fdf15c68cdc7c621c730 Signed-off-by: Simon Law <sfllaw@tailscale.com>
The json/v2 prototype used to support a `format` tag option, which has been removed for the initial release of json/v2 in Go 1.27. The wrapper types in this package provide a way to avoid using the `format` tag option for all existing use-cases. The types are written to cooperate with other tag options such as `string`, which may stringify JSON numbers. We adjust cmd/vet/jsontags accordingly. Updates #20220 Updates tailscale/corp#45953 Change-Id: Ie1fcea41dc30983e9acc43085f42a6e8ee49d26e Signed-off-by: Joe Tsai <joetsai@digital-static.net>
#20779) sendTCP dials through tsdial.Dialer and so honors UseNetstackForIP, but sendUDP always uses a host-stack socket. In userspace networking mode there is no route to the tailnet, so a split-DNS query to a tailnet resolver only succeeds after falling back to TCP. The two subtests differ only in transport, isolating that gap; the UDP one is skipped pending a fix. Updates #20314 Signed-off-by: Brendan Creane <bcreane@gmail.com>
Fix the small number of existing violations of this check, and enable it for
future runs. The fixes needed were:
- Clean up a few misspelled package names (probably renames).
- Clean up a few lexical nits ("Package x" instead of "The x package").
- Add lint directives to some files affected by build tag variance.
- Add a missing package comment and re-generate the k8s docs.
The lint overrides are a little ugly, but there are only a few places where we
need them, and it's probably worthwhile to enable the check on the rest of the
repo. Rather than replicate the docs around the build tag, I made the lint
diagnotics reference the "correct" file.
Updates #cleanup
Change-Id: I0d97f2f468542af456a0396cf9a023f04f23e436
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
sendTCP dials through tsdial.Dialer and so honors UseNetstackForIP, but sendUDP opened a host-stack socket via packetListener and never consulted the dialer. In userspace networking mode (tsnet, or tailscaled --tun=userspace-networking) there is no tun device, so a split-DNS query to a tailnet resolver blackholed for the full udpRaceTimeout before the TCP fallback answered it. Add dialUDP, which picks between the netstack dialer and the existing packetListener the same way tsdial.Dialer.dialOneUser does, and adapt the connected netstack conn to nettype.PacketConn. sendUDP is otherwise unchanged, so txid checks, SERVFAIL/REFUSED handling, TC flagging and EDNS clamping are identical on both paths. Unskips the UDP subtest of TestForwarderNetstackUpstream, which now answers in ~300µs rather than 2s, and adds unit tests for the dispatch and for truncation over the netstack path. TestSplitDNSToTailnetResolverUDP covers the whole path end to end over real gVisor: two tsnet nodes with no tun, one resolving a split-DNS name whose upstream is the other. Fixes #20314 Signed-off-by: Brendan Creane <bcreane@gmail.com>
This test was the repo's slowest at 60 seconds of wall time, all of it spent sleeping: three of its subtests reach the unconditional 20 second failover wait in kube/services.EnsureServicesAdvertised, despite using a pure in-memory FakeLocalClient with no real control or I/O. Run each subtest in a testing/synctest bubble so the wait elapses on the fake clock instead. The test now completes in milliseconds. Fixes #20792 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I3442815f7efcf6de740f893197ee0461ab049bb2
The pre-push large-blob check diffed the pushed tree only against the remote's old tree for the same ref. After rebasing a stale branch past an unrelated large-file change on the default branch, that diff shows the large file as changed even though the exact blob is already on the remote via main, rejecting the push with a false positive. Diff against every available base tree instead: the remote's old commit for the ref plus the merge base with the remote's default branch. Only flag a file that is a large addition relative to all bases, so blobs the remote already has are not reported, while genuinely new large files on the branch are still rejected. Updates tailscale/corp#9863 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: Ia0c98fc1f5ab67a2913f948aeff605c72641ada7
…cDNS is disabled nodeBackend.nodeByName should always contain both FQDNs and short names, as it is used in different contexts, including UserDial DNS resolution, which should be able to resolve unqualified DNS names regardless of the MagicDNS state. However, net/dns/resolver.Resolver and, by extension, MagicDNSHosts implementations should only resolve fully qualified domain names, skipping short names when MagicDNS is disabled for the tailnet. This fixes it in (*nodeBackend).nodeByFQDNLocked, which is only used in the MagicDNS paths, and updates the tests. Fixes #20789 Signed-off-by: Nick Khyl <nickk@tailscale.com>
… name (#20804) * tstest/natlab: test that a peer's name doesn't shadow a bare upstream name With MagicDNS off, only suffixed names should be answered locally, but since 1.102 quad-100 also answers a bare, unqualified name whenever a tailnet device shares that name. It returns the device's Tailscale IP instead of forwarding to the tailnet's global nameserver, leaving the upstream record unreachable. TestBareNameNotHijackedByPeer configures a global nameserver owning a single-label name, adds a peer named to collide with it, and asserts the client's lookup returns the upstream address. It queries via "tailscale dns query" so the name stays a single label; a search domain completing it would resolve a different name and pass regardless. Add SplitDNSBareName to vnet's split-DNS zone to serve that name, and a packet-level case asserting the fake server answers it. Updates #20789 Signed-off-by: Brendan Creane <bcreane@gmail.com> * tstest/natlab: check short names resolve via quad-100 when MagicDNS is on TestMagicDNS asserted a peer's short name resolves, but through getent with a search domain configured: libc completed it to the FQDN, so the lookup never asked quad-100 for a single label. The bare-name path the resolver takes when MagicDNS is enabled was untested. Query the short name with "tailscale dns query" too, which asks for exactly the name given. This is the enabled-MagicDNS mirror of TestBareNameNotHijackedByPeer, and pins the other side of the condition added in 1ec3487: a fix that dropped short names unconditionally, rather than only when MagicDNS is disabled, now fails here. Updates #20789 Signed-off-by: Brendan Creane <bcreane@gmail.com> --------- Signed-off-by: Brendan Creane <bcreane@gmail.com>
Updates tailscale/corp#45906 Updates tailscale/corp#45803 Signed-off-by: Jordan Whited <jordan@tailscale.com>
Package cobs implements Consistent Overhead Byte Stuffing (COBS), a technique for reliable packet framing over serial byte streams. This has future utility for storing a sequence of arbitrary log entries on disk without needing to depend on intrinsic framing within the log entries themselves (e.g., JSON or CBOR). While more complicated, COBS is superior to offset-based framing mechanisms as the null byte can be trivially used to demarcate the boundaries of a frame. This makes COBS more resistant against bit-corruption where a single corrupted offset can make everything else in the file unreadable. COBS makes it possible to resynchronize framing after a corrupted section by simply searching for the next null. Performance: Benchmark/EncodeForward/Zeros-32 16341 76312 ns/op 13740.68 MB/s 0 B/op 0 allocs/op Benchmark/EncodeReverse/Zeros-32 6326 188261 ns/op 5569.79 MB/s 0 B/op 0 allocs/op Benchmark/DecodeForward/Zeros-32 16461 72140 ns/op 14535.28 MB/s 0 B/op 0 allocs/op Benchmark/EncodeForward/NonZeros-32 41797 29155 ns/op 35965.56 MB/s 0 B/op 0 allocs/op Benchmark/EncodeReverse/NonZeros-32 4792 248788 ns/op 4214.74 MB/s 0 B/op 0 allocs/op Benchmark/DecodeForward/NonZeros-32 35790 34584 ns/op 30319.92 MB/s 0 B/op 0 allocs/op Benchmark/EncodeForward/Random-32 23042 53727 ns/op 19516.64 MB/s 0 B/op 0 allocs/op Benchmark/EncodeReverse/Random-32 3164 374590 ns/op 2799.26 MB/s 0 B/op 0 allocs/op Benchmark/DecodeForward/Random-32 27241 58506 ns/op 17922.41 MB/s 0 B/op 0 allocs/op EncodeReverse performance is notably slower than EncodeForward because modern CPU architectures are not as optimized for reading from memory in reverse. However, reverse encoding is necessary if appending into a dst buffer that is identical to the src buffer. In such a case, the CPU performance hit is worth the benefit of avoiding an intermediate allocation. Speeds of GB/s is still plenty fast enough and magnitudes faster than JSON or CBOR encoding. Updates #17242 Updates tailscale/corp#21363 Signed-off-by: Joe Tsai <joetsai@digital-static.net>
The unstamped Long() fallback (plain go build/install, no linker stamps) always prefixed the buildvcs commit hash with "t". But that hash describes the main module's repo, which for binaries built from a repo that imports tailscale.com (e.g. Tailscale's proprietary repo) is not the tailscale.com repo. That made e.g. "1.103.0-dev20260811-t4bb67392c" ambiguous with the stamped scheme, where "t" always means the tailscale.com commit and "g" the commit of the repo the binary was built from. Keep "t" when the main module is tailscale.com itself, and use "g" otherwise, matching mkversion's meaning of the two prefixes. Updates tailscale/corp#44945 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I42ed6dce6572854ead487b20b0889460c47d0f58
NewFlowTable pre-sized its two per-direction lookup maps to maxEntries. The datapath handler creates a client table (10k flows) and a connector table (100k flows) unconditionally at extension init, so every client paid ~15.8 MiB of empty map buckets up front (measured on device, and reproducible with a benchmark: 2x100k + 2x10k maps keyed by the 38-byte flowtrack.Tuple). On iOS the Network Extension has a hard 50 MiB jetsam limit. On large tailnets the netmap and derived engine state need ~18 MiB of live heap on top of the baseline, and this pre-allocation pushed the process over the limit: the extension was killed (per-process-limit) seconds after connecting, in a relaunch loop, making such tailnets unusable on iOS. maxEntries is still enforced as a bound at insertion time; the maps now grow on demand instead. On an iPhone 14 Pro Max joining a ~600 node tailnet this took peak live heap during netmap ingest from 36.8 MiB to 24.3 MiB and the extension now connects and stays up with ~18 MiB of headroom instead of being killed at 50 MiB. Fixes tailscale/corp#46408 Updates tailscale/corp#18514 Signed-off-by: James Tucker <james@tailscale.com>
ProxyGroup, Recorder, and PeerRelay each carried a near-identical copy of the auth key re-issuance state machine (in-flight tracking, per-parent rate limiting, stale-device cleanup). A bug fix had to land in three places and could silently drift. Extract it into a shared tailscaled.Reissuer, alongside the other tailscaled workload helpers (NewAuthKey, AuthKeyFromConfigSecret, DeviceIDFromStateSecret) that the callers already use. It owns its own mutex, tracks in-flight reissues per replica keyed by parent, and rate-limits re-issuance per parent; the three reconcilers drive it via EnsureState/RemoveState/ShouldReissue. The device deletion helper is shared too, so the reissue state machine and its tests now live in one place. Updates #20544 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
The egressPodsReconciler (added in #14792) only sets the tailscale.com/egress-services readiness condition on egress ProxyGroup replica Pods that declare the corresponding readiness gate. However, the gate was never actually added to the egress Pod template, so the reconciler always hit its early-return and the readiness condition was never set. This commit adds said readiness gate to the egress Pod template. Updates #14326 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
authkey.go references tsclient.Client in getAuthKey but never imported the package, breaking main. Updates #20544 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
…20834) This commit changes how PeerRelay services are exposed on AWS. A Network Load Balancer only forwards to targets in an availability zone enabled on it, and spec.aws.elasticIPs pins each service to a single subnet, which enables just one zone. A replica scheduled anywhere else silently receives nothing while still reporting PeerRelayReady with an address in status.endpoints. Without spec.aws we now leave the subnet unpinned, so the AWS Load Balancer Controller spreads the load balancer over every zone it finds, and cross-zone load balancing is on by default so any of its addresses reach the pod. Hostname resolution is no longer gated on the eip-allocations annotation, which had left these unpinned services in EndpointsPending forever, and a failure to resolve now logs at debug since it is expected while a load balancer provisions. Such a load balancer has an address per zone, and AWS bills for each, so every one of them is now advertised rather than only the lowest sorted. That also lets a peer reach the relay when one zone is unreachable. status.endpoints gains address as a second list map key so a replica can hold an entry per address; no field changes, so existing readers of endpoints[].address keep working. Readiness counts the replicas that have an endpoint rather than the entries, so a replica with several addresses cannot mask one that has none. The pods now serve containerboot's health check endpoint and the load balancer is pointed at it over HTTP. A peer relay listens only on UDP, so the default TCP check against the port the load balancer forwards could never succeed and every target sat unhealthy while relaying perfectly well. /healthz reports 200 once the device has tailnet addresses, which is the condition that actually matters. The CRD docs now describe spec.aws as the exception, note that it also needs a ProxyClass pinning pods to the zone of the subnets it names, and drop the claim that an Elastic IP has an availability zone of its own. Fixes: #20833 Signed-off-by: David Bond <davidsbond93@gmail.com>
This commit modifies the "generate" tool we use on the kubernetes operator that produces helm chart and static manifest assets for CRDs. Previously, this required always remembering to add new constants to a `main.go` and did not have any mechanism to fail in CI if you forgot to. Now this tool will iterate over all the CRDs and ensures that they're in the places they're expected to be, with a test that will fail if they are not. This removes the requirement for remembering to add these constants every time you have a new CRD. Closes: #20594 Signed-off-by: David Bond <davidsbond93@gmail.com> Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
…cts (#20839) 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>
…ges (#19696) This commit moves the reconcilers for both the DNS nameserver and DNSConfig custom resource into their own packages within `k8s-operator/reconciler` Closes: tailscale/corp#37088 Signed-off-by: David Bond <davidsbond93@gmail.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>
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>
…ctually enabled on interface 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>
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>
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>
…ured We were checking and writing an HTTP error, but not returning. Updates tailscale/corp#39033 Signed-off-by: Michael Ben-Ami <mzb@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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.