The suggest-exit-node LocalAPI response omits the Location field when
the suggested exit node has no location, such as a regular tailnet exit
node. Building the exit node menu called CountryCode and City on the
invalid view unconditionally, crashing the app on startup. The crash
was introduced in aa21b0c00 (#19627), which added those calls.
Add an integration test that starts the systray app against a private
dbus session bus, a fake StatusNotifierWatcher, and a fake LocalAPI
serving such a suggestion, then verifies the StatusNotifierItem is
registered and the initial menu is built. On Linux the systray is pure
dbus (StatusNotifierItem and dbusmenu), so no X server or desktop
environment is needed and the test runs in existing CI.
Fixes#20678
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I262ae0f724cb7c41aa0f531fcbded967e5bee432
Implements a way to send TSMPDiscoAdverts based on a trigger from
wireguard-go when a rekey happens. This lets us distribute disco keys
consistently, but also sets us up for a minimal message that can be
distributed to other clients.
A benchmark is implemented to make it easier to keep the call cheap and
to avoid locking up anything in wireguard-go.
Updates #20081
Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This commit moves the proxyclass reconciliation logic from its original
home in the rather large main package of the k8s-operator to a dedicated
package within `k8s-operator/reconciler/proxyclass`.
Closes: https://github.com/tailscale/corp/issues/37085
Signed-off-by: David Bond <davidsbond93@gmail.com>
The previous commit sent CHANNEL_EOF from the stdout copier as soon as
stdout hit EOF. With the process exited and the send window exhausted by
a slow client, the stderr copier could still be draining its backlog;
x/crypto/ssh fails every write after EOF (WriteExtended checks sentEOF),
so the tail of stderr was silently dropped.
Send CloseWrite from run() itself, after cmd.Wait, exit-status and both
output copiers have finished. Wire order is unchanged: exit-status,
remaining output, EOF, CHANNEL_CLOSE.
The test withholds stderr window credit until the process has exited
server-side, then releases it and checks the final bytes arrived. It
also pins the harness login shell to /bin/sh; fish forks -c commands and
stays resident, changing pipe fd ownership under test.
Updates #18256
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Send exit-status before EOF before CHANNEL_CLOSE per RFC 4254 §6.10.
The old code raced CloseWrite against Exit; macOS OpenSSH often saw
EOF first and dropped the exit code.
Replace atomic.Bool/atomic.Int32/channel sync with a sync.WaitGroup
over the output goroutines and emit:
cmd.Wait -> ss.Exit -> closeAll(childPipes) -> wg.Wait -> ss.Close
The stdin copier stays out of wg: it blocks reading from the SSH
channel until the client half-closes, and many clients (go-scp,
plain `ssh host cmd`, scp, sftp) don't half-close before they
receive CHANNEL_CLOSE from us. Including it deadlocks. It self-
cleans via deferred ss.Close.
Align exit codes with shell convention:
255 SSH-internal error (OpenSSH ssh.c:1693)
254 recording-infrastructure failure
127 command-not-found (POSIX)
Switch session termination from SIGKILL to SIGHUP, matching
OpenSSH PTY-master-close (session.c:2246). Process-group delivery
follows in a later commit.
Relies on the gliderssh wire-order change (tailscale/gliderssh#8),
which go.mod already pins since 3f5eb3199.
Based on tailscale/tailscale#18331.
Updates #18256
Signed-off-by: James Tucker <james@tailscale.com>
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Added before the fix so CI records the failure. Frame order is asserted
off the session interface rather than through a client, so it holds on
any GOOS instead of depending on how a particular client reacts.
Measured on CI against this commit, no fix yet:
TestExitStatusPrecedesEOF FAIL on darwin, linux/amd64 and the
privileged linux job, 10/10 retries, classified permanent:
got [eof exit-status], want [exit-status eof close]
TestExitCodePassthrough PASS on both
TestStderrTailNotTruncated PASS on both (guards a 2022 fix)
The ordering assertion is the only thing that pins this bug. Exit-code
assertions do not: OpenSSH 10.2p1 on darwin reads the pending
exit-status request after EOF just as linux does, so the wrong frame
order is invisible to the client's exit status over the direct-exec
path. The exit-status loss reported in #18256 comes from
/usr/bin/login -pq in the incubator path, which is a separate defect.
Updates #18256
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Extract the in-process SSH server harness out of tailssh_test.go into
tailssh_exitcodes_test.go so the exit-status tests can share it, and add
the integrationtest-tagged exit-code suite (Go and OpenSSH clients).
Updates #18256
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
By default the controlhttp dialer tries the plaintext port 80 upgrade
path first with a port 443 TLS fallback, so it's not deterministic
which of the two server code paths a debugging session exercises. Add
a --force-port flag to pin the noise connection to one or the other:
443 uses the dialer's existing TS_FORCE_NOISE_443 knob, and both
values are also enforced at the dial layer so the other port can't be
used by a silent fallback.
Updates tailscale/corp#29053
Change-Id: I5641db9bd2083d458ba7e0e766c7f9da535ba3e0
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Add a flag to dial a specific IP for the noise connection while
keeping the --host value for the /key fetch, TLS SNI, and Host
header. This permits testing a single control plane frontend (such
as a staging or canary trunkd instance with no DNS record) end to
end with a real ts2021 upgrade and noise handshake.
Updates tailscale/corp#29053
Change-Id: I8cd0208d6b01219eabd33f8fdd77cc490fffe29e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
There are two places in the code where we need to compare the
latencies of a netcheck report. In both cases, the comparison wasn’t
well tested.
This PR extracts that logic into a Compare method of the new
RegionLatency type. This type wraps the map of latency measurements
keyed by region ID.
Updates #cleanup
Change-Id: I7f248988973007c2f452283c1b82f84b03068f77
Signed-off-by: Simon Law <sfllaw@tailscale.com>
When running k8s operator e2e tests against real tailnets, use Let's
Encrypt's staging environment to avoid production rate limits.
Updates tailscale/corp#45571
Signed-off-by: Becky Pauley <becky@tailscale.com>
This commit adds 3 end-to-end tests to the suite for the
operator's `Connector` resources. Connectors are used
to deploy subnet routers, exit nodes & app connectors
within a Kubernetes cluster.
One test exists for each respective mode of the resource
and assertions are made against control that the machine
is running in the expected mode using the device identifier
within the pod's state secret.
Closes: https://github.com/tailscale/corp/issues/38025
Signed-off-by: David Bond <davidsbond93@gmail.com>
The session recording WebSocket parser rejected frames whose extended
payload length field had not fully arrived yet, tearing down 'kubectl
exec' sessions whenever a read from the network delivered only the
first two or three bytes of a frame header. Every data frame carrying
126 or more bytes of payload uses an extended length field, so bulk
stdin (such as piping a file into a pod through the API server proxy)
failed with near-certainty on large transfers, while small interactive
sessions were rarely affected.
Teach fragmentDimensions to report an incomplete frame header
distinctly, so that both the data and control frame paths buffer and
wait for more bytes instead of erroring out and killing the session.
Add tests covering both extended length forms and control frames being
split at every boundary within the frame header, into two and three
segments, on both the read (client to server) and write (server to
client) paths, plus a bulk stdin passthrough test approximating how
'kubectl exec' frames and chunks a large stdin stream.
Updates tailscale/corp#39583
Change-Id: I9c28a17bc6bee898abfa2af2519c99fff9144217
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
The MagicDNS resolver answered NXDOMAIN and no-data queries with no
SOA record in the authority section. RFC 2308 says such responses
should not be cached, but some resolvers cache them anyway on their
own schedule: macOS's mDNSResponder seems to cache them for a really
long time, so a name queried shortly before a node was renamed to it
didn't start resolving until something flushed the cache, such as
toggling Tailscale off and on.
Attach the zone's SOA record to negative responses for domains we're
authoritative for, advertising a 10 second negative-caching TTL. The
SOA serial is the response time in unix seconds; nothing consumes it
(no secondaries, no zone transfers), but it's at least monotonic.
Also lower the TTL of positive answers from 600 seconds to 5. The
source of truth is local and in-memory, so re-queries are nearly
free, while anything cached downstream delays clients noticing node
renames for the full TTL.
Updates tailscale/corp#45631
Change-Id: Ifb73874c8724f7d95ad7950dcb481f009acab9a4
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
not configured
We want all hooks and handlers to do as little as possible if the node
is not configured for conn25 or has no configured apps.
Updates tailscale/corp#39033
Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
Add end-to-end tests for egress to a tailnet target via its FQDN, IPv4 and
IPv6 addresses.
Update the kind cluster used in tests to support dual-stack (to allow
testing egress to both IPv4 and IPv6 tailnet targets). For pre-existing
clusters, detect on test setup which IP families the cluster supports.
IPv4/IPv6 subtests are skipped if not supported.
Run the end-to-end tests in parallel to improve the speed of test runs. To
enable this, run TestProxyGroupPolicy in a dedicated namespace so its
ValidatingAdmissionPolicies don't affect other parallel tests sharing the
same namespace.
Updates tailscale/corp#34834
Signed-off-by: Becky Pauley <becky@tailscale.com>
InterfaceIPDisappeared is documented as true when an address existed in the
old network state and is absent from the new network state. The
non-nil-state predicate was reversed, so it reported addresses that
appeared instead.
Fix the predicate and add table coverage for disappeared, appeared,
unchanged, and unknown-state cases.
Fixes#20614
Change-Id: Ieb7a8c5e13d23e868e3ffe0b99e20fb14ba2245e
Signed-off-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
When a node is renamed in the admin console, control sends peers a
single MapResponse delta: a PeersChanged entry carrying the full
updated node with its new Name, and no new DNSConfig (MagicDNS
records are computed client-side from peer names). That arrives as a
NodeMutationUpsert, but nodeBackend's upsert path only added the new
node's index entries and never removed the replaced node's, so
nodeByName retained the old name, and nodeByAddr, nodeByKey, and
nodeByStableID could likewise go stale if those fields changed.
Since 7e609b258 the quad-100 resolver serves MagicDNS answers on
demand from those live indexes, so a renamed peer's old name kept
resolving until something rebuilt the indexes from a full netmap,
such as toggling Tailscale off and on.
Evict the replaced node's index entries before adding the new ones.
Also consolidate the natlab DNS coverage into a single TestMagicDNS
that boots one VM and exercises extra records, search domains, and
peer add/rename/remove end to end, injecting the same MapResponse
shapes that production control sends.
Updates tailscale/corp#45631
Change-Id: I8a418317d930ec8ce112f7bd19bfd5778117a65e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Building a Set from a views.Slice previously required set.Of(v.AsSlice()...),
which allocates an intermediate slice copy before allocating the set. Add
OfSliceView and AddSliceView to populate a set directly from the view,
mirroring the existing AddSlice/AddSeq/AddSet family.
Updates tailscale/corp#45499
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I3f2a9d417c60be8e5f1acd42708e2f9a4d6c1b7e
This patch extracts all the DNS related JSON handling from the
cmd/tailscale/jsonoutput package into a new tsdnsjsonv0 package.
It adds package documentation for tsdnsjsonv0 with a big WARNING that
this is an unstable format with no backwards compatibility guarantees.
When we stabilize this format, we should spin off a new tsdnsjsonv1
package that uses jsonoutput.ResponseEnvelope to declare version 1.
Updates #13326
Updates #18750
Signed-off-by: Simon Law <sfllaw@tailscale.com>
Flatten the cmd/tailscale package hierarchy by extracting the
jsonoutput package out of the cmd/tailscale/cli package.
Updates #cleanup
Change-Id: I92f80db75b0328e82f1596b6a42f6f6ef5a94bfa
Signed-off-by: Simon Law <sfllaw@tailscale.com>
Reader.Close set r.store to nil without holding r.mu, while reload read
r.store while holding r.mu. If a policy store is closed while a
concurrent reload is in flight, reload could observe a nil store and
crash tailscaled with a nil interface method call in
readPolicySettingValue.
Nil out r.store only while holding r.mu, and make reload return the
last known policy once the reader is closing instead of reading from
a store that may no longer exist.
Fixestailscale/corp#45548Fixestailscale/triage#394
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I494cfe9ea1df67b563bc061db9e6944f87b42a4e
The fake ticker has a one-element channel buffer and drops ticks when
the probe loop goroutine isn't already blocked on the channel, so
advancing the fake clock 50 times in a tight loop didn't guarantee
that the loop observed enough ticks to start three concurrent probe
runs. Under CI load, only two of the three run goroutines could be
spawned before the convergence timeout expired.
Advance the clock inside the polling loop instead, so ticks keep
firing until all three probe goroutines have started. Verified with
flakestress: the old test failed within ~41k runs, while the fixed
test passed 175,214 runs with no failures.
See http://flakes/analyze-test?name=tailscale.com%2Fprober.TestProberConcurrency
Updates #deflake
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I673a4918bbb5fea6b650e0dc1bc491c4af922b19
Files whose names contain characters with Unicode decompositions (such
as umlauts or voiced kana) could not be opened or written over
Taildrive.
Background: keyboards and IMEs emit NFC (precomposed) characters on
every platform, so filenames on Linux (ext4 etc) and Windows (NTFS)
disks are usually NFC bytes. NFD (decomposed) names mostly come from
Apple software: HFS+ forced a variant of NFD on write, and Apple's
frameworks still decompose paths via fileSystemRepresentation. APFS
preserves whatever bytes it is given but does normalization-insensitive
lookups (it stores a hash of the normalized name), so canonically
equivalent names find the same file. ext4 and NTFS lookups, by
contrast, are byte-exact.
On the wire, the macOS WebDAV client sends paths in NFD form (they
pass through the decomposing file system representation, and unlike
Apple's NFS client there is no "nfc" mount option). Windows and Linux
WebDAV clients pass names through as the application provided them,
typically NFC. WebDAV itself mandates no normalization, and PROPFIND
hrefs reflect the server's on-disk bytes.
The two forms are canonically equivalent but byte-wise different, so a
macOS client requesting the NFD form of an NFC-named file on a Linux
or Windows host got a 404 from the exact-byte lookup. Even against an
APFS host, where the filesystem absorbs the mismatch, the client-side
StatCache could still infer a 404: a cached directory listing in one
form caused depth 0 PROPFINDs in the other form to be treated as not
found without ever reaching the server. The inverse direction (NFD
bytes on a Linux disk, copied there from a Mac, requested in NFC form
by a Windows or Linux client) was broken too.
Alternative regimes considered: normalizing names at storage time (as
Nextcloud and Syncthing's autoNormalize do) would rename user files in
shared directories as a side effect of serving them; normalizing
request paths to a fixed form on the wire is unsound because the
on-disk form is unknowable a priori (ext4 can hold either form, or
both). Instead, adopt the APFS model: preserve bytes, but make lookups
normalization-insensitive.
Concretely, wrap the remote file server's webdav.Dir in a
normalizingFS that, when an exact path lookup fails, rescans the
parent directory for an entry whose name is canonically equivalent,
comparing the NFC form of both sides (which also sidesteps Apple's
nonstandard decomposition tables). Exact matches always win, and newly
created files keep the exact bytes the client sent. Also NFC-normalize
StatCache keys so canonically equivalent names share a cache entry.
The change is covered at three levels: unit tests for the StatCache,
an in-process two-node test in drive/driveimpl, and a new TestTaildrive
VM integration test in tstest/natlab/vmtest that shares a directory
between two Ubuntu VMs and exercises the NFC/NFD cases over the real
stack with curl playing the part of a macOS WebDAV client.
Fixes#15020
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I9c2f157e604efc629828581e08d5b3191dbb7d4e
TestContainerBoot/kube_shutdown_during_state_write flaked with exit
code 1 instead of 0 when SIGTERM arrived while "tailscale up" was
still running. Two problems combined:
tailscaleUp and tailscaleSet wrapped errors with %v, flattening the
error chain, so main's errors.Is(err, context.Canceled) check could
not recognize a graceful shutdown.
Even with %w, cmd.Run under a canceled context usually reports the
death of the killed subprocess ("signal: killed") rather than the
context error that caused it, since Wait prefers the process error.
Check ctx.Err() explicitly and return it (wrapped with %w) so that
a shutdown-driven cancellation is recognized wherever it lands
relative to the subprocess lifetime.
Before: the exit-code failure reproduced 4 times in 808 stress runs
under CPU starvation. After: 0 in 1195 runs.
Fixes#19380
Change-Id: Ie15ca722d2d5ac2a3f79b2d0ab01fb71d4b9220d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Introduce a per-tailnet shared ACME account key so that all ingress
ProxyGroup replicas on a tailnet present the same account identity to
Let's Encrypt. This lets renewals claim the ARI "replaces" exemption
from the 50-certs-per-week rate limit, surviving Pod restarts,
ProxyGroup recreation, and cluster migrations.
The operator provisions a "tailscale-acme-accounts" Secret in its
namespace, guarded by a finalizer and a deletion warning event, and
watched so it is recreated promptly if removed. Proxies migrate any
pre-existing per-pod key into the shared Secret on first boot, adopt
the shared key on subsequent boots, and restore it on cert writes if
the Secret was recreated empty. Certs are stamped with the fingerprint
of the issuing account so renewals skip the "replaces" claim when the
account doesn't match.
Opt-in per-ProxyGroup via the tailscale.com/share-acme-account
annotation, or operator-wide via OPERATOR_SHARED_ACME_ACCOUNT_KEY.
Updates #18251
Updates #20288
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
Changing the name to "TailnetLockKey" would be clearer but introduces
more risk; this is an easy and low-stakes improvement.
Updates tailscale/corp#37904
Change-Id: I38d804202538b8670a80e744eb4dcb689f0002df
Signed-off-by: Alex Chan <alexc@tailscale.com>
I wrote this function two hours ago, tried to use it in corp, and
immediately found myself confused about the meaning of the arguments.
Time for named parameters!
Updates tailscale/corp#40404
Change-Id: Ic2866e052ccc9f6361b8d529233df54d63abbaa1
Signed-off-by: Alex Chan <alexc@tailscale.com>
We previously identified sync failures that occur when a node falls behind
the remote, and compacts away most its local state. We fixed the underlying
issue in #19444, but that PR only tested the basic scenario where the
local chain is a direct ancestor of the remote chain.
This patch adds an explicit regression test for the case where a node is on
a fork (that is, its HEAD is not part of the remote's active chain).
Although #19444 happened to cover this case, other proposed patches did not
handle the forked state. Adding this test locks in the behaviour and prevents
future sync regressions in this area.
Also, add a shared helper for writing this sort of TKA sync test.
Updates tailscale/corp#40404
Change-Id: I78fdc6beaf71392edf11806197f126db48886f93
Signed-off-by: Alex Chan <alexc@tailscale.com>
Add a large blob check to the pre-push hook, using the same git tree
diff logic as corp's check-file-size CI workflow (the
check-git-accidental-large-file GitHub Action): diff the pushed tree
against the remote's old tree (or the merge base with the remote's
default branch for new refs) and reject any new or changed blob over
1.5 MB. Unlike the CI check, which only guards PRs into main, the hook
runs before pushing to any branch, catching mistakes before they
permanently bloat the remote repo.
Set TS_SKIP_LARGE_FILE_CHECK=1 to push a large file intentionally,
mirroring the skip-large-file-check commit message tag honored by CI.
This folds the go.mod replace check and the new check into a single
CheckPrePush entry point so both share one read of the hook's stdin;
corp's git-hook.go needs the matching call site update when it next
bumps its tailscale.com dependency.
Updates tailscale/corp#9863
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I1c8cf2a277ce854d45c0ea809bed7c06b3295374
We were early-returning when the node was using an exit node, before
Connectors 2025 split DNS routes were calculated and installed.
Now we assemble the routes first, then install them in both exit node
and non-exit-node contexts. The returned resolvers set UseWithExitNode
to true even though as of today, we believe they should be installed in
all cases without regard to that boolean value. With the boolean, we
preserve the flexibility to toggle behavior without touching ipnlocal.
We also add a TODO to turn the extra split DNS route gathering into a
feature hook (tailscale/corp#37125).
This does not affect appc connectors, which receive split DNS routes,
and the UseWithExitNode value directly from control.
Updates #16384
Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit modifies the generation command for the kubernetes operator
to include the CRD for peer relays in the helm chart and static
manifests
Updates: #fixup
Signed-off-by: David Bond <davidsbond93@gmail.com>
Previously there was a mismatch between how nodes store AUMs and what
the control plane would offer during sync:
- Client compaction: Nodes aggressively compact their TKA state -- they
keep the last 24 AUMs, every AUM received in the last two weeks, and
then everything from there back to the last checkpoint. Depending on
when it compacts, a node may only have ~50 AUMs.
- Exponential sampling: To save bandwidth, the control plane would send
a SyncOffer containing ancestors at exponentially increasing intervals
(4th, 16th, 64th, 256th...).
If a node has been offline for too long, the exponential sampling skips
the node's smaller window. When the SyncOffer and local state are disjoint,
the node cannot find a common ancestor to use for synchronisation.
It enters a failure loop where it keeps polling for new TKA state, but
it cannot catch up and has an increasingly-outdated view of the tailnet.
This patch replaces the exponential sampling with a SyncOffer that sends
every checkpoint ancestor of the current HEAD. Since every node is
guaranteed to keep at least one checkpoint after compaction, we're more
likely to have an intersection for the sync process.
This patch also increases `maxSyncHeadIntersectionIter`, which in
practice means the control plane will send every checkpoint in the
current chain. This means all affected nodes will be able to find an
intersection and catch up immediately, without requiring a client update.
It's still possible for a node to be unable to sync, but these edge cases
become less likely with this change. (For example, if a node is 1000+ AUMs
behind, or if it creates a local branch and then compacts away the
intersection with the main chain.)
This patch includes a regression test with synthetic data, and I
verified the fix with customer data.
Updates https://github.com/tailscale/corp/issues/40404
Change-Id: I2174011bb23a2b5972f6d1591aadcc016e3cba35
Signed-off-by: Alex Chan <alexc@tailscale.com>
We have some client builds on the unstable track where the conn25 code
doesn't run if the TAILSCALE_USE_WIP_CODE env var is not set. But the
split DNS routes for conn25 configured domains do get installed. This
means that users running those builds would get traffic for configured
domains black holed if the env var is not set.
This issue was fixed in 425a916ce.
Bump tailcfg.CapabilityVersion, and then a corresponding change to the
control server to not send conn25 config to lower versions will
avoid this issue for those users.
Updates tailscale/corp#45363
Signed-off-by: Fran Bull <fran@tailscale.com>
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>
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
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
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>
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>
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>
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>