mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-04 20:12:16 +02:00
feature/clientupdate: move clientupdate to a modular feature, disabled for tsnet
Updates #12614 Change-Id: I5f685dec84a5396b7c2b66f2788ae3d286e1ddc6 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
69c79cb9f3
commit
038cdb4640
@ -24,9 +24,9 @@ import (
|
||||
|
||||
"tailscale.com/client/local"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/envknob/featureknob"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/hostinfo"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
@ -1049,7 +1049,7 @@ func availableFeatures() map[string]bool {
|
||||
"advertise-routes": true, // available on all platforms
|
||||
"use-exit-node": featureknob.CanUseExitNode() == nil,
|
||||
"ssh": featureknob.CanRunTailscaleSSH() == nil,
|
||||
"auto-update": version.IsUnstableBuild() && clientupdate.CanAutoUpdate(),
|
||||
"auto-update": version.IsUnstableBuild() && feature.CanAutoUpdate(),
|
||||
}
|
||||
return features
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/hostinfo"
|
||||
"tailscale.com/types/lazy"
|
||||
"tailscale.com/types/logger"
|
||||
@ -252,9 +253,13 @@ func (up *Updater) getUpdateFunction() (fn updateFunction, canAutoUpdate bool) {
|
||||
|
||||
var canAutoUpdateCache lazy.SyncValue[bool]
|
||||
|
||||
// CanAutoUpdate reports whether auto-updating via the clientupdate package
|
||||
func init() {
|
||||
feature.HookCanAutoUpdate.Set(canAutoUpdate)
|
||||
}
|
||||
|
||||
// canAutoUpdate reports whether auto-updating via the clientupdate package
|
||||
// is supported for the current os/distro.
|
||||
func CanAutoUpdate() bool { return canAutoUpdateCache.Get(canAutoUpdateUncached) }
|
||||
func canAutoUpdate() bool { return canAutoUpdateCache.Get(canAutoUpdateUncached) }
|
||||
|
||||
func canAutoUpdateUncached() bool {
|
||||
if version.IsMacSysExt() {
|
||||
|
@ -12,7 +12,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
github.com/coder/websocket/internal/util from github.com/coder/websocket
|
||||
github.com/coder/websocket/internal/xsync from github.com/coder/websocket
|
||||
💣 github.com/davecgh/go-spew/spew from k8s.io/apimachinery/pkg/util/dump
|
||||
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/com+
|
||||
W 💣 github.com/dblohm7/wingoes from tailscale.com/net/tshttpproxy+
|
||||
W 💣 github.com/dblohm7/wingoes/com from tailscale.com/util/osdiag+
|
||||
W 💣 github.com/dblohm7/wingoes/com/automation from tailscale.com/util/osdiag/internal/wsc
|
||||
W github.com/dblohm7/wingoes/internal from github.com/dblohm7/wingoes/com
|
||||
@ -60,7 +60,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
github.com/google/gofuzz from k8s.io/apimachinery/pkg/apis/meta/v1+
|
||||
github.com/google/gofuzz/bytesource from github.com/google/gofuzz
|
||||
github.com/google/uuid from github.com/prometheus-community/pro-bing+
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/tka
|
||||
github.com/josharian/intern from github.com/mailru/easyjson/jlexer
|
||||
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
|
||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
@ -686,8 +686,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/client/tailscale from tailscale.com/cmd/k8s-operator+
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
|
||||
tailscale.com/client/web from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||
LW tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||
tailscale.com/control/controlclient from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
|
||||
@ -780,7 +778,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
|
||||
tailscale.com/net/tsaddr from tailscale.com/client/web+
|
||||
tailscale.com/net/tsdial from tailscale.com/control/controlclient+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/tstun from tailscale.com/tsd+
|
||||
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/net/udprelay/status from tailscale.com/client/local
|
||||
@ -829,7 +827,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/util/cibuild from tailscale.com/health
|
||||
tailscale.com/util/clientmetric from tailscale.com/cmd/k8s-operator+
|
||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||
tailscale.com/util/cmpver from tailscale.com/clientupdate+
|
||||
LW tailscale.com/util/cmpver from tailscale.com/net/dns+
|
||||
tailscale.com/util/ctxkey from tailscale.com/client/tailscale/apitype+
|
||||
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
|
||||
L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics
|
||||
@ -843,7 +841,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/util/lineiter from tailscale.com/hostinfo+
|
||||
tailscale.com/util/mak from tailscale.com/appc+
|
||||
tailscale.com/util/multierr from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/must from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/must from tailscale.com/logpolicy+
|
||||
tailscale.com/util/nocasemaps from tailscale.com/types/ipproto
|
||||
💣 tailscale.com/util/osdiag from tailscale.com/ipn/localapi
|
||||
W 💣 tailscale.com/util/osdiag/internal/wsc from tailscale.com/util/osdiag
|
||||
@ -869,8 +867,8 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
tailscale.com/util/truncate from tailscale.com/logtail
|
||||
tailscale.com/util/usermetric from tailscale.com/health+
|
||||
tailscale.com/util/vizerror from tailscale.com/tailcfg+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/clientupdate+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/clientupdate+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/util/osdiag
|
||||
W 💣 tailscale.com/util/winutil/gp from tailscale.com/net/dns+
|
||||
W tailscale.com/util/winutil/policy from tailscale.com/ipn/ipnlocal
|
||||
W 💣 tailscale.com/util/winutil/winenv from tailscale.com/hostinfo+
|
||||
@ -907,7 +905,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||
LD golang.org/x/crypto/ssh from tailscale.com/ipn/ipnlocal
|
||||
LD golang.org/x/crypto/ssh/internal/bcrypt_pbkdf from golang.org/x/crypto/ssh
|
||||
golang.org/x/exp/constraints from github.com/dblohm7/wingoes/pe+
|
||||
golang.org/x/exp/constraints from tailscale.com/tsweb/varz+
|
||||
golang.org/x/exp/maps from sigs.k8s.io/controller-runtime/pkg/cache+
|
||||
golang.org/x/exp/slices from tailscale.com/cmd/k8s-operator+
|
||||
golang.org/x/net/bpf from github.com/mdlayher/genetlink+
|
||||
@ -944,13 +942,12 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from gvisor.dev/gvisor/pkg/log+
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from archive/tar+
|
||||
bytes from bufio+
|
||||
cmp from github.com/gaissmai/bart+
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from github.com/emicklei/go-restful/v3+
|
||||
compress/zlib from debug/pe+
|
||||
compress/zlib from github.com/emicklei/go-restful/v3+
|
||||
container/heap from gvisor.dev/gvisor/pkg/tcpip/transport/tcp+
|
||||
container/list from crypto/tls+
|
||||
context from crypto/tls+
|
||||
@ -1034,10 +1031,10 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
encoding/xml from github.com/emicklei/go-restful/v3+
|
||||
errors from archive/tar+
|
||||
errors from bufio+
|
||||
expvar from github.com/prometheus/client_golang/prometheus+
|
||||
flag from github.com/spf13/pflag+
|
||||
fmt from archive/tar+
|
||||
fmt from compress/flate+
|
||||
go/ast from go/doc+
|
||||
go/build/constraint from go/parser
|
||||
go/doc from k8s.io/apimachinery/pkg/runtime
|
||||
@ -1063,7 +1060,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
internal/filepathlite from os+
|
||||
internal/fmtsort from fmt+
|
||||
internal/goarch from crypto/internal/fips140deps/cpu+
|
||||
internal/godebug from archive/tar+
|
||||
internal/godebug from crypto/internal/fips140deps/godebug+
|
||||
internal/godebugs from internal/godebug+
|
||||
internal/goexperiment from hash/maphash+
|
||||
internal/goos from crypto/x509+
|
||||
@ -1100,8 +1097,8 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
internal/testlog from os
|
||||
internal/trace/tracev2 from runtime+
|
||||
internal/unsafeheader from internal/reflectlite+
|
||||
io from archive/tar+
|
||||
io/fs from archive/tar+
|
||||
io from bufio+
|
||||
io/fs from crypto/x509+
|
||||
io/ioutil from github.com/digitalocean/go-smbios/smbios+
|
||||
iter from go/ast+
|
||||
log from expvar+
|
||||
@ -1110,7 +1107,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
log/slog/internal from log/slog
|
||||
log/slog/internal/buffer from log/slog
|
||||
maps from sigs.k8s.io/controller-runtime/pkg/predicate+
|
||||
math from archive/tar+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from compress/flate+
|
||||
math/rand from github.com/google/go-cmp/cmp+
|
||||
@ -1132,29 +1129,29 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
os from crypto/internal/sysrand+
|
||||
os/exec from github.com/godbus/dbus/v5+
|
||||
os/signal from sigs.k8s.io/controller-runtime/pkg/manager/signals
|
||||
os/user from archive/tar+
|
||||
path from archive/tar+
|
||||
path/filepath from archive/tar+
|
||||
reflect from archive/tar+
|
||||
os/user from github.com/godbus/dbus/v5+
|
||||
path from debug/dwarf+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from github.com/davecgh/go-spew/spew+
|
||||
regexp/syntax from regexp
|
||||
runtime from archive/tar+
|
||||
runtime from crypto/internal/fips140+
|
||||
runtime/debug from github.com/coder/websocket/internal/xsync+
|
||||
runtime/metrics from github.com/prometheus/client_golang/prometheus+
|
||||
runtime/pprof from net/http/pprof+
|
||||
runtime/trace from net/http/pprof
|
||||
slices from encoding/base32+
|
||||
sort from compress/flate+
|
||||
strconv from archive/tar+
|
||||
strings from archive/tar+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
W structs from internal/syscall/windows
|
||||
sync from archive/tar+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from archive/tar+
|
||||
syscall from crypto/internal/sysrand+
|
||||
text/tabwriter from k8s.io/apimachinery/pkg/util/diff+
|
||||
text/template from html/template
|
||||
text/template/parse from html/template+
|
||||
time from archive/tar+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from crypto/x509+
|
||||
unicode/utf8 from bufio+
|
||||
|
@ -15,8 +15,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/cmd/tailscale/cli/ffcomplete"
|
||||
"tailscale.com/feature/buildfeatures"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/netutil"
|
||||
"tailscale.com/net/tsaddr"
|
||||
@ -226,21 +226,14 @@ func runSet(ctx context.Context, args []string) (retErr error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if maskedPrefs.AutoUpdateSet.ApplySet {
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
return errors.New("automatic updates are not supported on this platform")
|
||||
if maskedPrefs.AutoUpdateSet.ApplySet && buildfeatures.HasClientUpdate && version.IsMacSysExt() {
|
||||
apply := "0"
|
||||
if maskedPrefs.AutoUpdate.Apply.EqualBool(true) {
|
||||
apply = "1"
|
||||
}
|
||||
// On macsys, tailscaled will set the Sparkle auto-update setting. It
|
||||
// does not use clientupdate.
|
||||
if version.IsMacSysExt() {
|
||||
apply := "0"
|
||||
if maskedPrefs.AutoUpdate.Apply.EqualBool(true) {
|
||||
apply = "1"
|
||||
}
|
||||
out, err := exec.Command("defaults", "write", "io.tailscale.ipn.macsys", "SUAutomaticallyUpdate", apply).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable automatic updates: %v, %q", err, out)
|
||||
}
|
||||
out, err := exec.Command("defaults", "write", "io.tailscale.ipn.macsys", "SUAutomaticallyUpdate", apply).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable automatic updates: %v, %q", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
tailscale.com/client/tailscale from tailscale.com/internal/client/tailscale
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
|
||||
tailscale.com/client/web from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||
tailscale.com/clientupdate from tailscale.com/cmd/tailscale/cli
|
||||
LW tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
||||
tailscale.com/cmd/tailscale/cli/ffcomplete from tailscale.com/cmd/tailscale/cli
|
||||
|
@ -1,7 +1,5 @@
|
||||
tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/depaware)
|
||||
|
||||
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
|
||||
filippo.io/edwards25519/field from filippo.io/edwards25519
|
||||
github.com/digitalocean/go-smbios/smbios from tailscale.com/posture
|
||||
github.com/gaissmai/bart from tailscale.com/net/ipset+
|
||||
github.com/gaissmai/bart/internal/bitset from github.com/gaissmai/bart+
|
||||
@ -13,7 +11,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
github.com/go-json-experiment/json/internal/jsonwire from github.com/go-json-experiment/json+
|
||||
github.com/go-json-experiment/json/jsontext from github.com/go-json-experiment/json+
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign
|
||||
💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
|
||||
github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
github.com/klauspost/compress from github.com/klauspost/compress/zstd
|
||||
@ -44,8 +41,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/appc from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/atomicfile from tailscale.com/ipn+
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/ipn/ipnauth+
|
||||
tailscale.com/clientupdate from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/cmd/tailscaled/childproc from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||
tailscale.com/control/controlclient from tailscale.com/cmd/tailscaled+
|
||||
@ -65,7 +60,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
|
||||
tailscale.com/health from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/hostinfo from tailscale.com/clientupdate+
|
||||
tailscale.com/hostinfo from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
||||
tailscale.com/ipn from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/ipn/conffile from tailscale.com/cmd/tailscaled+
|
||||
@ -116,7 +111,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
|
||||
tailscale.com/net/tsaddr from tailscale.com/ipn+
|
||||
tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/net/tshttpproxy from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/omit from tailscale.com/ipn/conffile
|
||||
@ -142,7 +137,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/types/flagtype from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/types/ipproto from tailscale.com/ipn+
|
||||
tailscale.com/types/key from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/types/lazy from tailscale.com/clientupdate+
|
||||
tailscale.com/types/lazy from tailscale.com/hostinfo+
|
||||
tailscale.com/types/logger from tailscale.com/appc+
|
||||
tailscale.com/types/logid from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/types/mapx from tailscale.com/ipn/ipnext
|
||||
@ -161,7 +156,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/util/cibuild from tailscale.com/health
|
||||
tailscale.com/util/clientmetric from tailscale.com/appc+
|
||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||
tailscale.com/util/cmpver from tailscale.com/clientupdate
|
||||
tailscale.com/util/ctxkey from tailscale.com/client/tailscale/apitype+
|
||||
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
|
||||
💣 tailscale.com/util/dirwalk from tailscale.com/metrics
|
||||
@ -171,11 +165,11 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/util/goroutines from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/util/groupmember from tailscale.com/ipn/ipnauth
|
||||
💣 tailscale.com/util/hashx from tailscale.com/util/deephash
|
||||
tailscale.com/util/httpm from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/httpm from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/util/lineiter from tailscale.com/hostinfo+
|
||||
tailscale.com/util/mak from tailscale.com/appc+
|
||||
tailscale.com/util/multierr from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/util/must from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/must from tailscale.com/logpolicy+
|
||||
tailscale.com/util/nocasemaps from tailscale.com/types/ipproto
|
||||
tailscale.com/util/osdiag from tailscale.com/ipn/localapi
|
||||
tailscale.com/util/osshare from tailscale.com/cmd/tailscaled
|
||||
@ -195,8 +189,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/util/vizerror from tailscale.com/tailcfg+
|
||||
tailscale.com/util/winutil from tailscale.com/ipn/ipnauth
|
||||
tailscale.com/util/zstdframe from tailscale.com/control/controlclient
|
||||
tailscale.com/version from tailscale.com/clientupdate+
|
||||
tailscale.com/version/distro from tailscale.com/clientupdate+
|
||||
tailscale.com/version from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/version/distro from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||
@ -249,9 +243,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from tailscale.com/derp
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from archive/tar+
|
||||
bytes from bufio+
|
||||
cmp from encoding/json+
|
||||
compress/flate from compress/gzip
|
||||
compress/gzip from golang.org/x/net/http2+
|
||||
@ -329,10 +322,10 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
encoding/hex from crypto/x509+
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
errors from archive/tar+
|
||||
errors from bufio+
|
||||
expvar from tailscale.com/health+
|
||||
flag from tailscale.com/cmd/tailscaled+
|
||||
fmt from archive/tar+
|
||||
fmt from compress/flate+
|
||||
hash from crypto+
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/maphash from go4.org/mem
|
||||
@ -348,7 +341,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
internal/filepathlite from os+
|
||||
internal/fmtsort from fmt
|
||||
internal/goarch from crypto/internal/fips140deps/cpu+
|
||||
internal/godebug from archive/tar+
|
||||
internal/godebug from crypto/internal/fips140deps/godebug+
|
||||
internal/godebugs from internal/godebug+
|
||||
internal/goexperiment from hash/maphash+
|
||||
internal/goos from crypto/x509+
|
||||
@ -379,14 +372,14 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
internal/testlog from os
|
||||
internal/trace/tracev2 from runtime+
|
||||
internal/unsafeheader from internal/reflectlite+
|
||||
io from archive/tar+
|
||||
io/fs from archive/tar+
|
||||
io from bufio+
|
||||
io/fs from crypto/x509+
|
||||
io/ioutil from github.com/digitalocean/go-smbios/smbios
|
||||
iter from bytes+
|
||||
log from expvar+
|
||||
log/internal from log
|
||||
maps from archive/tar+
|
||||
math from archive/tar+
|
||||
maps from crypto/x509+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from bytes+
|
||||
math/rand from github.com/mdlayher/netlink+
|
||||
@ -405,27 +398,27 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
net/textproto from golang.org/x/net/http/httpguts+
|
||||
net/url from crypto/x509+
|
||||
os from crypto/internal/sysrand+
|
||||
os/exec from tailscale.com/clientupdate+
|
||||
os/exec from tailscale.com/hostinfo+
|
||||
os/signal from tailscale.com/cmd/tailscaled
|
||||
os/user from archive/tar+
|
||||
path from archive/tar+
|
||||
path/filepath from archive/tar+
|
||||
reflect from archive/tar+
|
||||
os/user from tailscale.com/ipn/ipnauth+
|
||||
path from io/fs+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from internal/profile+
|
||||
regexp/syntax from regexp
|
||||
runtime from archive/tar+
|
||||
runtime from crypto/internal/fips140+
|
||||
runtime/debug from github.com/klauspost/compress/zstd+
|
||||
runtime/pprof from net/http/pprof+
|
||||
runtime/trace from net/http/pprof
|
||||
slices from archive/tar+
|
||||
slices from crypto/tls+
|
||||
sort from compress/flate+
|
||||
strconv from archive/tar+
|
||||
strings from archive/tar+
|
||||
sync from archive/tar+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from archive/tar+
|
||||
syscall from crypto/internal/sysrand+
|
||||
text/tabwriter from runtime/pprof
|
||||
time from archive/tar+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from crypto/x509+
|
||||
unicode/utf8 from bufio+
|
||||
|
@ -246,7 +246,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/client/local from tailscale.com/client/web+
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/local+
|
||||
tailscale.com/client/web from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||
tailscale.com/clientupdate from tailscale.com/feature/clientupdate
|
||||
LW tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/cmd/tailscaled/childproc from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/cmd/tailscaled/tailscaledhooks from tailscale.com/cmd/tailscaled+
|
||||
@ -273,6 +273,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
tailscale.com/feature from tailscale.com/feature/wakeonlan+
|
||||
tailscale.com/feature/buildfeatures from tailscale.com/wgengine/magicsock+
|
||||
tailscale.com/feature/capture from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/clientupdate from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/feature/condregister/portmapper from tailscale.com/feature/condregister
|
||||
tailscale.com/feature/debugportmapper from tailscale.com/feature/condregister
|
||||
|
@ -9,7 +9,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
github.com/coder/websocket/internal/errd from github.com/coder/websocket
|
||||
github.com/coder/websocket/internal/util from github.com/coder/websocket
|
||||
github.com/coder/websocket/internal/xsync from github.com/coder/websocket
|
||||
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/com+
|
||||
W 💣 github.com/dblohm7/wingoes from tailscale.com/net/tshttpproxy+
|
||||
W 💣 github.com/dblohm7/wingoes/com from tailscale.com/util/osdiag+
|
||||
W 💣 github.com/dblohm7/wingoes/com/automation from tailscale.com/util/osdiag/internal/wsc
|
||||
W github.com/dblohm7/wingoes/internal from github.com/dblohm7/wingoes/com
|
||||
@ -28,8 +28,8 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
L 💣 github.com/godbus/dbus/v5 from tailscale.com/net/dns
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
github.com/google/btree from gvisor.dev/gvisor/pkg/tcpip/header+
|
||||
DW github.com/google/uuid from github.com/prometheus-community/pro-bing+
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
|
||||
D github.com/google/uuid from github.com/prometheus-community/pro-bing
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/tka
|
||||
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
|
||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
github.com/klauspost/compress from github.com/klauspost/compress/zstd
|
||||
@ -128,8 +128,6 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/client/tailscale from tailscale.com/internal/client/tailscale
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/local+
|
||||
tailscale.com/client/web from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||
LW tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||
tailscale.com/control/controlclient from tailscale.com/ipn/ipnext+
|
||||
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
|
||||
@ -208,7 +206,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
|
||||
tailscale.com/net/tsaddr from tailscale.com/client/web+
|
||||
tailscale.com/net/tsdial from tailscale.com/control/controlclient+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/tstun from tailscale.com/tsd+
|
||||
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/net/udprelay/status from tailscale.com/client/local
|
||||
@ -237,7 +235,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/types/empty from tailscale.com/ipn+
|
||||
tailscale.com/types/ipproto from tailscale.com/ipn+
|
||||
tailscale.com/types/key from tailscale.com/client/local+
|
||||
tailscale.com/types/lazy from tailscale.com/clientupdate+
|
||||
tailscale.com/types/lazy from tailscale.com/cmd/tsidp+
|
||||
tailscale.com/types/logger from tailscale.com/appc+
|
||||
tailscale.com/types/logid from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/types/mapx from tailscale.com/ipn/ipnext
|
||||
@ -256,12 +254,12 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/util/cibuild from tailscale.com/health
|
||||
tailscale.com/util/clientmetric from tailscale.com/appc+
|
||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||
tailscale.com/util/cmpver from tailscale.com/clientupdate+
|
||||
LW tailscale.com/util/cmpver from tailscale.com/net/dns+
|
||||
tailscale.com/util/ctxkey from tailscale.com/client/tailscale/apitype+
|
||||
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
|
||||
L 💣 tailscale.com/util/dirwalk from tailscale.com/metrics
|
||||
tailscale.com/util/dnsname from tailscale.com/appc+
|
||||
tailscale.com/util/eventbus from tailscale.com/ipn/localapi+
|
||||
tailscale.com/util/eventbus from tailscale.com/client/local+
|
||||
tailscale.com/util/execqueue from tailscale.com/appc+
|
||||
tailscale.com/util/goroutines from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/util/groupmember from tailscale.com/client/web+
|
||||
@ -270,7 +268,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/util/lineiter from tailscale.com/hostinfo+
|
||||
tailscale.com/util/mak from tailscale.com/appc+
|
||||
tailscale.com/util/multierr from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/must from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/must from tailscale.com/cmd/tsidp+
|
||||
tailscale.com/util/nocasemaps from tailscale.com/types/ipproto
|
||||
💣 tailscale.com/util/osdiag from tailscale.com/ipn/localapi
|
||||
W 💣 tailscale.com/util/osdiag/internal/wsc from tailscale.com/util/osdiag
|
||||
@ -296,8 +294,8 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
tailscale.com/util/truncate from tailscale.com/logtail
|
||||
tailscale.com/util/usermetric from tailscale.com/health+
|
||||
tailscale.com/util/vizerror from tailscale.com/tailcfg+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/clientupdate+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/clientupdate+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/util/osdiag
|
||||
W 💣 tailscale.com/util/winutil/gp from tailscale.com/net/dns+
|
||||
W tailscale.com/util/winutil/policy from tailscale.com/ipn/ipnlocal
|
||||
W 💣 tailscale.com/util/winutil/winenv from tailscale.com/hostinfo+
|
||||
@ -336,7 +334,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||
LD golang.org/x/crypto/ssh from tailscale.com/ipn/ipnlocal
|
||||
LD golang.org/x/crypto/ssh/internal/bcrypt_pbkdf from golang.org/x/crypto/ssh
|
||||
golang.org/x/exp/constraints from github.com/dblohm7/wingoes/pe+
|
||||
golang.org/x/exp/constraints from tailscale.com/tsweb/varz+
|
||||
golang.org/x/exp/maps from tailscale.com/ipn/store/mem+
|
||||
golang.org/x/net/bpf from github.com/mdlayher/genetlink+
|
||||
golang.org/x/net/dns/dnsmessage from net+
|
||||
@ -371,9 +369,8 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from gvisor.dev/gvisor/pkg/log+
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from archive/tar+
|
||||
bytes from bufio+
|
||||
cmp from encoding/json+
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from golang.org/x/net/http2+
|
||||
@ -446,7 +443,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
crypto/x509 from crypto/tls+
|
||||
D crypto/x509/internal/macos from crypto/x509
|
||||
crypto/x509/pkix from crypto/x509+
|
||||
DW database/sql/driver from github.com/google/uuid
|
||||
D database/sql/driver from github.com/google/uuid
|
||||
W debug/dwarf from debug/pe
|
||||
W debug/pe from github.com/dblohm7/wingoes/pe
|
||||
embed from github.com/tailscale/web-client-prebuilt+
|
||||
@ -459,11 +456,11 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
encoding/xml from github.com/tailscale/goupnp+
|
||||
errors from archive/tar+
|
||||
errors from bufio+
|
||||
expvar from tailscale.com/health+
|
||||
flag from tailscale.com/cmd/tsidp+
|
||||
fmt from archive/tar+
|
||||
hash from compress/zlib+
|
||||
fmt from compress/flate+
|
||||
hash from crypto+
|
||||
W hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/maphash from go4.org/mem
|
||||
@ -480,7 +477,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
internal/filepathlite from os+
|
||||
internal/fmtsort from fmt+
|
||||
internal/goarch from crypto/internal/fips140deps/cpu+
|
||||
internal/godebug from archive/tar+
|
||||
internal/godebug from crypto/internal/fips140deps/godebug+
|
||||
internal/godebugs from internal/godebug+
|
||||
internal/goexperiment from hash/maphash+
|
||||
internal/goos from crypto/x509+
|
||||
@ -516,14 +513,14 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
internal/testlog from os
|
||||
internal/trace/tracev2 from runtime+
|
||||
internal/unsafeheader from internal/reflectlite+
|
||||
io from archive/tar+
|
||||
io/fs from archive/tar+
|
||||
io from bufio+
|
||||
io/fs from crypto/x509+
|
||||
io/ioutil from github.com/digitalocean/go-smbios/smbios+
|
||||
iter from bytes+
|
||||
log from expvar+
|
||||
log/internal from log
|
||||
maps from archive/tar+
|
||||
math from archive/tar+
|
||||
maps from crypto/x509+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from bytes+
|
||||
math/rand from github.com/fxamacker/cbor/v2+
|
||||
@ -545,28 +542,28 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
os from crypto/internal/sysrand+
|
||||
os/exec from github.com/godbus/dbus/v5+
|
||||
os/signal from tailscale.com/cmd/tsidp
|
||||
os/user from archive/tar+
|
||||
path from archive/tar+
|
||||
path/filepath from archive/tar+
|
||||
reflect from archive/tar+
|
||||
os/user from github.com/godbus/dbus/v5+
|
||||
path from debug/dwarf+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from github.com/tailscale/goupnp/httpu+
|
||||
regexp/syntax from regexp
|
||||
runtime from archive/tar+
|
||||
runtime from crypto/internal/fips140+
|
||||
runtime/debug from github.com/coder/websocket/internal/xsync+
|
||||
runtime/pprof from net/http/pprof+
|
||||
runtime/trace from net/http/pprof
|
||||
slices from archive/tar+
|
||||
slices from crypto/tls+
|
||||
sort from compress/flate+
|
||||
strconv from archive/tar+
|
||||
strings from archive/tar+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
W structs from internal/syscall/windows
|
||||
sync from archive/tar+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from archive/tar+
|
||||
syscall from crypto/internal/sysrand+
|
||||
text/tabwriter from runtime/pprof
|
||||
text/template from html/template
|
||||
text/template/parse from html/template+
|
||||
time from archive/tar+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from crypto/x509+
|
||||
unicode/utf8 from bufio+
|
||||
|
13
feature/buildfeatures/feature_clientupdate_disabled.go
Normal file
13
feature/buildfeatures/feature_clientupdate_disabled.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by gen.go; DO NOT EDIT.
|
||||
|
||||
//go:build ts_omit_clientupdate
|
||||
|
||||
package buildfeatures
|
||||
|
||||
// HasClientUpdate is whether the binary was built with support for modular feature "Client auto-update support".
|
||||
// Specifically, it's whether the binary was NOT built with the "ts_omit_clientupdate" build tag.
|
||||
// It's a const so it can be used for dead code elimination.
|
||||
const HasClientUpdate = false
|
13
feature/buildfeatures/feature_clientupdate_enabled.go
Normal file
13
feature/buildfeatures/feature_clientupdate_enabled.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by gen.go; DO NOT EDIT.
|
||||
|
||||
//go:build !ts_omit_clientupdate
|
||||
|
||||
package buildfeatures
|
||||
|
||||
// HasClientUpdate is whether the binary was built with support for modular feature "Client auto-update support".
|
||||
// Specifically, it's whether the binary was NOT built with the "ts_omit_clientupdate" build tag.
|
||||
// It's a const so it can be used for dead code elimination.
|
||||
const HasClientUpdate = true
|
530
feature/clientupdate/clientupdate.go
Normal file
530
feature/clientupdate/clientupdate.go
Normal file
@ -0,0 +1,530 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package clientupdate enables the client update feature.
|
||||
package clientupdate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnext"
|
||||
"tailscale.com/ipn/ipnlocal"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/ipn/localapi"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/httpm"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ipnext.RegisterExtension("clientupdate", newExt)
|
||||
|
||||
// C2N
|
||||
ipnlocal.RegisterC2N("GET /update", handleC2NUpdateGet)
|
||||
ipnlocal.RegisterC2N("POST /update", handleC2NUpdatePost)
|
||||
|
||||
// LocalAPI:
|
||||
localapi.Register("update/install", serveUpdateInstall)
|
||||
localapi.Register("update/progress", serveUpdateProgress)
|
||||
}
|
||||
|
||||
func newExt(logf logger.Logf, sb ipnext.SafeBackend) (ipnext.Extension, error) {
|
||||
return &extension{
|
||||
logf: logf,
|
||||
sb: sb,
|
||||
|
||||
lastSelfUpdateState: ipnstate.UpdateFinished,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type extension struct {
|
||||
logf logger.Logf
|
||||
sb ipnext.SafeBackend
|
||||
|
||||
mu sync.Mutex
|
||||
|
||||
// c2nUpdateStatus is the status of c2n-triggered client update.
|
||||
c2nUpdateStatus updateStatus
|
||||
prefs ipn.PrefsView
|
||||
state ipn.State
|
||||
|
||||
lastSelfUpdateState ipnstate.SelfUpdateStatus
|
||||
selfUpdateProgress []ipnstate.UpdateProgress
|
||||
|
||||
// offlineAutoUpdateCancel stops offline auto-updates when called. It
|
||||
// should be used via stopOfflineAutoUpdate and
|
||||
// maybeStartOfflineAutoUpdate. It is nil when offline auto-updates are
|
||||
// not running.
|
||||
//
|
||||
//lint:ignore U1000 only used in Linux and Windows builds in autoupdate.go
|
||||
offlineAutoUpdateCancel func()
|
||||
}
|
||||
|
||||
func (e *extension) Name() string { return "clientupdate" }
|
||||
|
||||
func (e *extension) Init(h ipnext.Host) error {
|
||||
|
||||
h.Hooks().ProfileStateChange.Add(e.onChangeProfile)
|
||||
h.Hooks().BackendStateChange.Add(e.onBackendStateChange)
|
||||
|
||||
// TODO(nickkhyl): remove this after the profileManager refactoring.
|
||||
// See tailscale/tailscale#15974.
|
||||
// This same workaround appears in feature/portlist/portlist.go.
|
||||
profile, prefs := h.Profiles().CurrentProfileState()
|
||||
e.onChangeProfile(profile, prefs, false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *extension) Shutdown() error {
|
||||
e.stopOfflineAutoUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *extension) onBackendStateChange(newState ipn.State) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.state = newState
|
||||
e.updateOfflineAutoUpdateLocked()
|
||||
}
|
||||
|
||||
func (e *extension) onChangeProfile(profile ipn.LoginProfileView, prefs ipn.PrefsView, sameNode bool) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.prefs = prefs
|
||||
e.updateOfflineAutoUpdateLocked()
|
||||
}
|
||||
|
||||
func (e *extension) updateOfflineAutoUpdateLocked() {
|
||||
want := e.prefs.Valid() && e.prefs.AutoUpdate().Apply.EqualBool(true) &&
|
||||
e.state != ipn.Running && e.state != ipn.Starting
|
||||
|
||||
cur := e.offlineAutoUpdateCancel != nil
|
||||
|
||||
if want && !cur {
|
||||
e.maybeStartOfflineAutoUpdateLocked(e.prefs)
|
||||
} else if !want && cur {
|
||||
e.stopOfflineAutoUpdateLocked()
|
||||
}
|
||||
}
|
||||
|
||||
type updateStatus struct {
|
||||
started bool
|
||||
}
|
||||
|
||||
func (e *extension) clearSelfUpdateProgress() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.selfUpdateProgress = make([]ipnstate.UpdateProgress, 0)
|
||||
e.lastSelfUpdateState = ipnstate.UpdateFinished
|
||||
}
|
||||
|
||||
func (e *extension) GetSelfUpdateProgress() []ipnstate.UpdateProgress {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
res := make([]ipnstate.UpdateProgress, len(e.selfUpdateProgress))
|
||||
copy(res, e.selfUpdateProgress)
|
||||
return res
|
||||
}
|
||||
|
||||
func (e *extension) DoSelfUpdate() {
|
||||
e.mu.Lock()
|
||||
updateState := e.lastSelfUpdateState
|
||||
e.mu.Unlock()
|
||||
// don't start an update if one is already in progress
|
||||
if updateState == ipnstate.UpdateInProgress {
|
||||
return
|
||||
}
|
||||
e.clearSelfUpdateProgress()
|
||||
e.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateInProgress, ""))
|
||||
up, err := clientupdate.NewUpdater(clientupdate.Arguments{
|
||||
Logf: func(format string, args ...any) {
|
||||
e.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateInProgress, fmt.Sprintf(format, args...)))
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
e.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFailed, err.Error()))
|
||||
}
|
||||
err = up.Update()
|
||||
if err != nil {
|
||||
e.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFailed, err.Error()))
|
||||
} else {
|
||||
e.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFinished, "tailscaled did not restart; please restart Tailscale manually."))
|
||||
}
|
||||
}
|
||||
|
||||
// serveUpdateInstall sends a request to the LocalBackend to start a Tailscale
|
||||
// self-update. A successful response does not indicate whether the update
|
||||
// succeeded, only that the request was accepted. Clients should use
|
||||
// serveUpdateProgress after pinging this endpoint to check how the update is
|
||||
// going.
|
||||
func serveUpdateInstall(h *localapi.Handler, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != httpm.POST {
|
||||
http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
b := h.LocalBackend()
|
||||
ext, ok := ipnlocal.GetExt[*extension](b)
|
||||
if !ok {
|
||||
http.Error(w, "clientupdate extension not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
go ext.DoSelfUpdate()
|
||||
}
|
||||
|
||||
// serveUpdateProgress returns the status of an in-progress Tailscale self-update.
|
||||
// This is provided as a slice of ipnstate.UpdateProgress structs with various
|
||||
// log messages in order from oldest to newest. If an update is not in progress,
|
||||
// the returned slice will be empty.
|
||||
func serveUpdateProgress(h *localapi.Handler, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != httpm.GET {
|
||||
http.Error(w, "only GET allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
b := h.LocalBackend()
|
||||
ext, ok := ipnlocal.GetExt[*extension](b)
|
||||
if !ok {
|
||||
http.Error(w, "clientupdate extension not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ups := ext.GetSelfUpdateProgress()
|
||||
|
||||
json.NewEncoder(w).Encode(ups)
|
||||
}
|
||||
|
||||
func (e *extension) pushSelfUpdateProgress(up ipnstate.UpdateProgress) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.selfUpdateProgress = append(e.selfUpdateProgress, up)
|
||||
e.lastSelfUpdateState = up.Status
|
||||
}
|
||||
|
||||
func handleC2NUpdateGet(b *ipnlocal.LocalBackend, w http.ResponseWriter, r *http.Request) {
|
||||
e, ok := ipnlocal.GetExt[*extension](b)
|
||||
if !ok {
|
||||
http.Error(w, "clientupdate extension not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
e.logf("c2n: GET /update received")
|
||||
|
||||
res := e.newC2NUpdateResponse()
|
||||
res.Started = e.c2nUpdateStarted()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
|
||||
func handleC2NUpdatePost(b *ipnlocal.LocalBackend, w http.ResponseWriter, r *http.Request) {
|
||||
e, ok := ipnlocal.GetExt[*extension](b)
|
||||
if !ok {
|
||||
http.Error(w, "clientupdate extension not found", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
e.logf("c2n: POST /update received")
|
||||
res := e.newC2NUpdateResponse()
|
||||
defer func() {
|
||||
if res.Err != "" {
|
||||
e.logf("c2n: POST /update failed: %s", res.Err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}()
|
||||
|
||||
if !res.Enabled {
|
||||
res.Err = "not enabled"
|
||||
return
|
||||
}
|
||||
if !res.Supported {
|
||||
res.Err = "not supported"
|
||||
return
|
||||
}
|
||||
|
||||
// Do not update if we have active inbound SSH connections. Control can set
|
||||
// force=true query parameter to override this.
|
||||
if r.FormValue("force") != "true" && b.ActiveSSHConns() > 0 {
|
||||
res.Err = "not updating due to active SSH connections"
|
||||
return
|
||||
}
|
||||
|
||||
if err := e.startAutoUpdate("c2n"); err != nil {
|
||||
res.Err = err.Error()
|
||||
return
|
||||
}
|
||||
res.Started = true
|
||||
}
|
||||
|
||||
func (e *extension) newC2NUpdateResponse() tailcfg.C2NUpdateResponse {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
// If NewUpdater does not return an error, we can update the installation.
|
||||
//
|
||||
// Note that we create the Updater solely to check for errors; we do not
|
||||
// invoke it here. For this purpose, it is ok to pass it a zero Arguments.
|
||||
var upPref ipn.AutoUpdatePrefs
|
||||
if e.prefs.Valid() {
|
||||
upPref = e.prefs.AutoUpdate()
|
||||
}
|
||||
return tailcfg.C2NUpdateResponse{
|
||||
Enabled: envknob.AllowsRemoteUpdate() || upPref.Apply.EqualBool(true),
|
||||
Supported: feature.CanAutoUpdate() && !version.IsMacSysExt(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *extension) c2nUpdateStarted() bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.c2nUpdateStatus.started
|
||||
}
|
||||
|
||||
func (e *extension) setC2NUpdateStarted(v bool) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.c2nUpdateStatus.started = v
|
||||
}
|
||||
|
||||
func (e *extension) trySetC2NUpdateStarted() bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if e.c2nUpdateStatus.started {
|
||||
return false
|
||||
}
|
||||
e.c2nUpdateStatus.started = true
|
||||
return true
|
||||
}
|
||||
|
||||
// findCmdTailscale looks for the cmd/tailscale that corresponds to the
|
||||
// currently running cmd/tailscaled. It's up to the caller to verify that the
|
||||
// two match, but this function does its best to find the right one. Notably, it
|
||||
// doesn't use $PATH for security reasons.
|
||||
func findCmdTailscale() (string, error) {
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var ts string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
if self == "/usr/sbin/tailscaled" || self == "/usr/bin/tailscaled" {
|
||||
ts = "/usr/bin/tailscale"
|
||||
}
|
||||
if self == "/usr/local/sbin/tailscaled" || self == "/usr/local/bin/tailscaled" {
|
||||
ts = "/usr/local/bin/tailscale"
|
||||
}
|
||||
switch distro.Get() {
|
||||
case distro.QNAP:
|
||||
// The volume under /share/ where qpkg are installed is not
|
||||
// predictable. But the rest of the path is.
|
||||
ok, err := filepath.Match("/share/*/.qpkg/Tailscale/tailscaled", self)
|
||||
if err == nil && ok {
|
||||
ts = filepath.Join(filepath.Dir(self), "tailscale")
|
||||
}
|
||||
case distro.Unraid:
|
||||
if self == "/usr/local/emhttp/plugins/tailscale/bin/tailscaled" {
|
||||
ts = "/usr/local/emhttp/plugins/tailscale/bin/tailscale"
|
||||
}
|
||||
}
|
||||
case "windows":
|
||||
ts = filepath.Join(filepath.Dir(self), "tailscale.exe")
|
||||
case "freebsd", "openbsd":
|
||||
if self == "/usr/local/bin/tailscaled" {
|
||||
ts = "/usr/local/bin/tailscale"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported OS %v", runtime.GOOS)
|
||||
}
|
||||
if ts != "" && regularFileExists(ts) {
|
||||
return ts, nil
|
||||
}
|
||||
return "", errors.New("tailscale executable not found in expected place")
|
||||
}
|
||||
|
||||
func tailscaleUpdateCmd(cmdTS string) *exec.Cmd {
|
||||
defaultCmd := exec.Command(cmdTS, "update", "--yes")
|
||||
if runtime.GOOS != "linux" {
|
||||
return defaultCmd
|
||||
}
|
||||
if _, err := exec.LookPath("systemd-run"); err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
|
||||
// When systemd-run is available, use it to run the update command. This
|
||||
// creates a new temporary unit separate from the tailscaled unit. When
|
||||
// tailscaled is restarted during the update, systemd won't kill this
|
||||
// temporary update unit, which could cause unexpected breakage.
|
||||
//
|
||||
// We want to use a few optional flags:
|
||||
// * --wait, to block the update command until completion (added in systemd 232)
|
||||
// * --pipe, to collect stdout/stderr (added in systemd 235)
|
||||
// * --collect, to clean up failed runs from memory (added in systemd 236)
|
||||
//
|
||||
// We need to check the version of systemd to figure out if those flags are
|
||||
// available.
|
||||
//
|
||||
// The output will look like:
|
||||
//
|
||||
// systemd 255 (255.7-1-arch)
|
||||
// +PAM +AUDIT ... other feature flags ...
|
||||
systemdVerOut, err := exec.Command("systemd-run", "--version").Output()
|
||||
if err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
parts := strings.Fields(string(systemdVerOut))
|
||||
if len(parts) < 2 || parts[0] != "systemd" {
|
||||
return defaultCmd
|
||||
}
|
||||
systemdVer, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
if systemdVer >= 236 {
|
||||
return exec.Command("systemd-run", "--wait", "--pipe", "--collect", cmdTS, "update", "--yes")
|
||||
} else if systemdVer >= 235 {
|
||||
return exec.Command("systemd-run", "--wait", "--pipe", cmdTS, "update", "--yes")
|
||||
} else if systemdVer >= 232 {
|
||||
return exec.Command("systemd-run", "--wait", cmdTS, "update", "--yes")
|
||||
} else {
|
||||
return exec.Command("systemd-run", cmdTS, "update", "--yes")
|
||||
}
|
||||
}
|
||||
|
||||
func regularFileExists(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// startAutoUpdate triggers an auto-update attempt. The actual update happens
|
||||
// asynchronously. If another update is in progress, an error is returned.
|
||||
func (e *extension) startAutoUpdate(logPrefix string) (retErr error) {
|
||||
// Check if update was already started, and mark as started.
|
||||
if !e.trySetC2NUpdateStarted() {
|
||||
return errors.New("update already started")
|
||||
}
|
||||
defer func() {
|
||||
// Clear the started flag if something failed.
|
||||
if retErr != nil {
|
||||
e.setC2NUpdateStarted(false)
|
||||
}
|
||||
}()
|
||||
|
||||
cmdTS, err := findCmdTailscale()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find cmd/tailscale binary: %w", err)
|
||||
}
|
||||
var ver struct {
|
||||
Long string `json:"long"`
|
||||
}
|
||||
out, err := exec.Command(cmdTS, "version", "--json").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find cmd/tailscale binary: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(out, &ver); err != nil {
|
||||
return fmt.Errorf("invalid JSON from cmd/tailscale version --json: %w", err)
|
||||
}
|
||||
if ver.Long != version.Long() {
|
||||
return fmt.Errorf("cmd/tailscale version %q does not match tailscaled version %q", ver.Long, version.Long())
|
||||
}
|
||||
|
||||
cmd := tailscaleUpdateCmd(cmdTS)
|
||||
buf := new(bytes.Buffer)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = buf
|
||||
e.logf("%s: running %q", logPrefix, strings.Join(cmd.Args, " "))
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start cmd/tailscale update: %w", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
e.logf("%s: update command failed: %v, output: %s", logPrefix, err, buf)
|
||||
} else {
|
||||
e.logf("%s: update attempt complete", logPrefix)
|
||||
}
|
||||
e.setC2NUpdateStarted(false)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *extension) stopOfflineAutoUpdate() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.stopOfflineAutoUpdateLocked()
|
||||
}
|
||||
|
||||
func (e *extension) stopOfflineAutoUpdateLocked() {
|
||||
if e.offlineAutoUpdateCancel == nil {
|
||||
return
|
||||
}
|
||||
e.logf("offline auto-update: stopping update checks")
|
||||
e.offlineAutoUpdateCancel()
|
||||
e.offlineAutoUpdateCancel = nil
|
||||
}
|
||||
|
||||
// e.mu must be held
|
||||
func (e *extension) maybeStartOfflineAutoUpdateLocked(prefs ipn.PrefsView) {
|
||||
if !prefs.Valid() || !prefs.AutoUpdate().Apply.EqualBool(true) {
|
||||
return
|
||||
}
|
||||
// AutoUpdate.Apply field in prefs can only be true for platforms that
|
||||
// support auto-updates. But check it here again, just in case.
|
||||
if !feature.CanAutoUpdate() {
|
||||
return
|
||||
}
|
||||
// On macsys, auto-updates are managed by Sparkle.
|
||||
if version.IsMacSysExt() {
|
||||
return
|
||||
}
|
||||
|
||||
if e.offlineAutoUpdateCancel != nil {
|
||||
// Already running.
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
e.offlineAutoUpdateCancel = cancel
|
||||
|
||||
e.logf("offline auto-update: starting update checks")
|
||||
go e.offlineAutoUpdate(ctx)
|
||||
}
|
||||
|
||||
const offlineAutoUpdateCheckPeriod = time.Hour
|
||||
|
||||
func (e *extension) offlineAutoUpdate(ctx context.Context) {
|
||||
t := time.NewTicker(offlineAutoUpdateCheckPeriod)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
}
|
||||
if err := e.startAutoUpdate("offline auto-update"); err != nil {
|
||||
e.logf("offline auto-update: failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
8
feature/condregister/maybe_clientupdate.go
Normal file
8
feature/condregister/maybe_clientupdate.go
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !ts_omit_clientupdate
|
||||
|
||||
package condregister
|
||||
|
||||
import _ "tailscale.com/feature/clientupdate"
|
@ -97,6 +97,7 @@ var Features = map[FeatureTag]FeatureMeta{
|
||||
"capture": {"Capture", "Packet capture", nil},
|
||||
"cli": {"CLI", "embed the CLI into the tailscaled binary", nil},
|
||||
"cliconndiag": {"CLIConnDiag", "CLI connection error diagnostics", nil},
|
||||
"clientupdate": {"ClientUpdate", "Client auto-update support", nil},
|
||||
"completion": {"Completion", "CLI shell completion", nil},
|
||||
"dbus": {"DBus", "Linux DBus support", nil},
|
||||
"debugeventbus": {"DebugEventBus", "eventbus debug support", nil},
|
||||
|
17
feature/hooks.go
Normal file
17
feature/hooks.go
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package feature
|
||||
|
||||
// HookCanAutoUpdate is a hook for the clientupdate package
|
||||
// to conditionally initialize.
|
||||
var HookCanAutoUpdate Hook[func() bool]
|
||||
|
||||
// CanAutoUpdate reports whether the current binary is built with auto-update
|
||||
// support and, if so, whether the current platform supports it.
|
||||
func CanAutoUpdate() bool {
|
||||
if f, ok := HookCanAutoUpdate.GetOk(); ok {
|
||||
return f()
|
||||
}
|
||||
return false
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build linux || windows
|
||||
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
func (b *LocalBackend) stopOfflineAutoUpdate() {
|
||||
if b.offlineAutoUpdateCancel != nil {
|
||||
b.logf("offline auto-update: stopping update checks")
|
||||
b.offlineAutoUpdateCancel()
|
||||
b.offlineAutoUpdateCancel = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LocalBackend) maybeStartOfflineAutoUpdate(prefs ipn.PrefsView) {
|
||||
if !prefs.AutoUpdate().Apply.EqualBool(true) {
|
||||
return
|
||||
}
|
||||
// AutoUpdate.Apply field in prefs can only be true for platforms that
|
||||
// support auto-updates. But check it here again, just in case.
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
return
|
||||
}
|
||||
// On macsys, auto-updates are managed by Sparkle.
|
||||
if version.IsMacSysExt() {
|
||||
return
|
||||
}
|
||||
|
||||
if b.offlineAutoUpdateCancel != nil {
|
||||
// Already running.
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
b.offlineAutoUpdateCancel = cancel
|
||||
|
||||
b.logf("offline auto-update: starting update checks")
|
||||
go b.offlineAutoUpdate(ctx)
|
||||
}
|
||||
|
||||
const offlineAutoUpdateCheckPeriod = time.Hour
|
||||
|
||||
func (b *LocalBackend) offlineAutoUpdate(ctx context.Context) {
|
||||
t := time.NewTicker(offlineAutoUpdateCheckPeriod)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
}
|
||||
if err := b.startAutoUpdate("offline auto-update"); err != nil {
|
||||
b.logf("offline auto-update: failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !(linux || windows)
|
||||
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"tailscale.com/ipn"
|
||||
)
|
||||
|
||||
func (b *LocalBackend) stopOfflineAutoUpdate() {
|
||||
// Not supported on this platform.
|
||||
}
|
||||
|
||||
func (b *LocalBackend) maybeStartOfflineAutoUpdate(prefs ipn.PrefsView) {
|
||||
// Not supported on this platform.
|
||||
}
|
@ -5,23 +5,16 @@ package ipnlocal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/sockstats"
|
||||
"tailscale.com/posture"
|
||||
@ -34,7 +27,6 @@ import (
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/ptype"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
// c2nHandlers maps an HTTP method and URI path (without query parameters) to
|
||||
@ -60,10 +52,6 @@ var c2nHandlers = map[methodAndPath]c2nHandler{
|
||||
// SSH
|
||||
req("/ssh/usernames"): handleC2NSSHUsernames,
|
||||
|
||||
// Auto-updates.
|
||||
req("GET /update"): handleC2NUpdateGet,
|
||||
req("POST /update"): handleC2NUpdatePost,
|
||||
|
||||
// Device posture.
|
||||
req("GET /posture/identity"): handleC2NPostureIdentityGet,
|
||||
|
||||
@ -337,50 +325,6 @@ func handleC2NSetNetfilterKind(b *LocalBackend, w http.ResponseWriter, r *http.R
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func handleC2NUpdateGet(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
|
||||
b.logf("c2n: GET /update received")
|
||||
|
||||
res := b.newC2NUpdateResponse()
|
||||
res.Started = b.c2nUpdateStarted()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
|
||||
func handleC2NUpdatePost(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
|
||||
b.logf("c2n: POST /update received")
|
||||
res := b.newC2NUpdateResponse()
|
||||
defer func() {
|
||||
if res.Err != "" {
|
||||
b.logf("c2n: POST /update failed: %s", res.Err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}()
|
||||
|
||||
if !res.Enabled {
|
||||
res.Err = "not enabled"
|
||||
return
|
||||
}
|
||||
if !res.Supported {
|
||||
res.Err = "not supported"
|
||||
return
|
||||
}
|
||||
|
||||
// Do not update if we have active inbound SSH connections. Control can set
|
||||
// force=true query parameter to override this.
|
||||
if r.FormValue("force") != "true" && b.sshServer != nil && b.sshServer.NumActiveConns() > 0 {
|
||||
res.Err = "not updating due to active SSH connections"
|
||||
return
|
||||
}
|
||||
|
||||
if err := b.startAutoUpdate("c2n"); err != nil {
|
||||
res.Err = err.Error()
|
||||
return
|
||||
}
|
||||
res.Started = true
|
||||
}
|
||||
|
||||
func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http.Request) {
|
||||
b.logf("c2n: GET /posture/identity received")
|
||||
|
||||
@ -423,137 +367,3 @@ func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
|
||||
func (b *LocalBackend) newC2NUpdateResponse() tailcfg.C2NUpdateResponse {
|
||||
// If NewUpdater does not return an error, we can update the installation.
|
||||
//
|
||||
// Note that we create the Updater solely to check for errors; we do not
|
||||
// invoke it here. For this purpose, it is ok to pass it a zero Arguments.
|
||||
prefs := b.Prefs().AutoUpdate()
|
||||
return tailcfg.C2NUpdateResponse{
|
||||
Enabled: envknob.AllowsRemoteUpdate() || prefs.Apply.EqualBool(true),
|
||||
Supported: clientupdate.CanAutoUpdate() && !version.IsMacSysExt(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LocalBackend) c2nUpdateStarted() bool {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.c2nUpdateStatus.started
|
||||
}
|
||||
|
||||
func (b *LocalBackend) setC2NUpdateStarted(v bool) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.c2nUpdateStatus.started = v
|
||||
}
|
||||
|
||||
func (b *LocalBackend) trySetC2NUpdateStarted() bool {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.c2nUpdateStatus.started {
|
||||
return false
|
||||
}
|
||||
b.c2nUpdateStatus.started = true
|
||||
return true
|
||||
}
|
||||
|
||||
// findCmdTailscale looks for the cmd/tailscale that corresponds to the
|
||||
// currently running cmd/tailscaled. It's up to the caller to verify that the
|
||||
// two match, but this function does its best to find the right one. Notably, it
|
||||
// doesn't use $PATH for security reasons.
|
||||
func findCmdTailscale() (string, error) {
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var ts string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
if self == "/usr/sbin/tailscaled" || self == "/usr/bin/tailscaled" {
|
||||
ts = "/usr/bin/tailscale"
|
||||
}
|
||||
if self == "/usr/local/sbin/tailscaled" || self == "/usr/local/bin/tailscaled" {
|
||||
ts = "/usr/local/bin/tailscale"
|
||||
}
|
||||
switch distro.Get() {
|
||||
case distro.QNAP:
|
||||
// The volume under /share/ where qpkg are installed is not
|
||||
// predictable. But the rest of the path is.
|
||||
ok, err := filepath.Match("/share/*/.qpkg/Tailscale/tailscaled", self)
|
||||
if err == nil && ok {
|
||||
ts = filepath.Join(filepath.Dir(self), "tailscale")
|
||||
}
|
||||
case distro.Unraid:
|
||||
if self == "/usr/local/emhttp/plugins/tailscale/bin/tailscaled" {
|
||||
ts = "/usr/local/emhttp/plugins/tailscale/bin/tailscale"
|
||||
}
|
||||
}
|
||||
case "windows":
|
||||
ts = filepath.Join(filepath.Dir(self), "tailscale.exe")
|
||||
case "freebsd", "openbsd":
|
||||
if self == "/usr/local/bin/tailscaled" {
|
||||
ts = "/usr/local/bin/tailscale"
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported OS %v", runtime.GOOS)
|
||||
}
|
||||
if ts != "" && regularFileExists(ts) {
|
||||
return ts, nil
|
||||
}
|
||||
return "", errors.New("tailscale executable not found in expected place")
|
||||
}
|
||||
|
||||
func tailscaleUpdateCmd(cmdTS string) *exec.Cmd {
|
||||
defaultCmd := exec.Command(cmdTS, "update", "--yes")
|
||||
if runtime.GOOS != "linux" {
|
||||
return defaultCmd
|
||||
}
|
||||
if _, err := exec.LookPath("systemd-run"); err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
|
||||
// When systemd-run is available, use it to run the update command. This
|
||||
// creates a new temporary unit separate from the tailscaled unit. When
|
||||
// tailscaled is restarted during the update, systemd won't kill this
|
||||
// temporary update unit, which could cause unexpected breakage.
|
||||
//
|
||||
// We want to use a few optional flags:
|
||||
// * --wait, to block the update command until completion (added in systemd 232)
|
||||
// * --pipe, to collect stdout/stderr (added in systemd 235)
|
||||
// * --collect, to clean up failed runs from memory (added in systemd 236)
|
||||
//
|
||||
// We need to check the version of systemd to figure out if those flags are
|
||||
// available.
|
||||
//
|
||||
// The output will look like:
|
||||
//
|
||||
// systemd 255 (255.7-1-arch)
|
||||
// +PAM +AUDIT ... other feature flags ...
|
||||
systemdVerOut, err := exec.Command("systemd-run", "--version").Output()
|
||||
if err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
parts := strings.Fields(string(systemdVerOut))
|
||||
if len(parts) < 2 || parts[0] != "systemd" {
|
||||
return defaultCmd
|
||||
}
|
||||
systemdVer, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return defaultCmd
|
||||
}
|
||||
if systemdVer >= 236 {
|
||||
return exec.Command("systemd-run", "--wait", "--pipe", "--collect", cmdTS, "update", "--yes")
|
||||
} else if systemdVer >= 235 {
|
||||
return exec.Command("systemd-run", "--wait", "--pipe", cmdTS, "update", "--yes")
|
||||
} else if systemdVer >= 232 {
|
||||
return exec.Command("systemd-run", "--wait", cmdTS, "update", "--yes")
|
||||
} else {
|
||||
return exec.Command("systemd-run", cmdTS, "update", "--yes")
|
||||
}
|
||||
}
|
||||
|
||||
func regularFileExists(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
@ -25,7 +24,6 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"slices"
|
||||
@ -40,7 +38,6 @@ import (
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/drive"
|
||||
@ -302,22 +299,11 @@ type LocalBackend struct {
|
||||
notifyWatchers map[string]*watchSession // by session ID
|
||||
lastStatusTime time.Time // status.AsOf value of the last processed status update
|
||||
componentLogUntil map[string]componentLogState
|
||||
// c2nUpdateStatus is the status of c2n-triggered client update.
|
||||
c2nUpdateStatus updateStatus
|
||||
currentUser ipnauth.Actor
|
||||
currentUser ipnauth.Actor
|
||||
|
||||
selfUpdateProgress []ipnstate.UpdateProgress
|
||||
lastSelfUpdateState ipnstate.SelfUpdateStatus
|
||||
// capForcedNetfilter is the netfilter that control instructs Linux clients
|
||||
// to use, unless overridden locally.
|
||||
capForcedNetfilter string // TODO(nickkhyl): move to nodeBackend
|
||||
// offlineAutoUpdateCancel stops offline auto-updates when called. It
|
||||
// should be used via stopOfflineAutoUpdate and
|
||||
// maybeStartOfflineAutoUpdate. It is nil when offline auto-updates are
|
||||
// note running.
|
||||
//
|
||||
//lint:ignore U1000 only used in Linux and Windows builds in autoupdate.go
|
||||
offlineAutoUpdateCancel func()
|
||||
|
||||
// ServeConfig fields. (also guarded by mu)
|
||||
lastServeConfJSON mem.RO // last JSON that was parsed into serveConfig
|
||||
@ -433,10 +419,6 @@ func (b *LocalBackend) NetMon() *netmon.Monitor {
|
||||
return b.sys.NetMon.Get()
|
||||
}
|
||||
|
||||
type updateStatus struct {
|
||||
started bool
|
||||
}
|
||||
|
||||
type metrics struct {
|
||||
// advertisedRoutes is a metric that reports the number of network routes that are advertised by the local node.
|
||||
// This informs the user of how many routes are being advertised by the local node, excluding exit routes.
|
||||
@ -517,8 +499,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
em: newExpiryManager(logf, sys.Bus.Get()),
|
||||
loginFlags: loginFlags,
|
||||
clock: clock,
|
||||
selfUpdateProgress: make([]ipnstate.UpdateProgress, 0),
|
||||
lastSelfUpdateState: ipnstate.UpdateFinished,
|
||||
captiveCtx: captiveCtx,
|
||||
captiveCancel: nil, // so that we start checkCaptivePortalLoop when Running
|
||||
needsCaptiveDetection: make(chan bool),
|
||||
@ -1127,7 +1107,6 @@ func (b *LocalBackend) Shutdown() {
|
||||
defer cancel()
|
||||
b.sockstatLogger.Shutdown(ctx)
|
||||
}
|
||||
b.stopOfflineAutoUpdate()
|
||||
|
||||
b.unregisterSysPolicyWatch()
|
||||
if cc != nil {
|
||||
@ -3412,7 +3391,7 @@ func (b *LocalBackend) onTailnetDefaultAutoUpdate(au bool) {
|
||||
// can still manually enable auto-updates on this node.
|
||||
return
|
||||
}
|
||||
if clientupdate.CanAutoUpdate() {
|
||||
if buildfeatures.HasClientUpdate && feature.CanAutoUpdate() {
|
||||
b.logf("using tailnet default auto-update setting: %v", au)
|
||||
prefsClone := prefs.AsStruct()
|
||||
prefsClone.AutoUpdate.Apply = opt.NewBool(au)
|
||||
@ -4100,7 +4079,12 @@ func (b *LocalBackend) checkFunnelEnabledLocked(p *ipn.Prefs) error {
|
||||
}
|
||||
|
||||
func (b *LocalBackend) checkAutoUpdatePrefsLocked(p *ipn.Prefs) error {
|
||||
if p.AutoUpdate.Apply.EqualBool(true) && !clientupdate.CanAutoUpdate() {
|
||||
if !buildfeatures.HasClientUpdate {
|
||||
if p.AutoUpdate.Apply.EqualBool(true) {
|
||||
return errors.New("Auto-update support is disabled in this build")
|
||||
}
|
||||
}
|
||||
if p.AutoUpdate.Apply.EqualBool(true) && !feature.CanAutoUpdate() {
|
||||
return errors.New("Auto-updates are not supported on this platform.")
|
||||
}
|
||||
return nil
|
||||
@ -4552,14 +4536,6 @@ func (b *LocalBackend) setPrefsLockedOnEntry(newp *ipn.Prefs, unlock unlockOnce)
|
||||
b.resetAlwaysOnOverrideLocked()
|
||||
}
|
||||
|
||||
if newp.AutoUpdate.Apply.EqualBool(true) {
|
||||
if b.state != ipn.Running {
|
||||
b.maybeStartOfflineAutoUpdate(newp.View())
|
||||
}
|
||||
} else {
|
||||
b.stopOfflineAutoUpdate()
|
||||
}
|
||||
|
||||
unlock.UnlockEarly()
|
||||
|
||||
if oldp.ShieldsUp() != newp.ShieldsUp || hostInfoChanged {
|
||||
@ -5467,12 +5443,6 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
|
||||
}
|
||||
b.pauseOrResumeControlClientLocked()
|
||||
|
||||
if newState == ipn.Running {
|
||||
b.stopOfflineAutoUpdate()
|
||||
} else {
|
||||
b.maybeStartOfflineAutoUpdate(prefs)
|
||||
}
|
||||
|
||||
unlock.UnlockEarly()
|
||||
|
||||
// prefs may change irrespective of state; WantRunning should be explicitly
|
||||
@ -6611,6 +6581,15 @@ func (b *LocalBackend) DoNoiseRequest(req *http.Request) (*http.Response, error)
|
||||
return cc.DoNoiseRequest(req)
|
||||
}
|
||||
|
||||
// ActiveSSHConns returns the number of active SSH connections,
|
||||
// or 0 if SSH is not linked into the binary or available on the platform.
|
||||
func (b *LocalBackend) ActiveSSHConns() int {
|
||||
if b.sshServer == nil {
|
||||
return 0
|
||||
}
|
||||
return b.sshServer.NumActiveConns()
|
||||
}
|
||||
|
||||
func (b *LocalBackend) sshServerOrInit() (_ SSHServer, err error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
@ -6941,54 +6920,6 @@ func (b *LocalBackend) DebugBreakDERPConns() error {
|
||||
return b.MagicConn().DebugBreakDERPConns()
|
||||
}
|
||||
|
||||
func (b *LocalBackend) pushSelfUpdateProgress(up ipnstate.UpdateProgress) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.selfUpdateProgress = append(b.selfUpdateProgress, up)
|
||||
b.lastSelfUpdateState = up.Status
|
||||
}
|
||||
|
||||
func (b *LocalBackend) clearSelfUpdateProgress() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.selfUpdateProgress = make([]ipnstate.UpdateProgress, 0)
|
||||
b.lastSelfUpdateState = ipnstate.UpdateFinished
|
||||
}
|
||||
|
||||
func (b *LocalBackend) GetSelfUpdateProgress() []ipnstate.UpdateProgress {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
res := make([]ipnstate.UpdateProgress, len(b.selfUpdateProgress))
|
||||
copy(res, b.selfUpdateProgress)
|
||||
return res
|
||||
}
|
||||
|
||||
func (b *LocalBackend) DoSelfUpdate() {
|
||||
b.mu.Lock()
|
||||
updateState := b.lastSelfUpdateState
|
||||
b.mu.Unlock()
|
||||
// don't start an update if one is already in progress
|
||||
if updateState == ipnstate.UpdateInProgress {
|
||||
return
|
||||
}
|
||||
b.clearSelfUpdateProgress()
|
||||
b.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateInProgress, ""))
|
||||
up, err := clientupdate.NewUpdater(clientupdate.Arguments{
|
||||
Logf: func(format string, args ...any) {
|
||||
b.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateInProgress, fmt.Sprintf(format, args...)))
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
b.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFailed, err.Error()))
|
||||
}
|
||||
err = up.Update()
|
||||
if err != nil {
|
||||
b.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFailed, err.Error()))
|
||||
} else {
|
||||
b.pushSelfUpdateProgress(ipnstate.NewUpdateProgress(ipnstate.UpdateFinished, "tailscaled did not restart; please restart Tailscale manually."))
|
||||
}
|
||||
}
|
||||
|
||||
// ObserveDNSResponse passes a DNS response from the PeerAPI DNS server to the
|
||||
// App Connector to enable route discovery.
|
||||
func (b *LocalBackend) ObserveDNSResponse(res []byte) error {
|
||||
@ -7603,58 +7534,6 @@ func isAllowedAutoExitNodeID(polc policyclient.Client, exitNodeID tailcfg.Stable
|
||||
return true // no policy configured; allow all exit nodes
|
||||
}
|
||||
|
||||
// startAutoUpdate triggers an auto-update attempt. The actual update happens
|
||||
// asynchronously. If another update is in progress, an error is returned.
|
||||
func (b *LocalBackend) startAutoUpdate(logPrefix string) (retErr error) {
|
||||
// Check if update was already started, and mark as started.
|
||||
if !b.trySetC2NUpdateStarted() {
|
||||
return errors.New("update already started")
|
||||
}
|
||||
defer func() {
|
||||
// Clear the started flag if something failed.
|
||||
if retErr != nil {
|
||||
b.setC2NUpdateStarted(false)
|
||||
}
|
||||
}()
|
||||
|
||||
cmdTS, err := findCmdTailscale()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find cmd/tailscale binary: %w", err)
|
||||
}
|
||||
var ver struct {
|
||||
Long string `json:"long"`
|
||||
}
|
||||
out, err := exec.Command(cmdTS, "version", "--json").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find cmd/tailscale binary: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(out, &ver); err != nil {
|
||||
return fmt.Errorf("invalid JSON from cmd/tailscale version --json: %w", err)
|
||||
}
|
||||
if ver.Long != version.Long() {
|
||||
return fmt.Errorf("cmd/tailscale version %q does not match tailscaled version %q", ver.Long, version.Long())
|
||||
}
|
||||
|
||||
cmd := tailscaleUpdateCmd(cmdTS)
|
||||
buf := new(bytes.Buffer)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = buf
|
||||
b.logf("%s: running %q", logPrefix, strings.Join(cmd.Args, " "))
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start cmd/tailscale update: %w", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
b.logf("%s: update command failed: %v, output: %s", logPrefix, err, buf)
|
||||
} else {
|
||||
b.logf("%s: update attempt complete", logPrefix)
|
||||
}
|
||||
b.setC2NUpdateStarted(false)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// srcIPHasCapForFilter is called by the packet filter when evaluating firewall
|
||||
// rules that require a source IP to have a certain node capability.
|
||||
//
|
||||
|
@ -30,10 +30,10 @@ import (
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/appc/appctest"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/drive"
|
||||
"tailscale.com/drive/driveimpl"
|
||||
"tailscale.com/feature"
|
||||
_ "tailscale.com/feature/condregister/portmapper"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/hostinfo"
|
||||
@ -3710,7 +3710,7 @@ func TestOnTailnetDefaultAutoUpdate(t *testing.T) {
|
||||
// On platforms that don't support auto-update we can never
|
||||
// transition to auto-updates being enabled. The value should
|
||||
// remain unchanged after onTailnetDefaultAutoUpdate.
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
if !feature.CanAutoUpdate() {
|
||||
want = tt.before
|
||||
}
|
||||
if got := b.pm.CurrentPrefs().AutoUpdate().Apply; got != want {
|
||||
@ -5455,7 +5455,7 @@ func TestEnableAutoUpdates(t *testing.T) {
|
||||
})
|
||||
// Enabling may fail, depending on which environment we are running this
|
||||
// test in.
|
||||
wantErr := !clientupdate.CanAutoUpdate()
|
||||
wantErr := !feature.CanAutoUpdate()
|
||||
gotErr := err != nil
|
||||
if gotErr != wantErr {
|
||||
t.Fatalf("enabling auto-updates: got error: %v (%v); want error: %v", gotErr, err, wantErr)
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnext"
|
||||
@ -674,7 +674,7 @@ func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error
|
||||
// cause any EditPrefs calls to fail (other than disabling auto-updates).
|
||||
//
|
||||
// Reset AutoUpdate.Apply if we detect such invalid prefs.
|
||||
if savedPrefs.AutoUpdate.Apply.EqualBool(true) && !clientupdate.CanAutoUpdate() {
|
||||
if savedPrefs.AutoUpdate.Apply.EqualBool(true) && !feature.CanAutoUpdate() {
|
||||
savedPrefs.AutoUpdate.Apply.Clear()
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"tailscale.com/clientupdate"
|
||||
_ "tailscale.com/clientupdate" // for feature registration side effects
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/store/mem"
|
||||
@ -464,7 +465,7 @@ func TestProfileManagement(t *testing.T) {
|
||||
wantCurProfile = "user@2.example.com"
|
||||
checkProfiles(t)
|
||||
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
if !feature.CanAutoUpdate() {
|
||||
t.Logf("Save an invalid AutoUpdate pref value")
|
||||
prefs := pm.CurrentPrefs().AsStruct()
|
||||
prefs.AutoUpdate.Apply.Set(true)
|
||||
|
@ -27,8 +27,8 @@ import (
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/feature"
|
||||
"tailscale.com/feature/buildfeatures"
|
||||
"tailscale.com/health/healthmsg"
|
||||
"tailscale.com/hostinfo"
|
||||
@ -120,8 +120,6 @@ var handler = map[string]LocalAPIHandler{
|
||||
"status": (*Handler).serveStatus,
|
||||
"suggest-exit-node": (*Handler).serveSuggestExitNode,
|
||||
"update/check": (*Handler).serveUpdateCheck,
|
||||
"update/install": (*Handler).serveUpdateInstall,
|
||||
"update/progress": (*Handler).serveUpdateProgress,
|
||||
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
||||
"usermetrics": (*Handler).serveUserMetrics,
|
||||
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
||||
@ -1897,7 +1895,7 @@ func (h *Handler) serveUpdateCheck(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
if !feature.CanAutoUpdate() {
|
||||
// if we don't support auto-update, just say that we're up to date
|
||||
json.NewEncoder(w).Encode(tailcfg.ClientVersion{RunningLatest: true})
|
||||
return
|
||||
@ -1915,37 +1913,6 @@ func (h *Handler) serveUpdateCheck(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(cv)
|
||||
}
|
||||
|
||||
// serveUpdateInstall sends a request to the LocalBackend to start a Tailscale
|
||||
// self-update. A successful response does not indicate whether the update
|
||||
// succeeded, only that the request was accepted. Clients should use
|
||||
// serveUpdateProgress after pinging this endpoint to check how the update is
|
||||
// going.
|
||||
func (h *Handler) serveUpdateInstall(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != httpm.POST {
|
||||
http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
go h.b.DoSelfUpdate()
|
||||
}
|
||||
|
||||
// serveUpdateProgress returns the status of an in-progress Tailscale self-update.
|
||||
// This is provided as a slice of ipnstate.UpdateProgress structs with various
|
||||
// log messages in order from oldest to newest. If an update is not in progress,
|
||||
// the returned slice will be empty.
|
||||
func (h *Handler) serveUpdateProgress(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != httpm.GET {
|
||||
http.Error(w, "only GET allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
ups := h.b.GetSelfUpdateProgress()
|
||||
|
||||
json.NewEncoder(w).Encode(ups)
|
||||
}
|
||||
|
||||
// serveDNSOSConfig serves the current system DNS configuration as a JSON object, if
|
||||
// supported by the OS.
|
||||
func (h *Handler) serveDNSOSConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -9,7 +9,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
LDW github.com/coder/websocket/internal/errd from github.com/coder/websocket
|
||||
LDW github.com/coder/websocket/internal/util from github.com/coder/websocket
|
||||
LDW github.com/coder/websocket/internal/xsync from github.com/coder/websocket
|
||||
W 💣 github.com/dblohm7/wingoes from github.com/dblohm7/wingoes/com+
|
||||
W 💣 github.com/dblohm7/wingoes from tailscale.com/net/tshttpproxy+
|
||||
W 💣 github.com/dblohm7/wingoes/com from tailscale.com/util/osdiag+
|
||||
W 💣 github.com/dblohm7/wingoes/com/automation from tailscale.com/util/osdiag/internal/wsc
|
||||
W github.com/dblohm7/wingoes/internal from github.com/dblohm7/wingoes/com
|
||||
@ -28,8 +28,8 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
L 💣 github.com/godbus/dbus/v5 from tailscale.com/net/dns
|
||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||
github.com/google/btree from gvisor.dev/gvisor/pkg/tcpip/header+
|
||||
DWI github.com/google/uuid from github.com/prometheus-community/pro-bing+
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+
|
||||
DI github.com/google/uuid from github.com/prometheus-community/pro-bing
|
||||
github.com/hdevalence/ed25519consensus from tailscale.com/tka
|
||||
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/netmon
|
||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
github.com/klauspost/compress from github.com/klauspost/compress/zstd
|
||||
@ -124,8 +124,6 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/client/tailscale from tailscale.com/internal/client/tailscale
|
||||
tailscale.com/client/tailscale/apitype from tailscale.com/client/local+
|
||||
LDW tailscale.com/client/web from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/clientupdate from tailscale.com/client/web+
|
||||
LW tailscale.com/clientupdate/distsign from tailscale.com/clientupdate
|
||||
tailscale.com/control/controlbase from tailscale.com/control/controlhttp+
|
||||
tailscale.com/control/controlclient from tailscale.com/ipn/ipnext+
|
||||
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
|
||||
@ -204,7 +202,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/net/tlsdial/blockblame from tailscale.com/net/tlsdial
|
||||
tailscale.com/net/tsaddr from tailscale.com/client/web+
|
||||
tailscale.com/net/tsdial from tailscale.com/control/controlclient+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/clientupdate/distsign+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/tstun from tailscale.com/tsd+
|
||||
tailscale.com/net/udprelay/endpoint from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/net/udprelay/status from tailscale.com/client/local
|
||||
@ -232,7 +230,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/types/empty from tailscale.com/ipn+
|
||||
tailscale.com/types/ipproto from tailscale.com/ipn+
|
||||
tailscale.com/types/key from tailscale.com/client/local+
|
||||
tailscale.com/types/lazy from tailscale.com/clientupdate+
|
||||
tailscale.com/types/lazy from tailscale.com/hostinfo+
|
||||
tailscale.com/types/logger from tailscale.com/appc+
|
||||
tailscale.com/types/logid from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/types/mapx from tailscale.com/ipn/ipnext
|
||||
@ -251,12 +249,12 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/util/cibuild from tailscale.com/health
|
||||
tailscale.com/util/clientmetric from tailscale.com/appc+
|
||||
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
|
||||
tailscale.com/util/cmpver from tailscale.com/clientupdate+
|
||||
LW tailscale.com/util/cmpver from tailscale.com/net/dns+
|
||||
tailscale.com/util/ctxkey from tailscale.com/client/tailscale/apitype+
|
||||
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
|
||||
LA 💣 tailscale.com/util/dirwalk from tailscale.com/metrics
|
||||
tailscale.com/util/dnsname from tailscale.com/appc+
|
||||
tailscale.com/util/eventbus from tailscale.com/ipn/localapi+
|
||||
tailscale.com/util/eventbus from tailscale.com/client/local+
|
||||
tailscale.com/util/execqueue from tailscale.com/appc+
|
||||
tailscale.com/util/goroutines from tailscale.com/ipn/ipnlocal
|
||||
tailscale.com/util/groupmember from tailscale.com/client/web+
|
||||
@ -265,7 +263,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/util/lineiter from tailscale.com/hostinfo+
|
||||
tailscale.com/util/mak from tailscale.com/appc+
|
||||
tailscale.com/util/multierr from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/must from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/must from tailscale.com/logpolicy+
|
||||
tailscale.com/util/nocasemaps from tailscale.com/types/ipproto
|
||||
💣 tailscale.com/util/osdiag from tailscale.com/ipn/localapi
|
||||
W 💣 tailscale.com/util/osdiag/internal/wsc from tailscale.com/util/osdiag
|
||||
@ -291,8 +289,8 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
tailscale.com/util/truncate from tailscale.com/logtail
|
||||
tailscale.com/util/usermetric from tailscale.com/health+
|
||||
tailscale.com/util/vizerror from tailscale.com/tailcfg+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/clientupdate+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/clientupdate+
|
||||
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
|
||||
W 💣 tailscale.com/util/winutil/authenticode from tailscale.com/util/osdiag
|
||||
W 💣 tailscale.com/util/winutil/gp from tailscale.com/net/dns+
|
||||
W tailscale.com/util/winutil/policy from tailscale.com/ipn/ipnlocal
|
||||
W 💣 tailscale.com/util/winutil/winenv from tailscale.com/hostinfo+
|
||||
@ -329,7 +327,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||
LD golang.org/x/crypto/ssh from tailscale.com/ipn/ipnlocal
|
||||
LD golang.org/x/crypto/ssh/internal/bcrypt_pbkdf from golang.org/x/crypto/ssh
|
||||
golang.org/x/exp/constraints from github.com/dblohm7/wingoes/pe+
|
||||
golang.org/x/exp/constraints from tailscale.com/tsweb/varz+
|
||||
golang.org/x/exp/maps from tailscale.com/ipn/store/mem+
|
||||
golang.org/x/net/bpf from github.com/mdlayher/genetlink+
|
||||
golang.org/x/net/dns/dnsmessage from net+
|
||||
@ -364,9 +362,8 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
|
||||
golang.org/x/text/unicode/norm from golang.org/x/net/idna
|
||||
golang.org/x/time/rate from gvisor.dev/gvisor/pkg/log+
|
||||
archive/tar from tailscale.com/clientupdate
|
||||
bufio from compress/flate+
|
||||
bytes from archive/tar+
|
||||
bytes from bufio+
|
||||
cmp from encoding/json+
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from golang.org/x/net/http2+
|
||||
@ -439,7 +436,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
crypto/x509 from crypto/tls+
|
||||
DI crypto/x509/internal/macos from crypto/x509
|
||||
crypto/x509/pkix from crypto/x509+
|
||||
DWI database/sql/driver from github.com/google/uuid
|
||||
DI database/sql/driver from github.com/google/uuid
|
||||
W debug/dwarf from debug/pe
|
||||
W debug/pe from github.com/dblohm7/wingoes/pe
|
||||
embed from github.com/tailscale/web-client-prebuilt+
|
||||
@ -452,11 +449,11 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
encoding/xml from github.com/tailscale/goupnp+
|
||||
errors from archive/tar+
|
||||
errors from bufio+
|
||||
expvar from tailscale.com/health+
|
||||
flag from tailscale.com/util/testenv
|
||||
fmt from archive/tar+
|
||||
hash from compress/zlib+
|
||||
fmt from compress/flate+
|
||||
hash from crypto+
|
||||
W hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/maphash from go4.org/mem
|
||||
@ -473,7 +470,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
internal/filepathlite from os+
|
||||
internal/fmtsort from fmt+
|
||||
internal/goarch from crypto/internal/fips140deps/cpu+
|
||||
internal/godebug from archive/tar+
|
||||
internal/godebug from crypto/internal/fips140deps/godebug+
|
||||
internal/godebugs from internal/godebug+
|
||||
internal/goexperiment from hash/maphash+
|
||||
internal/goos from crypto/x509+
|
||||
@ -509,14 +506,14 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
internal/testlog from os
|
||||
internal/trace/tracev2 from runtime+
|
||||
internal/unsafeheader from internal/reflectlite+
|
||||
io from archive/tar+
|
||||
io/fs from archive/tar+
|
||||
io from bufio+
|
||||
io/fs from crypto/x509+
|
||||
io/ioutil from github.com/digitalocean/go-smbios/smbios+
|
||||
iter from bytes+
|
||||
log from expvar+
|
||||
log/internal from log
|
||||
maps from archive/tar+
|
||||
math from archive/tar+
|
||||
maps from crypto/x509+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from bytes+
|
||||
math/rand from github.com/fxamacker/cbor/v2+
|
||||
@ -537,28 +534,28 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
net/url from crypto/x509+
|
||||
os from crypto/internal/sysrand+
|
||||
os/exec from github.com/godbus/dbus/v5+
|
||||
os/user from archive/tar+
|
||||
path from archive/tar+
|
||||
path/filepath from archive/tar+
|
||||
reflect from archive/tar+
|
||||
os/user from github.com/godbus/dbus/v5+
|
||||
path from debug/dwarf+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from github.com/tailscale/goupnp/httpu+
|
||||
regexp/syntax from regexp
|
||||
runtime from archive/tar+
|
||||
runtime from crypto/internal/fips140+
|
||||
runtime/debug from github.com/coder/websocket/internal/xsync+
|
||||
runtime/pprof from net/http/pprof+
|
||||
runtime/trace from net/http/pprof
|
||||
slices from archive/tar+
|
||||
slices from crypto/tls+
|
||||
sort from compress/flate+
|
||||
strconv from archive/tar+
|
||||
strings from archive/tar+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
W structs from internal/syscall/windows
|
||||
sync from archive/tar+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from archive/tar+
|
||||
syscall from crypto/internal/sysrand+
|
||||
text/tabwriter from runtime/pprof
|
||||
LDW text/template from html/template
|
||||
LDW text/template/parse from html/template+
|
||||
time from archive/tar+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from crypto/x509+
|
||||
unicode/utf8 from bufio+
|
||||
|
@ -33,8 +33,9 @@ import (
|
||||
"go4.org/mem"
|
||||
"tailscale.com/client/local"
|
||||
"tailscale.com/client/tailscale"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/cmd/testwrapper/flakytest"
|
||||
"tailscale.com/feature"
|
||||
_ "tailscale.com/feature/clientupdate"
|
||||
"tailscale.com/hostinfo"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/tsaddr"
|
||||
@ -1125,7 +1126,7 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAutoUpdateDefaults(t *testing.T) {
|
||||
if !clientupdate.CanAutoUpdate() {
|
||||
if !feature.CanAutoUpdate() {
|
||||
t.Skip("auto-updates not supported on this platform")
|
||||
}
|
||||
tstest.Shard(t)
|
||||
|
Loading…
x
Reference in New Issue
Block a user