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>
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: https://github.com/tailscale/tailscale/issues/20833
Signed-off-by: David Bond <davidsbond93@gmail.com>
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>
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>
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
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>
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>
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 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: https://github.com/tailscale/corp/issues/45981
Updates: https://github.com/tailscale/corp/issues/44443
Signed-off-by: David Bond <davidsbond93@gmail.com>
This commit adds a new `--base-image` flag to the e2e test suite
so that tests can build the operator and operator accessories
with a different base docker image. We want this so that we can
try things out with red hat's UBI as part of getting the operator
up and running on openshift clusters.
We can then modify the e2e test runner to use a matrix for normal
alpine base and redhat's.
Updates: https://github.com/tailscale/corp/issues/45981
Updates: https://github.com/tailscale/corp/issues/44443
Signed-off-by: David Bond <davidsbond93@gmail.com>
The Helm chart hardcoded the operator's Deployment, ServiceAccount,
Role, and RoleBinding names to "operator".
Adds a standard _helpers.tpl with name/fullname template functions and
use them for the operator's resources, including the oauth Secret and
the cluster-scoped ClusterRole/ClusterRoleBinding. When neither
nameOverride nor fullnameOverride is set, the fullname resolves to the
historical hardcoded names rather than the release name, so existing
installations upgrade with no resource renames; rendering with default
values is byte-identical to before.
Fixestailscale/tailscale#18232
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
The nameserver Deployment created for a DNSConfig was the only
operator-managed workload with no way to configure imagePullSecrets, so its
pods could not pull the nameserver image from a private registry.
Adds imagePullSecrets to NameserverPod and thread it through to the nameserver
Deployment pod spec.
Updates #16772
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
The Helm chart only applied imagePullSecrets to the operator Deployment's
pod spec, so proxy pods (which use a different ServiceAccount) never
received them and could not pull images from private registries.
Apply imagePullSecrets to the operator, proxies, and kube-apiserver-auth-proxy
ServiceAccounts.
Updates #16772
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit adds a new end-to-end test for the `PeerRelay` custom
resource.
This test is currently quite limited due to the fact that we are running
our tests within a kind cluster within github actions. This means it's
not really possible to give the peer relays a proper public IP address
via the `LoadBalancer` type services that we spin up.
That being said, we intend to expand our e2e test suite with actual
real clusters in future so this can be expanded upon at a later date.
For now, this test spins up a single and multi-replica deployment of
a peer relay and confirms that it has been registered with control
and is configured to act as a peer relay.
This test also caught a small bug where the server URL was not being
passed into the peer relay's configuration, which has been fixed here.
Closes: https://github.com/tailscale/corp/issues/45731
Closes: https://github.com/tailscale/corp/issues/45737
Signed-off-by: David Bond <davidsbond93@gmail.com>
PR #20388 (commit ca9f6971e) bumped k8s.io/* from v0.34 to v0.35 as a
transitive effect of pulling in helm v3.21 (for containerd CVE fixes).
The old controller-runtime v0.19.4 is aligned with k8s 1.31 APIs and
regresses at runtime against v0.35 client-go (informer reflectors stall
on Watch responses), leaving the operator unable to reconcile.
v0.23.3 is the release aligned with k8s.io/* v0.35 / Kubernetes 1.35.
The operator uses a narrow slice of controller-runtime (manager +
builder + client + reconcile + handler + source), so this bump is a
lift-and-shift: no reconciler wiring changes needed.
Test scaffolding fix: controller-runtime v0.20+ populates TypeMeta
(Kind/APIVersion) on objects returned by the fake client. The shared
expectEqual helper now strips TypeMeta before diffing so existing
tests continue to work without needing every 'want' object updated.
Tested: deployed to live clusters across Kubernetes 1.33, 1.34, 1.35,
and 1.36 (identical build per cluster). Verified the operator starts and
reconciles cleanly on all versions with no RBAC/forbidden errors, CRDs
establish and enforce validation, and the field indexers resolve
correctly under annotation churn (no stale-index misrouting). Exercised
the Service, ProxyClass, Connector, egress (simple + ProxyGroup, incl.
the EndpointSlice reconciler), and HA ingress-for-pg reconcilers.
Confirmed informer/watch recovery after an API server restart forces a
re-list/re-watch (the exact regression this bump fixes).
Updates tailscale/corp#44898
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
Go 1.27 requires this new v0.8.0-rc.1.
But staticcheck 0.8's SA4023 gets stricter and points out that
modifiedExternallyError and handleListenersAccept always return
non-nil errors, and that MonitorHealth's callers don't need a separate
nil check before errors.Is. Simplify all three call sites; no behavior
change.
But then a handful of other places that SA4023 is angry about are
wrong (because it's not considering build tags) and can't be addressed
by ignore directives (again not considering build tags), so we just
disable SA4023 for now.
Updates #20220
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I2fefe3b986b5798c2e01624a0e9820839d21a569
This patch adds examples of unmarshalling tslockjsonv1.LogResponse
and tslockjsonv1.StatusResponse to the documentation.
Updates #17613
Signed-off-by: Simon Law <sfllaw@tailscale.com>
This patch pulls the printing and JSON-encoding out of
feature/tailnetlock/tslockjsonv1 into their callers, so that this
package only handles type conversions.
In cmd/tailscale/cli/tailnet-lock.go, it extracts the
printTailnetLockStatus function from runTailnetLockStatus to mirror
printTailnetLockLog and runTailnetLockLog.
Updates #17613
Signed-off-by: Simon Law <sfllaw@tailscale.com>
This patch extracts the functions used to marshal the JSON output of
the `tailscale lock` subcommand.
Updates #17613
Signed-off-by: Simon Law <sfllaw@tailscale.com>
This patch exports the structs used to unmarshal the JSON output of
the `tailscale lock` subcommand.
Updates #17613
Change-Id: I615723ff9dbc631e452dbf6bbdeab7b97e96dbb3
Signed-off-by: Simon Law <sfllaw@tailscale.com>
This patch extracts the handling of the TKA types for the
`tailscale lock` subcommand into its own tka.go file.
Updates #17613
Signed-off-by: Simon Law <sfllaw@tailscale.com>
This patch extracts the JSON handling for the `tailscale lock`
subcommand from the jsonoutput package into its own tslockjsonv1
package.
Updates #17613
Signed-off-by: Simon Law <sfllaw@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>
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>
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>
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>
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>
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>
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>
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
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>
Depend on the tailscale/breakglass fork directly for its new
access-control flags. The fork renamed its module path so no replace
directive (disallowed here) is needed. Upstream gokrazy/breakglass
stays for the arm64 appliances.
Regenerate depaware manifests and nix flake hashes for the pkg/sftp
bump pulled in by the fork.
Updates #1866
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit contains the Kubernetes implementation of peer relays via the new `PeerRelay` CRD. It's a mega branch consisting of the commits of other PRs gone into this work:
1. https://github.com/tailscale/tailscale/pull/20211
2. https://github.com/tailscale/tailscale/pull/20329
3. https://github.com/tailscale/tailscale/pull/20423
4. https://github.com/tailscale/tailscale/pull/20503
An instance of the `PeerRelay` CRD deploys a `StatefulSet` of containerboot instances configured to advertise themselves as peer relays using the IP addresses configured via `LoadBalancer` services on each cloud provider (with some AWS specifics as it's less automatic than its competing cloud providers).
Per replica, a `LoadBalancer` type `Service` resource is provisioned and its IP address is used to configure the respective relay.
This has been tested with success in AWS, GCP & Azure and provides additional modification to `Service` resources via the CRD for any other kinds of deployment environments. It also contains some work that may appear to be duplication of what already exists within `cmd/k8s-operator` so we can start building an appropriate migration path for `Connector`, `ProxyGroup` etc into respective `k8s-operator/reconciler/*` packages.
Closes https://github.com/tailscale/corp/issues/34524
Go 1.27 enables GOEXPERIMENT=jsonv2 by default: encoding/json is now
backed by the json/v2 machinery, and github.com/go-json-experiment/json
compiles as a thin alias of the standard library's encoding/json/v2.
Several tag options and behaviors we relied on did not make the cut for
the final Go 1.27 API, breaking tailscaled at runtime and four packages'
tests. This change adapts to the final API while keeping the wire format
byte-for-byte identical on all Go versions.
First, the `format` tag option was demoted to experimental. Its mere
presence in a struct tag now makes marshaling and unmarshaling fail at
runtime. tailcfg.SSHAction.SessionDuration had `format:nano` (added in
a2dc517d7 to pin the v1 representation), so on Go 1.27 any netmap
containing an SSH policy failed to decode, breaking every PollNetMap.
Remove the option here and in net/speedtest; time.Duration still
marshals as int64 nanoseconds under encoding/json on all Go versions
(Go 1.27's v1 mode sets FormatDurationAsNano by default), so old
clients and servers are unaffected. Add a regression test locking in
the exact wire format.
Consequently, invert the cmd/vet jsontags rule: it previously required
an explicit `format` tag on time.Duration fields, which is now exactly
wrong. It now rejects any `format` tag option, which would have caught
this bug in CI.
Second, the `inline` tag option was renamed to `embed`. The standard
library silently ignores `inline`, while the pinned go-json-experiment
module (used on Go 1.26) only knows `inline`. Specify both options in
types/prefs and logtail; each implementation ignores the option it does
not know, producing identical output. Drop `inline` once we require
Go 1.27.
Third, encoding/json (v1) now dispatches to MarshalJSONTo and
UnmarshalJSONFrom methods and its v1 options flow into nested
jsonv2.MarshalEncode calls. Types whose v1 methods deliberately
routed through jsonv2 for v2 semantics (types/opt.Value, the
types/prefs preference types) would silently change wire format
(e.g. nil slices becoming null). Pin jsonv2.DefaultOptionsV2 in
their jsonv2 methods so the representation is the same regardless
of the entry point.
Finally, json.Marshal costs one more allocation under Go 1.27,
tripping the types/logger.AsJSON alloc test. Switch its fmt.Formatter
to jsonv2.MarshalWrite with explicit v1 options, which writes directly
to the fmt.State: one allocation on both toolchains with unchanged
output. Depaware files pick up the go-json-experiment/json/v1 options
shim as a new dependency of types/logger.
With this change, go test ./... passes with both Go 1.26.5 and
go1.27rc2.
Updates #20220Fixes#20528Fixes#20254
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I694c7d57fd81e55a579c579e9be10032bca569d4
The NetstackDialTCP/UDP hooks returned the result of DialContextTCP/UDP
directly, so on error they returned a non-nil net.Conn interface holding
a nil *gonet.TCPConn or *gonet.UDPConn pointer, tripping up callers that
check the interface against nil and then call Close, crashing the wasm
worker. Apply the same fix that 46bdbb387 made for tailscaled and tsnet.
Fixes#20529
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I4fd66bb7615ee9b2d204256a43288ed7b7a12f35
Simplifies cmd/containerboot env var parsing. Most of the private helpers did
not earn their abstraction: defaultEnv(name, "") is just os.Getenv(name), and
the rest collapse into cmp.Or and the existing def.Bool. defaultEnv,
defaultEnvs and defaultBool are gone.
Adds def.LookupEnv, the env companion to def.Bool, for the one case that needs
it: TS_KUBE_SECRET, where an explicit "" disables Kubernetes secret storage and
must stay distinct from unset (cmp.Or cannot express that).
Updates #20018
Signed-off-by: Nick Rossi <nrossi0530@gmail.com>