From 45a3de14a603a2909dad1edfca79e5ccb030c8e2 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 13 Sep 2022 07:09:57 -0700 Subject: [PATCH] cmd/tailscaled, tailcfg, hostinfo: add flag to disable logging + support As noted in #5617, our documented method of blocking log.tailscale.io DNS no longer works due to bootstrap DNS. Instead, provide an explicit flag (--no-logs-no-support) and/or env variable (TS_NO_LOGS_NO_SUPPORT=true) to explicitly disable logcatcher uploads. It also sets a bit on Hostinfo to say that the node is in that mode so we can end any support tickets from such nodes more quickly. This does not yet provide an easy mechanism for users on some platforms (such as Windows, macOS, Synology) to set flags/env. On Linux you'd used /etc/default/tailscaled typically. Making it easier to set flags for other platforms is tracked in #5114. Fixes #5617 Fixes tailscale/corp#1475 Change-Id: I72404e1789f9e56ec47f9b7021b44c025f7a373a Signed-off-by: Brad Fitzpatrick --- cmd/tailscaled/tailscaled.go | 6 ++++++ control/controlclient/direct.go | 1 + envknob/envknob.go | 11 +++++++++++ hostinfo/hostinfo.go | 32 +++++++++++++++++--------------- ipn/localapi/localapi.go | 4 ++++ logpolicy/logpolicy.go | 16 +++++++++++++++- tailcfg/tailcfg.go | 1 + tailcfg/tailcfg_clone.go | 1 + tailcfg/tailcfg_test.go | 1 + tailcfg/tailcfg_view.go | 2 ++ 10 files changed, 59 insertions(+), 16 deletions(-) diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 69c12d9e7..2397bbe48 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -113,6 +113,7 @@ var args struct { verbose int socksAddr string // listen address for SOCKS5 server httpProxyAddr string // listen address for HTTP proxy server + disableLogs bool } var ( @@ -144,6 +145,7 @@ func main() { flag.StringVar(&args.socketpath, "socket", paths.DefaultTailscaledSocket(), "path of the service unix socket") flag.StringVar(&args.birdSocketPath, "bird-socket", "", "path of the bird unix socket") flag.BoolVar(&printVersion, "version", false, "print version information and exit") + flag.BoolVar(&args.disableLogs, "no-logs-no-support", false, "disable log uploads; this also disables any technical support") if len(os.Args) > 0 && filepath.Base(os.Args[0]) == "tailscale" && beCLI != nil { beCLI() @@ -199,6 +201,10 @@ func main() { args.statepath = paths.DefaultTailscaledStateFile() } + if args.disableLogs { + envknob.SetNoLogsNoSupport() + } + if beWindowsSubprocess() { return } diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 80401f4dd..12de6d2ca 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -937,6 +937,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool } if resp.Debug.DisableLogTail { logtail.Disable() + envknob.SetNoLogsNoSupport() } if resp.Debug.LogHeapPprof { go logheap.LogHeap(resp.Debug.LogHeapURL) diff --git a/envknob/envknob.go b/envknob/envknob.go index aa11763cc..ecc731b3d 100644 --- a/envknob/envknob.go +++ b/envknob/envknob.go @@ -155,3 +155,14 @@ func SSHPolicyFile() string { return String("TS_DEBUG_SSH_POLICY_FILE") } // SSHIgnoreTailnetPolicy is whether to ignore the Tailnet SSH policy for development. func SSHIgnoreTailnetPolicy() bool { return Bool("TS_DEBUG_SSH_IGNORE_TAILNET_POLICY") } + +// NoLogsNoSupport reports whether the client's opted out of log uploads and +// technical support. +func NoLogsNoSupport() bool { + return Bool("TS_NO_LOGS_NO_SUPPORT") +} + +// SetNoLogsNoSupport enables no-logs-no-support mode. +func SetNoLogsNoSupport() { + os.Setenv("TS_NO_LOGS_NO_SUPPORT", "true") +} diff --git a/hostinfo/hostinfo.go b/hostinfo/hostinfo.go index d7f239e7c..8d626400c 100644 --- a/hostinfo/hostinfo.go +++ b/hostinfo/hostinfo.go @@ -17,6 +17,7 @@ import ( "time" "go4.org/mem" + "tailscale.com/envknob" "tailscale.com/tailcfg" "tailscale.com/types/opt" "tailscale.com/util/cloudenv" @@ -32,21 +33,22 @@ func New() *tailcfg.Hostinfo { hostname, _ := os.Hostname() hostname = dnsname.FirstLabel(hostname) return &tailcfg.Hostinfo{ - IPNVersion: version.Long, - Hostname: hostname, - OS: version.OS(), - OSVersion: GetOSVersion(), - Container: lazyInContainer.Get(), - Distro: condCall(distroName), - DistroVersion: condCall(distroVersion), - DistroCodeName: condCall(distroCodeName), - Env: string(GetEnvType()), - Desktop: desktop(), - Package: packageTypeCached(), - GoArch: runtime.GOARCH, - GoVersion: runtime.Version(), - DeviceModel: deviceModel(), - Cloud: string(cloudenv.Get()), + IPNVersion: version.Long, + Hostname: hostname, + OS: version.OS(), + OSVersion: GetOSVersion(), + Container: lazyInContainer.Get(), + Distro: condCall(distroName), + DistroVersion: condCall(distroVersion), + DistroCodeName: condCall(distroCodeName), + Env: string(GetEnvType()), + Desktop: desktop(), + Package: packageTypeCached(), + GoArch: runtime.GOARCH, + GoVersion: runtime.Version(), + DeviceModel: deviceModel(), + Cloud: string(cloudenv.Get()), + NoLogsNoSupport: envknob.NoLogsNoSupport(), } } diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index e854992e3..50e4909e9 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -25,6 +25,7 @@ import ( "time" "tailscale.com/client/tailscale/apitype" + "tailscale.com/envknob" "tailscale.com/ipn" "tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnstate" @@ -213,6 +214,9 @@ func (h *Handler) serveBugReport(w http.ResponseWriter, r *http.Request) { } logMarker := fmt.Sprintf("BUG-%v-%v-%v", h.backendLogID, time.Now().UTC().Format("20060102150405Z"), randHex(8)) + if envknob.NoLogsNoSupport() { + logMarker = "BUG-NO-LOGS-NO-SUPPORT-this-node-has-had-its-logging-disabled" + } h.logf("user bugreport: %s", logMarker) if note := r.FormValue("note"); len(note) > 0 { h.logf("user bugreport note: %s", note) diff --git a/logpolicy/logpolicy.go b/logpolicy/logpolicy.go index e201df765..638b8c8e7 100644 --- a/logpolicy/logpolicy.go +++ b/logpolicy/logpolicy.go @@ -539,7 +539,10 @@ func New(collection string) *Policy { conf.IncludeProcSequence = true } - if val := getLogTarget(); val != "" { + if envknob.NoLogsNoSupport() { + log.Println("You have disabled logging. Tailscale will not be able to provide support.") + conf.HTTPC = &http.Client{Transport: noopPretendSuccessTransport{}} + } else if val := getLogTarget(); val != "" { log.Println("You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult.") conf.BaseURL = val u, _ := url.Parse(val) @@ -735,3 +738,14 @@ func goVersion() string { } return v } + +type noopPretendSuccessTransport struct{} + +func (noopPretendSuccessTransport) RoundTrip(req *http.Request) (*http.Response, error) { + io.ReadAll(req.Body) + req.Body.Close() + return &http.Response{ + StatusCode: 200, + Status: "200 OK", + }, nil +} diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 514a01bdd..24f544ad9 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -495,6 +495,7 @@ type Hostinfo struct { Hostname string `json:",omitempty"` // name of the host the client runs on ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user + NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary) GoVersion string `json:",omitempty"` // Go version binary was built with RoutableIPs []netip.Prefix `json:",omitempty"` // set of IP ranges this client can route diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index f89bc6c2f..9bbd396b5 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -131,6 +131,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct { Hostname string ShieldsUp bool ShareeNode bool + NoLogsNoSupport bool GoArch string GoVersion string RoutableIPs []netip.Prefix diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index ce13bef37..c391963db 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -47,6 +47,7 @@ func TestHostinfoEqual(t *testing.T) { "Hostname", "ShieldsUp", "ShareeNode", + "NoLogsNoSupport", "GoArch", "GoVersion", "RoutableIPs", diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index d06489bda..d2d96a7d3 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -266,6 +266,7 @@ func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel } func (v HostinfoView) Hostname() string { return v.ж.Hostname } func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp } func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode } +func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport } func (v HostinfoView) GoArch() string { return v.ж.GoArch } func (v HostinfoView) GoVersion() string { return v.ж.GoVersion } func (v HostinfoView) RoutableIPs() views.IPPrefixSlice { @@ -298,6 +299,7 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct { Hostname string ShieldsUp bool ShareeNode bool + NoLogsNoSupport bool GoArch string GoVersion string RoutableIPs []netip.Prefix