From 0b36774ff9e8d5a82efee38e3734298ffb453ae9 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 21 May 2025 15:10:03 -0400 Subject: [PATCH 01/11] VERSION.txt: this is v1.84.0 (#16041) Signed-off-by: Jonathan Nobels --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 6b4de0a42b03c..bd0f9e6c28f77 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.83.0 +1.84.0 From 914acdc8b4a4ccf4f3f2f329bd8223aa80b87c5c Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 28 May 2025 10:08:06 -0400 Subject: [PATCH 02/11] ipn: set RouteAll=true by default for new accounts on iOS and Android (#16110) fixes tailscale/tailscale#16082 RouteAll should be true by default on iOS and Android. Signed-off-by: Jonathan Nobels (cherry picked from commit 842df378037579f35f996c9f3bb89dc53ba8e720) --- ipn/prefs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ipn/prefs.go b/ipn/prefs.go index caf9ccfc3af09..01275a7e25bdc 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -721,9 +721,10 @@ func (p *Prefs) ControlURLOrDefault() string { // of the platform it's running on. func (p *Prefs) DefaultRouteAll(goos string) bool { switch goos { - case "windows": + case "windows", "android", "ios": return true case "darwin": + // Only true for macAppStore and macsys, false for darwin tailscaled. return version.IsSandboxedMacOS() default: return false From c417248c1fa3f99156aeea7ec2276a38487dff60 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 28 May 2025 15:43:12 -0400 Subject: [PATCH 03/11] net/dns: cache dns.Config for reuse when compileConfig fails (#16059) fixes tailscale/corp#25612 We now keep track of any dns configurations which we could not compile. This gives RecompileDNSConfig a configuration to attempt to recompile and apply when the OS pokes us to indicate that the interface dns servers have changed/updated. The manager config will remain unset until we have the required information to compile it correctly which should eliminate the problematic SERVFAIL responses (especially on macOS 15). This also removes the missingUpstreamRecovery func in the forwarder which is no longer required now that we have proper error handling and recovery manager and the client. Signed-off-by: Jonathan Nobels (cherry picked from commit 5e54819ceecd789bce87c42b09b632932931d794) --- net/dns/manager.go | 46 ++++++++++------------------ net/dns/manager_test.go | 56 +++++++++++++++++++++++++++++++++-- net/dns/resolver/forwarder.go | 26 ++++------------ net/dns/resolver/tsdns.go | 9 ------ 4 files changed, 76 insertions(+), 61 deletions(-) diff --git a/net/dns/manager.go b/net/dns/manager.go index 64bf12c6b1c26..5d6f225ce032f 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -25,7 +25,6 @@ import ( "tailscale.com/net/netmon" "tailscale.com/net/tsdial" "tailscale.com/syncs" - "tailscale.com/tstime/rate" "tailscale.com/types/dnstype" "tailscale.com/types/logger" "tailscale.com/util/clientmetric" @@ -63,10 +62,8 @@ type Manager struct { knobs *controlknobs.Knobs // or nil goos string // if empty, gets set to runtime.GOOS - mu sync.Mutex // guards following - // config is the last configuration we successfully compiled or nil if there - // was any failure applying the last configuration. - config *Config + mu sync.Mutex // guards following + config *Config // Tracks the last viable DNS configuration set by Set. nil on failures other than compilation failures or if set has never been called. } // NewManagers created a new manager from the given config. @@ -93,22 +90,6 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, health *health.Tracker, goos: goos, } - // Rate limit our attempts to correct our DNS configuration. - // This is done on incoming queries, we don't want to spam it. - limiter := rate.NewLimiter(1.0/5.0, 1) - - // This will recompile the DNS config, which in turn will requery the system - // DNS settings. The recovery func should triggered only when we are missing - // upstream nameservers and require them to forward a query. - m.resolver.SetMissingUpstreamRecovery(func() { - if limiter.Allow() { - m.logf("resolution failed due to missing upstream nameservers. Recompiling DNS configuration.") - if err := m.RecompileDNSConfig(); err != nil { - m.logf("config recompilation failed: %v", err) - } - } - }) - m.ctx, m.ctxCancel = context.WithCancel(context.Background()) m.logf("using %T", m.os) return m @@ -117,7 +98,7 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, health *health.Tracker, // Resolver returns the Manager's DNS Resolver. func (m *Manager) Resolver() *resolver.Resolver { return m.resolver } -// RecompileDNSConfig sets the DNS config to the current value, which has +// RecompileDNSConfig recompiles the last attempted DNS configuration, which has // the side effect of re-querying the OS's interface nameservers. This should be used // on platforms where the interface nameservers can change. Darwin, for example, // where the nameservers aren't always available when we process a major interface @@ -127,14 +108,14 @@ func (m *Manager) Resolver() *resolver.Resolver { return m.resolver } // give a better or different result than when [Manager.Set] was last called. The // logic for making that determination is up to the caller. // -// It returns [ErrNoDNSConfig] if the [Manager] has no existing DNS configuration. +// It returns [ErrNoDNSConfig] if [Manager.Set] has never been called. func (m *Manager) RecompileDNSConfig() error { m.mu.Lock() defer m.mu.Unlock() - if m.config == nil { - return ErrNoDNSConfig + if m.config != nil { + return m.setLocked(*m.config) } - return m.setLocked(*m.config) + return ErrNoDNSConfig } func (m *Manager) Set(cfg Config) error { @@ -154,15 +135,15 @@ func (m *Manager) GetBaseConfig() (OSConfig, error) { func (m *Manager) setLocked(cfg Config) error { syncs.AssertLocked(&m.mu) - // On errors, the 'set' config is cleared. - m.config = nil - m.logf("Set: %v", logger.ArgWriter(func(w *bufio.Writer) { cfg.WriteToBufioWriter(w) })) rcfg, ocfg, err := m.compileConfig(cfg) if err != nil { + // On a compilation failure, set m.config set for later reuse by + // [Manager.RecompileDNSConfig] and return the error. + m.config = &cfg return err } @@ -174,9 +155,11 @@ func (m *Manager) setLocked(cfg Config) error { })) if err := m.resolver.SetConfig(rcfg); err != nil { + m.config = nil return err } if err := m.os.SetDNS(ocfg); err != nil { + m.config = nil m.health.SetUnhealthy(osConfigurationSetWarnable, health.Args{health.ArgError: err.Error()}) return err } @@ -355,7 +338,10 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig // that as the forwarder for all DNS traffic that quad-100 doesn't handle. if isApple || !m.os.SupportsSplitDNS() { // If the OS can't do native split-dns, read out the underlying - // resolver config and blend it into our config. + // resolver config and blend it into our config. On apple platforms, [OSConfigurator.GetBaseConfig] + // has a tendency to temporarily fail if called immediately following + // an interface change. These failures should be retried if/when the OS + // indicates that the DNS configuration has changed via [RecompileDNSConfig]. cfg, err := m.os.GetBaseConfig() if err == nil { baseCfg = &cfg diff --git a/net/dns/manager_test.go b/net/dns/manager_test.go index 2bdbc72e26093..522f9636abefe 100644 --- a/net/dns/manager_test.go +++ b/net/dns/manager_test.go @@ -4,6 +4,7 @@ package dns import ( + "errors" "net/netip" "runtime" "strings" @@ -24,8 +25,9 @@ type fakeOSConfigurator struct { SplitDNS bool BaseConfig OSConfig - OSConfig OSConfig - ResolverConfig resolver.Config + OSConfig OSConfig + ResolverConfig resolver.Config + GetBaseConfigErr *error } func (c *fakeOSConfigurator) SetDNS(cfg OSConfig) error { @@ -45,6 +47,9 @@ func (c *fakeOSConfigurator) SupportsSplitDNS() bool { } func (c *fakeOSConfigurator) GetBaseConfig() (OSConfig, error) { + if c.GetBaseConfigErr != nil { + return OSConfig{}, *c.GetBaseConfigErr + } return c.BaseConfig, nil } @@ -1019,3 +1024,50 @@ func upstreams(strs ...string) (ret map[dnsname.FQDN][]*dnstype.Resolver) { } return ret } + +func TestConfigRecompilation(t *testing.T) { + fakeErr := errors.New("fake os configurator error") + f := &fakeOSConfigurator{} + f.GetBaseConfigErr = &fakeErr + f.BaseConfig = OSConfig{ + Nameservers: mustIPs("1.1.1.1"), + } + + config := Config{ + Routes: upstreams("ts.net", "69.4.2.0", "foo.ts.net", ""), + SearchDomains: fqdns("foo.ts.net"), + } + + m := NewManager(t.Logf, f, new(health.Tracker), tsdial.NewDialer(netmon.NewStatic()), nil, nil, "darwin") + + var managerConfig *resolver.Config + m.resolver.TestOnlySetHook(func(cfg resolver.Config) { + managerConfig = &cfg + }) + + // Initial set should error out and store the config + if err := m.Set(config); err == nil { + t.Fatalf("Want non-nil error. Got nil") + } + if m.config == nil { + t.Fatalf("Want persisted config. Got nil.") + } + if managerConfig != nil { + t.Fatalf("Want nil managerConfig. Got %v", managerConfig) + } + + // Clear the error. We should take the happy path now and + // set m.manager's Config. + f.GetBaseConfigErr = nil + + // Recompilation without an error should succeed and set m.config and m.manager's [resolver.Config] + if err := m.RecompileDNSConfig(); err != nil { + t.Fatalf("Want nil error. Got err %v", err) + } + if m.config == nil { + t.Fatalf("Want non-nil config. Got nil") + } + if managerConfig == nil { + t.Fatalf("Want non nil managerConfig. Got nil") + } +} diff --git a/net/dns/resolver/forwarder.go b/net/dns/resolver/forwarder.go index 321401a843e4e..c87fbd5041a93 100644 --- a/net/dns/resolver/forwarder.go +++ b/net/dns/resolver/forwarder.go @@ -245,12 +245,6 @@ type forwarder struct { // /etc/resolv.conf is missing/corrupt, and the peerapi ExitDNS stub // resolver lookup. cloudHostFallback []resolverAndDelay - - // missingUpstreamRecovery, if non-nil, is set called when a SERVFAIL is - // returned due to missing upstream resolvers. - // - // This should attempt to properly (re)set the upstream resolvers. - missingUpstreamRecovery func() } func newForwarder(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkSelector, dialer *tsdial.Dialer, health *health.Tracker, knobs *controlknobs.Knobs) *forwarder { @@ -258,13 +252,12 @@ func newForwarder(logf logger.Logf, netMon *netmon.Monitor, linkSel ForwardLinkS panic("nil netMon") } f := &forwarder{ - logf: logger.WithPrefix(logf, "forward: "), - netMon: netMon, - linkSel: linkSel, - dialer: dialer, - health: health, - controlKnobs: knobs, - missingUpstreamRecovery: func() {}, + logf: logger.WithPrefix(logf, "forward: "), + netMon: netMon, + linkSel: linkSel, + dialer: dialer, + health: health, + controlKnobs: knobs, } f.ctx, f.ctxCancel = context.WithCancel(context.Background()) return f @@ -962,13 +955,6 @@ func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, respo f.health.SetUnhealthy(dnsForwarderFailing, health.Args{health.ArgDNSServers: ""}) f.logf("no upstream resolvers set, returning SERVFAIL") - // Attempt to recompile the DNS configuration - // If we are being asked to forward queries and we have no - // nameservers, the network is in a bad state. - if f.missingUpstreamRecovery != nil { - f.missingUpstreamRecovery() - } - res, err := servfailResponse(query) if err != nil { return err diff --git a/net/dns/resolver/tsdns.go b/net/dns/resolver/tsdns.go index 107740b136d54..33fa9c3c07d4c 100644 --- a/net/dns/resolver/tsdns.go +++ b/net/dns/resolver/tsdns.go @@ -251,15 +251,6 @@ func New(logf logger.Logf, linkSel ForwardLinkSelector, dialer *tsdial.Dialer, h return r } -// SetMissingUpstreamRecovery sets a callback to be called upon encountering -// a SERVFAIL due to missing upstream resolvers. -// -// This call should only happen before the resolver is used. It is not safe -// for concurrent use. -func (r *Resolver) SetMissingUpstreamRecovery(f func()) { - r.forwarder.missingUpstreamRecovery = f -} - func (r *Resolver) TestOnlySetHook(hook func(Config)) { r.saveConfigForTests = hook } func (r *Resolver) SetConfig(cfg Config) error { From 72ec2811bfa0157ba406f5631cc5d7c1a3b692bc Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Thu, 29 May 2025 13:40:49 -0400 Subject: [PATCH 04/11] VERSION.txt: this is v1.84.1 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index bd0f9e6c28f77..1a17bdc1710f0 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.84.0 +1.84.1 From f1b8c4a4d3bc6ac6a1c9bdc21b6d23aaa04f1570 Mon Sep 17 00:00:00 2001 From: Irbe Krumina Date: Fri, 30 May 2025 14:21:58 +0100 Subject: [PATCH 05/11] cmd/containerboot: allow setting --accept-dns via TS_EXTRA_ARGS again (#16129) (#16140) In 1.84 we made 'tailscale set'/'tailscale up' error out if duplicate command line flags are passed. This broke some container configurations as we have two env vars that can be used to set --accept-dns flag: - TS_ACCEPT_DNS- specifically for --accept-dns - TS_EXTRA_ARGS- accepts any arbitrary 'tailscale up'/'tailscale set' flag. We default TS_ACCEPT_DNS to false (to make the container behaviour more declarative), which with the new restrictive CLI behaviour resulted in failure for users who had set --accept-dns via TS_EXTRA_ARGS as the flag would be provided twice. This PR re-instates the previous behaviour by checking if TS_EXTRA_ARGS contains --accept-dns flag and if so using its value to override TS_ACCEPT_DNS. Updates tailscale/tailscale#16108 (cherry picked from commit 5b670eb3a5f1749a655692d97a2e7086c78d1580) Signed-off-by: Irbe Krumina --- cmd/containerboot/main_test.go | 248 ++++++++++++++++++----------- cmd/containerboot/settings.go | 57 +++++++ cmd/containerboot/settings_test.go | 108 +++++++++++++ 3 files changed, 322 insertions(+), 91 deletions(-) create mode 100644 cmd/containerboot/settings_test.go diff --git a/cmd/containerboot/main_test.go b/cmd/containerboot/main_test.go index a0ccce3dd86a2..c7293c77a4afa 100644 --- a/cmd/containerboot/main_test.go +++ b/cmd/containerboot/main_test.go @@ -41,97 +41,6 @@ import ( "tailscale.com/types/ptr" ) -// testEnv represents the environment needed for a single sub-test so that tests -// can run in parallel. -type testEnv struct { - kube *kubeServer // Fake kube server. - lapi *localAPI // Local TS API server. - d string // Temp dir for the specific test. - argFile string // File with commands test_tailscale{,d}.sh were invoked with. - runningSockPath string // Path to the running tailscaled socket. - localAddrPort int // Port for the containerboot HTTP server. - healthAddrPort int // Port for the (deprecated) containerboot health server. -} - -func newTestEnv(t *testing.T) testEnv { - d := t.TempDir() - - lapi := localAPI{FSRoot: d} - if err := lapi.Start(); err != nil { - t.Fatal(err) - } - t.Cleanup(lapi.Close) - - kube := kubeServer{FSRoot: d} - kube.Start(t) - t.Cleanup(kube.Close) - - tailscaledConf := &ipn.ConfigVAlpha{AuthKey: ptr.To("foo"), Version: "alpha0"} - serveConf := ipn.ServeConfig{TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}}} - egressCfg := egressSvcConfig("foo", "foo.tailnetxyz.ts.net") - - dirs := []string{ - "var/lib", - "usr/bin", - "tmp", - "dev/net", - "proc/sys/net/ipv4", - "proc/sys/net/ipv6/conf/all", - "etc/tailscaled", - } - for _, path := range dirs { - if err := os.MkdirAll(filepath.Join(d, path), 0700); err != nil { - t.Fatal(err) - } - } - files := map[string][]byte{ - "usr/bin/tailscaled": fakeTailscaled, - "usr/bin/tailscale": fakeTailscale, - "usr/bin/iptables": fakeTailscale, - "usr/bin/ip6tables": fakeTailscale, - "dev/net/tun": []byte(""), - "proc/sys/net/ipv4/ip_forward": []byte("0"), - "proc/sys/net/ipv6/conf/all/forwarding": []byte("0"), - "etc/tailscaled/cap-95.hujson": mustJSON(t, tailscaledConf), - "etc/tailscaled/serve-config.json": mustJSON(t, serveConf), - filepath.Join("etc/tailscaled/", egressservices.KeyEgressServices): mustJSON(t, egressCfg), - filepath.Join("etc/tailscaled/", egressservices.KeyHEPPings): []byte("4"), - } - for path, content := range files { - // Making everything executable is a little weird, but the - // stuff that doesn't need to be executable doesn't care if we - // do make it executable. - if err := os.WriteFile(filepath.Join(d, path), content, 0700); err != nil { - t.Fatal(err) - } - } - - argFile := filepath.Join(d, "args") - runningSockPath := filepath.Join(d, "tmp/tailscaled.sock") - var localAddrPort, healthAddrPort int - for _, p := range []*int{&localAddrPort, &healthAddrPort} { - ln, err := net.Listen("tcp", ":0") - if err != nil { - t.Fatalf("Failed to open listener: %v", err) - } - if err := ln.Close(); err != nil { - t.Fatalf("Failed to close listener: %v", err) - } - port := ln.Addr().(*net.TCPAddr).Port - *p = port - } - - return testEnv{ - kube: &kube, - lapi: &lapi, - d: d, - argFile: argFile, - runningSockPath: runningSockPath, - localAddrPort: localAddrPort, - healthAddrPort: healthAddrPort, - } -} - func TestContainerBoot(t *testing.T) { boot := filepath.Join(t.TempDir(), "containerboot") if err := exec.Command("go", "build", "-ldflags", "-X main.testSleepDuration=1ms", "-o", boot, "tailscale.com/cmd/containerboot").Run(); err != nil { @@ -515,6 +424,37 @@ func TestContainerBoot(t *testing.T) { }, } }, + "auth_key_once_extra_args_override_dns": func(env *testEnv) testCase { + return testCase{ + Env: map[string]string{ + "TS_AUTHKEY": "tskey-key", + "TS_AUTH_ONCE": "true", + "TS_ACCEPT_DNS": "false", + "TS_EXTRA_ARGS": "--accept-dns", + }, + Phases: []phase{ + { + WantCmds: []string{ + "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", + }, + }, + { + Notify: &ipn.Notify{ + State: ptr.To(ipn.NeedsLogin), + }, + WantCmds: []string{ + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=true --authkey=tskey-key", + }, + }, + { + Notify: runningNotify, + WantCmds: []string{ + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock set --accept-dns=true", + }, + }, + }, + } + }, "kube_storage": func(env *testEnv) testCase { return testCase{ Env: map[string]string{ @@ -766,6 +706,41 @@ func TestContainerBoot(t *testing.T) { }, } }, + "extra_args_accept_dns": func(env *testEnv) testCase { + return testCase{ + Env: map[string]string{ + "TS_EXTRA_ARGS": "--accept-dns", + }, + Phases: []phase{ + { + WantCmds: []string{ + "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=true", + }, + }, { + Notify: runningNotify, + }, + }, + } + }, + "extra_args_accept_dns_overrides_env_var": func(env *testEnv) testCase { + return testCase{ + Env: map[string]string{ + "TS_ACCEPT_DNS": "true", // Overridden by TS_EXTRA_ARGS. + "TS_EXTRA_ARGS": "--accept-dns=false", + }, + Phases: []phase{ + { + WantCmds: []string{ + "/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking", + "/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false", + }, + }, { + Notify: runningNotify, + }, + }, + } + }, "hostname": func(env *testEnv) testCase { return testCase{ Env: map[string]string{ @@ -1604,3 +1579,94 @@ func egressSvcConfig(name, fqdn string) egressservices.Configs { }, } } + +// testEnv represents the environment needed for a single sub-test so that tests +// can run in parallel. +type testEnv struct { + kube *kubeServer // Fake kube server. + lapi *localAPI // Local TS API server. + d string // Temp dir for the specific test. + argFile string // File with commands test_tailscale{,d}.sh were invoked with. + runningSockPath string // Path to the running tailscaled socket. + localAddrPort int // Port for the containerboot HTTP server. + healthAddrPort int // Port for the (deprecated) containerboot health server. +} + +func newTestEnv(t *testing.T) testEnv { + d := t.TempDir() + + lapi := localAPI{FSRoot: d} + if err := lapi.Start(); err != nil { + t.Fatal(err) + } + t.Cleanup(lapi.Close) + + kube := kubeServer{FSRoot: d} + kube.Start(t) + t.Cleanup(kube.Close) + + tailscaledConf := &ipn.ConfigVAlpha{AuthKey: ptr.To("foo"), Version: "alpha0"} + serveConf := ipn.ServeConfig{TCP: map[uint16]*ipn.TCPPortHandler{80: {HTTP: true}}} + egressCfg := egressSvcConfig("foo", "foo.tailnetxyz.ts.net") + + dirs := []string{ + "var/lib", + "usr/bin", + "tmp", + "dev/net", + "proc/sys/net/ipv4", + "proc/sys/net/ipv6/conf/all", + "etc/tailscaled", + } + for _, path := range dirs { + if err := os.MkdirAll(filepath.Join(d, path), 0700); err != nil { + t.Fatal(err) + } + } + files := map[string][]byte{ + "usr/bin/tailscaled": fakeTailscaled, + "usr/bin/tailscale": fakeTailscale, + "usr/bin/iptables": fakeTailscale, + "usr/bin/ip6tables": fakeTailscale, + "dev/net/tun": []byte(""), + "proc/sys/net/ipv4/ip_forward": []byte("0"), + "proc/sys/net/ipv6/conf/all/forwarding": []byte("0"), + "etc/tailscaled/cap-95.hujson": mustJSON(t, tailscaledConf), + "etc/tailscaled/serve-config.json": mustJSON(t, serveConf), + filepath.Join("etc/tailscaled/", egressservices.KeyEgressServices): mustJSON(t, egressCfg), + filepath.Join("etc/tailscaled/", egressservices.KeyHEPPings): []byte("4"), + } + for path, content := range files { + // Making everything executable is a little weird, but the + // stuff that doesn't need to be executable doesn't care if we + // do make it executable. + if err := os.WriteFile(filepath.Join(d, path), content, 0700); err != nil { + t.Fatal(err) + } + } + + argFile := filepath.Join(d, "args") + runningSockPath := filepath.Join(d, "tmp/tailscaled.sock") + var localAddrPort, healthAddrPort int + for _, p := range []*int{&localAddrPort, &healthAddrPort} { + ln, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatalf("Failed to open listener: %v", err) + } + if err := ln.Close(); err != nil { + t.Fatalf("Failed to close listener: %v", err) + } + port := ln.Addr().(*net.TCPAddr).Port + *p = port + } + + return testEnv{ + kube: &kube, + lapi: &lapi, + d: d, + argFile: argFile, + runningSockPath: runningSockPath, + localAddrPort: localAddrPort, + healthAddrPort: healthAddrPort, + } +} diff --git a/cmd/containerboot/settings.go b/cmd/containerboot/settings.go index 0ac9c828e2f76..5a8be9036b3ca 100644 --- a/cmd/containerboot/settings.go +++ b/cmd/containerboot/settings.go @@ -147,12 +147,69 @@ func configFromEnv() (*settings, error) { } } + // See https://github.com/tailscale/tailscale/issues/16108 for context- we + // do this to preserve the previous behaviour where --accept-dns could be + // set either via TS_ACCEPT_DNS or TS_EXTRA_ARGS. + acceptDNS := cfg.AcceptDNS != nil && *cfg.AcceptDNS + tsExtraArgs, acceptDNSNew := parseAcceptDNS(cfg.ExtraArgs, acceptDNS) + cfg.ExtraArgs = tsExtraArgs + if acceptDNS != acceptDNSNew { + cfg.AcceptDNS = &acceptDNSNew + } + if err := cfg.validate(); err != nil { return nil, fmt.Errorf("invalid configuration: %v", err) } return cfg, nil } +// parseAcceptDNS parses any values for Tailscale --accept-dns flag set via +// TS_ACCEPT_DNS and TS_EXTRA_ARGS env vars. If TS_EXTRA_ARGS contains +// --accept-dns flag, override the acceptDNS value with the one from +// TS_EXTRA_ARGS. +// The value of extraArgs can be empty string or one or more whitespace-separate +// key value pairs for 'tailscale up' command. The value for boolean flags can +// be omitted (default to true). +func parseAcceptDNS(extraArgs string, acceptDNS bool) (string, bool) { + if !strings.Contains(extraArgs, "--accept-dns") { + return extraArgs, acceptDNS + } + // TODO(irbekrm): we should validate that TS_EXTRA_ARGS contains legit + // 'tailscale up' flag values separated by whitespace. + argsArr := strings.Fields(extraArgs) + i := -1 + for key, val := range argsArr { + if strings.HasPrefix(val, "--accept-dns") { + i = key + break + } + } + if i == -1 { + return extraArgs, acceptDNS + } + a := strings.TrimSpace(argsArr[i]) + var acceptDNSFromExtraArgsS string + keyval := strings.Split(a, "=") + if len(keyval) == 2 { + acceptDNSFromExtraArgsS = keyval[1] + } else if len(keyval) == 1 && keyval[0] == "--accept-dns" { + // If the arg is just --accept-dns, we assume it means true. + acceptDNSFromExtraArgsS = "true" + } else { + log.Printf("TS_EXTRA_ARGS contains --accept-dns, but it is not in the expected format --accept-dns=, ignoring it") + return extraArgs, acceptDNS + } + acceptDNSFromExtraArgs, err := strconv.ParseBool(acceptDNSFromExtraArgsS) + if err != nil { + log.Printf("TS_EXTRA_ARGS contains --accept-dns=%q, which is not a valid boolean value, ignoring it", acceptDNSFromExtraArgsS) + return extraArgs, acceptDNS + } + if acceptDNSFromExtraArgs != acceptDNS { + log.Printf("TS_EXTRA_ARGS contains --accept-dns=%v, which overrides TS_ACCEPT_DNS=%v", acceptDNSFromExtraArgs, acceptDNS) + } + return strings.Join(append(argsArr[:i], argsArr[i+1:]...), " "), acceptDNSFromExtraArgs +} + func (s *settings) validate() error { if s.TailscaledConfigFilePath != "" { dir, file := path.Split(s.TailscaledConfigFilePath) diff --git a/cmd/containerboot/settings_test.go b/cmd/containerboot/settings_test.go new file mode 100644 index 0000000000000..dbec066c9ab0d --- /dev/null +++ b/cmd/containerboot/settings_test.go @@ -0,0 +1,108 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +//go:build linux + +package main + +import "testing" + +func Test_parseAcceptDNS(t *testing.T) { + tests := []struct { + name string + extraArgs string + acceptDNS bool + wantExtraArgs string + wantAcceptDNS bool + }{ + { + name: "false_extra_args_unset", + extraArgs: "", + wantExtraArgs: "", + wantAcceptDNS: false, + }, + { + name: "false_unrelated_args_set", + extraArgs: "--accept-routes=true --advertise-routes=10.0.0.1/32", + wantExtraArgs: "--accept-routes=true --advertise-routes=10.0.0.1/32", + wantAcceptDNS: false, + }, + { + name: "true_extra_args_unset", + extraArgs: "", + acceptDNS: true, + wantExtraArgs: "", + wantAcceptDNS: true, + }, + { + name: "true_unrelated_args_set", + acceptDNS: true, + extraArgs: "--accept-routes=true --advertise-routes=10.0.0.1/32", + wantExtraArgs: "--accept-routes=true --advertise-routes=10.0.0.1/32", + wantAcceptDNS: true, + }, + { + name: "false_extra_args_set_to_false", + extraArgs: "--accept-dns=false", + wantExtraArgs: "", + wantAcceptDNS: false, + }, + { + name: "false_extra_args_set_to_true", + extraArgs: "--accept-dns=true", + wantExtraArgs: "", + wantAcceptDNS: true, + }, + { + name: "true_extra_args_set_to_false", + extraArgs: "--accept-dns=false", + acceptDNS: true, + wantExtraArgs: "", + wantAcceptDNS: false, + }, + { + name: "true_extra_args_set_to_true", + extraArgs: "--accept-dns=true", + acceptDNS: true, + wantExtraArgs: "", + wantAcceptDNS: true, + }, + { + name: "false_extra_args_set_to_true_implicitly", + extraArgs: "--accept-dns", + wantExtraArgs: "", + wantAcceptDNS: true, + }, + { + name: "false_extra_args_set_to_true_implicitly_with_unrelated_args", + extraArgs: "--accept-dns --accept-routes --advertise-routes=10.0.0.1/32", + wantExtraArgs: "--accept-routes --advertise-routes=10.0.0.1/32", + wantAcceptDNS: true, + }, + { + name: "false_extra_args_set_to_true_implicitly_surrounded_with_unrelated_args", + extraArgs: "--accept-routes --accept-dns --advertise-routes=10.0.0.1/32", + wantExtraArgs: "--accept-routes --advertise-routes=10.0.0.1/32", + wantAcceptDNS: true, + }, + { + name: "true_extra_args_set_to_false_with_unrelated_args", + extraArgs: "--accept-routes --accept-dns=false", + acceptDNS: true, + wantExtraArgs: "--accept-routes", + wantAcceptDNS: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotExtraArgs, gotAcceptDNS := parseAcceptDNS(tt.extraArgs, tt.acceptDNS) + if gotExtraArgs != tt.wantExtraArgs { + t.Errorf("parseAcceptDNS() gotExtraArgs = %v, want %v", gotExtraArgs, tt.wantExtraArgs) + } + if gotAcceptDNS != tt.wantAcceptDNS { + t.Errorf("parseAcceptDNS() gotAcceptDNS = %v, want %v", gotAcceptDNS, tt.wantAcceptDNS) + } + }) + } +} From 2e915f45c544256ef65b0a07daf4a24a4d2c3f99 Mon Sep 17 00:00:00 2001 From: Tom Meadows Date: Mon, 9 Jun 2025 15:54:10 +0100 Subject: [PATCH 06/11] cmd/k8s-operator: explicitly set tcp on VIPService port configuration for Ingress with ProxyGroup (#16199) (#16226) Updates tailscale/corp#24795 (cherry picked from commit 4456f77af71367e52565a76dd58a796fa108e3f8) Signed-off-by: chaosinthecrd --- cmd/k8s-operator/ingress-for-pg.go | 4 ++-- cmd/k8s-operator/ingress-for-pg_test.go | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/k8s-operator/ingress-for-pg.go b/cmd/k8s-operator/ingress-for-pg.go index 9cdd9cba96fca..5f9c549407717 100644 --- a/cmd/k8s-operator/ingress-for-pg.go +++ b/cmd/k8s-operator/ingress-for-pg.go @@ -318,9 +318,9 @@ func (r *HAIngressReconciler) maybeProvision(ctx context.Context, hostname strin tags = strings.Split(tstr, ",") } - tsSvcPorts := []string{"443"} // always 443 for Ingress + tsSvcPorts := []string{"tcp:443"} // always 443 for Ingress if isHTTPEndpointEnabled(ing) { - tsSvcPorts = append(tsSvcPorts, "80") + tsSvcPorts = append(tsSvcPorts, "tcp:80") } tsSvc := &tailscale.VIPService{ diff --git a/cmd/k8s-operator/ingress-for-pg_test.go b/cmd/k8s-operator/ingress-for-pg_test.go index 3330da8d001b0..f155963030b4f 100644 --- a/cmd/k8s-operator/ingress-for-pg_test.go +++ b/cmd/k8s-operator/ingress-for-pg_test.go @@ -68,7 +68,7 @@ func TestIngressPGReconciler(t *testing.T) { populateTLSSecret(context.Background(), fc, "test-pg", "my-svc.ts.net") expectReconciled(t, ingPGR, "default", "test-ingress") verifyServeConfig(t, fc, "svc:my-svc", false) - verifyTailscaleService(t, ft, "svc:my-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:my-svc", []string{"tcp:443"}) verifyTailscaledConfig(t, fc, []string{"svc:my-svc"}) // Verify that Role and RoleBinding have been created for the first Ingress. @@ -130,7 +130,7 @@ func TestIngressPGReconciler(t *testing.T) { populateTLSSecret(context.Background(), fc, "test-pg", "my-other-svc.ts.net") expectReconciled(t, ingPGR, "default", "my-other-ingress") verifyServeConfig(t, fc, "svc:my-other-svc", false) - verifyTailscaleService(t, ft, "svc:my-other-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:my-other-svc", []string{"tcp:443"}) // Verify that Role and RoleBinding have been created for the first Ingress. // Do not verify the cert Secret as that was already verified implicitly above. @@ -139,7 +139,7 @@ func TestIngressPGReconciler(t *testing.T) { // Verify first Ingress is still working verifyServeConfig(t, fc, "svc:my-svc", false) - verifyTailscaleService(t, ft, "svc:my-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:my-svc", []string{"tcp:443"}) verifyTailscaledConfig(t, fc, []string{"svc:my-svc", "svc:my-other-svc"}) @@ -244,7 +244,7 @@ func TestIngressPGReconciler_UpdateIngressHostname(t *testing.T) { populateTLSSecret(context.Background(), fc, "test-pg", "my-svc.ts.net") expectReconciled(t, ingPGR, "default", "test-ingress") verifyServeConfig(t, fc, "svc:my-svc", false) - verifyTailscaleService(t, ft, "svc:my-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:my-svc", []string{"tcp:443"}) verifyTailscaledConfig(t, fc, []string{"svc:my-svc"}) // Update the Ingress hostname and make sure the original Tailscale Service is deleted. @@ -255,7 +255,7 @@ func TestIngressPGReconciler_UpdateIngressHostname(t *testing.T) { populateTLSSecret(context.Background(), fc, "test-pg", "updated-svc.ts.net") expectReconciled(t, ingPGR, "default", "test-ingress") verifyServeConfig(t, fc, "svc:updated-svc", false) - verifyTailscaleService(t, ft, "svc:updated-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:updated-svc", []string{"tcp:443"}) verifyTailscaledConfig(t, fc, []string{"svc:updated-svc"}) _, err := ft.GetVIPService(context.Background(), tailcfg.ServiceName("svc:my-svc")) @@ -475,7 +475,7 @@ func TestIngressPGReconciler_HTTPEndpoint(t *testing.T) { expectReconciled(t, ingPGR, "default", "test-ingress") populateTLSSecret(context.Background(), fc, "test-pg", "my-svc.ts.net") expectReconciled(t, ingPGR, "default", "test-ingress") - verifyTailscaleService(t, ft, "svc:my-svc", []string{"80", "443"}) + verifyTailscaleService(t, ft, "svc:my-svc", []string{"tcp:80", "tcp:443"}) verifyServeConfig(t, fc, "svc:my-svc", true) // Verify Ingress status @@ -528,7 +528,7 @@ func TestIngressPGReconciler_HTTPEndpoint(t *testing.T) { // Verify reconciliation after removing HTTP expectReconciled(t, ingPGR, "default", "test-ingress") - verifyTailscaleService(t, ft, "svc:my-svc", []string{"443"}) + verifyTailscaleService(t, ft, "svc:my-svc", []string{"tcp:443"}) verifyServeConfig(t, fc, "svc:my-svc", false) // Verify Ingress status From 5f702f4c2babf95e4244ce0dafbb7a50b52c1fc8 Mon Sep 17 00:00:00 2001 From: Nick O'Neill Date: Mon, 9 Jun 2025 14:39:17 -0700 Subject: [PATCH 07/11] VERSION.txt: this is v1.84.2 (#16232) Signed-off-by: Nick O'Neill --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 1a17bdc1710f0..209084d73d622 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.84.1 +1.84.2 From 155e3460758e313c40dbce496f8f7adaa7a35b0c Mon Sep 17 00:00:00 2001 From: Cezar Craciunoiu Date: Fri, 13 Dec 2024 18:18:46 +0200 Subject: [PATCH 08/11] net/netmon: Do not populate interfaces with 'ukp' ones Signed-off-by: Cezar Craciunoiu --- net/netmon/state.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/netmon/state.go b/net/netmon/state.go index bd09607682bb4..2bb84f384a9cb 100644 --- a/net/netmon/state.go +++ b/net/netmon/state.go @@ -58,6 +58,10 @@ func LocalAddresses() (regular, loopback []netip.Addr, err error) { } var regular4, regular6, linklocal4, ula6 []netip.Addr for _, iface := range ifaces { + if strings.HasPrefix(iface.Name, "ukp") { + continue + } + stdIf := iface.Interface if !isUp(stdIf) || isProblematicInterface(stdIf) { // Skip down interfaces and ones that are @@ -203,6 +207,9 @@ func ForeachInterface(fn func(Interface, []netip.Prefix)) error { // the interface, and Bits are the subnet mask. func (ifaces InterfaceList) ForeachInterface(fn func(Interface, []netip.Prefix)) error { for _, iface := range ifaces { + if strings.HasPrefix(iface.Name, "ukp") { + continue + } addrs, err := iface.Addrs() if err != nil { return err From d5dc6f705f20fcbbd52c6a7ab9e23cb3fdde9c25 Mon Sep 17 00:00:00 2001 From: Cezar Craciunoiu Date: Wed, 11 Jun 2025 10:42:58 +0300 Subject: [PATCH 09/11] build_dist.sh: Statically link binaries Signed-off-by: Cezar Craciunoiu --- build_dist.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build_dist.sh b/build_dist.sh index fed37c2646175..30bd60c8a4adf 100755 --- a/build_dist.sh +++ b/build_dist.sh @@ -29,7 +29,7 @@ EOF fi tags="${TAGS:-}" -ldflags="-X tailscale.com/version.longStamp=${VERSION_LONG} -X tailscale.com/version.shortStamp=${VERSION_SHORT}" +ldflags="-X tailscale.com/version.longStamp=${VERSION_LONG} -s -w -X tailscale.com/version.shortStamp=${VERSION_SHORT}" # build_dist.sh arguments must precede go build arguments. while [ "$#" -gt 1 ]; do @@ -57,4 +57,7 @@ while [ "$#" -gt 1 ]; do esac done -exec $go build ${tags:+-tags=$tags} -ldflags "$ldflags" "$@" +export CGO_ENABLED=0 + +exec $go build ${tags:+-tags=$tags} -ldflags "$ldflags" -buildmode=pie "$@" + From 35e8e364419745c1d1a7cbf895d82fefcbb99930 Mon Sep 17 00:00:00 2001 From: Cezar Craciunoiu Date: Wed, 11 Jun 2025 10:43:53 +0300 Subject: [PATCH 10/11] release: Add nfpm for releasing debian packages Signed-off-by: Cezar Craciunoiu Signed-off-by: Simon Kuenzer --- .github/workflows/release.yaml | 107 +++++++++++++++++++++++++++++++++ nfpm.yaml | 39 ++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 .github/workflows/release.yaml create mode 100644 nfpm.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000000000..34e0564cfb849 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,107 @@ +name: release + +on: + push: + branches: [prod-staging, prod-stable] + workflow_dispatch: + +permissions: + contents: write + packages: write + +jobs: + release: + runs-on: ubuntu-latest + container: debian:bookworm + env: + GO_VERSION: stable + PYTHONDONTWRITEBYTECODE: 1 + UKP_PACKAGE: /platform-tailscaled-install + steps: + - name: Install extra build dependencies + run: | + set -xe + apt-get update + apt-get install --no-install-recommends -y \ + git \ + wget \ + curl \ + rename \ + ca-certificates + + - name: Install release dependencies + run: | + set -xe + echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list + echo "deb [trusted=yes] https://apt.fury.io/cli/ * *" > /etc/apt/sources.list.d/fury-cli.list + apt-get update + apt-get install --no-install-recommends -y nfpm fury-cli + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: false + + - name: Build + id: build + run: | + set -xe + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + ./build_dist.sh tailscale.com/cmd/tailscale + ./build_dist.sh tailscale.com/cmd/tailscaled + + mkdir -p "${UKP_PACKAGE}/usr/bin" + mkdir -p "${UKP_PACKAGE}/usr/sbin" + mv ./tailscale "${UKP_PACKAGE}/usr/bin/tailscale" + mv ./tailscaled "${UKP_PACKAGE}/usr/sbin/tailscaled" + + echo "COMMITS=$(git describe --tags --long | awk -F'-' '{print $2}')" > "$GITHUB_OUTPUT" + + - name: Package + shell: bash + run: | + set -xe + ts_version="$(${UKP_PACKAGE}/usr/sbin/tailscaled --version)" + export SEMVER=$(echo -n "$ts_version" | head -n 1 | sed 's/^v//') + export SEMVER="${SEMVER}-${{ steps.build.outputs.COMMITS }}+$(git rev-parse --short=7 HEAD)" + if [[ "$GITHUB_REF" == *"prod-staging"* ]]; then + export SEMVER="5:${SEMVER}-1staging" + elif [[ "$GITHUB_REF" == *"prod-stable"* ]]; then + export SEMVER="5:${SEMVER}-9stable" + else + name=$(echo "$GITHUB_REF" | cut -f1,2 --complement -d'/' | tr '/' '+' | tr '-' '+' | tr '*' '+' | tr '[:upper:]' '[:lower:]') + export SEMVER="1:${SEMVER}-0dev+${name}" + fi + mkdir -p /dist + nfpm package --config nfpm.yaml --packager deb --target /dist + rename 's/:/_/g' /dist/*.deb + + - name: Upload build artifacts to GitHub + uses: actions/upload-artifact@v4 + with: + name: platform-tailscaled-latest + path: /dist + if-no-files-found: error + retention-days: 1 + overwrite: true + + - name: Publish to Gemfury + shell: bash + run: | + set -xe + if [[ "$GITHUB_REF" == *"prod-stable"* ]]; then + FUSER="$FURY_USER_STABLE" + FTOKEN="$FURY_TOKEN_STABLE" + fi + fury push "$(ls /dist/*.deb)" --quiet --account "$FUSER" --api-token "$FTOKEN" + env: + FURY_TOKEN_STABLE: ${{ secrets.FURY_TOKEN }} + FURY_USER_STABLE: ${{ secrets.FURY_USER }} + FUSER: ${{ secrets.FURY_USER_STAGING }} + FTOKEN: ${{ secrets.FURY_TOKEN_STAGING }} diff --git a/nfpm.yaml b/nfpm.yaml new file mode 100644 index 0000000000000..3a84846744428 --- /dev/null +++ b/nfpm.yaml @@ -0,0 +1,39 @@ +name: ukp-tailscale +arch: amd64 +platform: linux +version: ${SEMVER} +maintainer: Unikraft GmbH +description: The easiest, most secure, cross platform way to use WireGuard + oauth2 + 2FA/SSO. Patched version, compatible with Unikraft Cloud. +homepage: https://www.tailscale.com +vendor: Unikraft GmbH +priority: extra +section: net +depends: + - iptables +provides: + - tailscale +recommends: + - iproute2 +conflicts: + - tailscale + - tailscale-relay +replaces: + - tailscale + - tailscale-relay +deb: + breaks: + - tailscale +scripts: + postinstall: ./release/deb/debian.postinst.sh + preremove: ./release/deb/debian.prerm.sh + postremove: ./release/deb/debian.postrm.sh +contents: + - src: /platform-tailscaled-install/usr/sbin/tailscaled + dst: /usr/sbin/tailscaled + - src: /platform-tailscaled-install/usr/bin/tailscale + dst: /usr/bin/tailscale + - src: ./cmd/tailscaled/tailscaled.service + dst: /lib/systemd/system/tailscaled.service + - src: ./cmd/tailscaled/tailscaled.defaults + dst: /etc/default/tailscaled + type: config From ba548ab346846795f0cff10a6938ef4d790e6566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 20:51:52 +0000 Subject: [PATCH 11/11] build(deps): bump golang.org/x/crypto Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../github.com/gokrazy/breakglass/go.mod | 18 +------- .../github.com/gokrazy/breakglass/go.sum | 46 ------------------- 2 files changed, 1 insertion(+), 63 deletions(-) diff --git a/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.mod b/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.mod index fc809b8f7c130..8f7d9e2d78227 100644 --- a/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.mod +++ b/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.mod @@ -1,19 +1,3 @@ module gokrazy/build/tsapp -go 1.22.2 - -require ( - github.com/creack/pty v1.1.18 // indirect - github.com/gokrazy/breakglass v0.0.0-20240604170121-09eeab3321d6 // indirect - github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83 // indirect - github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 // indirect - github.com/google/renameio/v2 v2.0.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/kenshaw/evdev v0.1.0 // indirect - github.com/kr/fs v0.1.0 // indirect - github.com/kr/pty v1.1.8 // indirect - github.com/mdlayher/watchdog v0.0.0-20221003142519-49be0df7b3b5 // indirect - github.com/pkg/sftp v1.13.5 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect -) +go 1.23.0 diff --git a/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.sum b/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.sum index 99e0622742caf..e69de29bb2d1d 100644 --- a/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.sum +++ b/gokrazy/tsapp/builddir/github.com/gokrazy/breakglass/go.sum @@ -1,46 +0,0 @@ -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gokrazy/breakglass v0.0.0-20240529175905-44b3fe64f19c h1:cWzgXJIluB6jAQ0HcnvA1yExLawmtDSssk9H4fLv3yM= -github.com/gokrazy/breakglass v0.0.0-20240529175905-44b3fe64f19c/go.mod h1:4Yffo2Z5w3q2eDvo3HDR8eDnmkDpMAkX0Tn7b/9upgs= -github.com/gokrazy/breakglass v0.0.0-20240604170121-09eeab3321d6 h1:38JB1lVPx+ihCzlWZdbH1LoNmu0KR+jRSmNFR7aMVTg= -github.com/gokrazy/breakglass v0.0.0-20240604170121-09eeab3321d6/go.mod h1:4Yffo2Z5w3q2eDvo3HDR8eDnmkDpMAkX0Tn7b/9upgs= -github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83 h1:Y4sADvUYd/c0eqnqebipHHl0GMpAxOQeTzPnwI4ievM= -github.com/gokrazy/gokrazy v0.0.0-20230812092215-346db1998f83/go.mod h1:9q5Tg+q+YvRjC3VG0gfMFut46dhbhtAnvUEp4lPjc6c= -github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0 h1:QTi0skQ/OM7he/5jEWA9k/DYgdwGAhw3hrUoiPGGZHM= -github.com/gokrazy/internal v0.0.0-20230211171410-9608422911d0/go.mod h1:ddHcxXZ/VVQOSAWcRBbkYY58+QOw4L145ye6phyDmRA= -github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= -github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/kenshaw/evdev v0.1.0 h1:wmtceEOFfilChgdNT+c/djPJ2JineVsQ0N14kGzFRUo= -github.com/kenshaw/evdev v0.1.0/go.mod h1:B/fErKCihUyEobz0mjn2qQbHgyJKFQAxkXSvkeeA/Wo= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/mdlayher/watchdog v0.0.0-20221003142519-49be0df7b3b5 h1:80FAK3TW5lVymfHu3kvB1QvTZvy9Kmx1lx6sT5Ep16s= -github.com/mdlayher/watchdog v0.0.0-20221003142519-49be0df7b3b5/go.mod h1:z0QjVpjpK4jksEkffQwS3+abQ3XFTm1bnimyDzWyUk0= -github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= -github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tailscale/breakglass v0.0.0-20240529174846-0d8ebfc2c652 h1:36TB+ZuYaA8OTdMoPnygC9CJuQmTWxMEmn+a+9XTOgk= -github.com/tailscale/breakglass v0.0.0-20240529174846-0d8ebfc2c652/go.mod h1:4Yffo2Z5w3q2eDvo3HDR8eDnmkDpMAkX0Tn7b/9upgs= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=