envknob: add new package for all the strconv.ParseBool(os.Getenv(..))

A new package can also later record/report which knobs are checked and
set. It also makes the code cleaner & easier to grep for env knobs.

Change-Id: Id8a123ab7539f1fadbd27e0cbeac79c2e4f09751
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2022-01-24 10:52:57 -08:00 committed by Brad Fitzpatrick
parent 6feb8f4c51
commit 41fd4eab5c
39 changed files with 195 additions and 128 deletions

View File

@ -16,7 +16,6 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
"unicode/utf8" "unicode/utf8"
@ -26,6 +25,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/client/tailscale/apitype" "tailscale.com/client/tailscale/apitype"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -148,7 +148,7 @@ func runCp(ctx context.Context, args []string) error {
name = filepath.Base(fileArg) name = filepath.Base(fileArg)
} }
if slow, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_SLOW_PUSH")); slow { if envknob.Bool("TS_DEBUG_SLOW_PUSH") {
fileContents = &slowReader{r: fileContents} fileContents = &slowReader{r: fileContents}
} }
} }

View File

@ -13,13 +13,13 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/peterbourgon/ff/v3/ffcli" "github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/netcheck" "tailscale.com/net/netcheck"
"tailscale.com/net/portmapper" "tailscale.com/net/portmapper"
@ -49,7 +49,7 @@ var netcheckArgs struct {
func runNetcheck(ctx context.Context, args []string) error { func runNetcheck(ctx context.Context, args []string) error {
c := &netcheck.Client{ c := &netcheck.Client{
UDPBindAddr: os.Getenv("TS_DEBUG_NETCHECK_UDP_BIND"), UDPBindAddr: envknob.String("TS_DEBUG_NETCHECK_UDP_BIND"),
PortMapper: portmapper.NewClient(logger.WithPrefix(log.Printf, "portmap: "), nil), PortMapper: portmapper.NewClient(logger.WithPrefix(log.Printf, "portmap: "), nil),
} }
if netcheckArgs.verbose { if netcheckArgs.verbose {

View File

@ -38,6 +38,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/derp/derphttp from tailscale.com/net/netcheck tailscale.com/derp/derphttp from tailscale.com/net/netcheck
L tailscale.com/derp/wsconn from tailscale.com/derp/derphttp L tailscale.com/derp/wsconn from tailscale.com/derp/derphttp
tailscale.com/disco from tailscale.com/derp tailscale.com/disco from tailscale.com/derp
tailscale.com/envknob from tailscale.com/cmd/tailscale/cli+
tailscale.com/hostinfo from tailscale.com/net/interfaces tailscale.com/hostinfo from tailscale.com/net/interfaces
tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+
tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+

View File

@ -24,6 +24,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/derp/derphttp" "tailscale.com/derp/derphttp"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/interfaces" "tailscale.com/net/interfaces"
"tailscale.com/net/portmapper" "tailscale.com/net/portmapper"
@ -224,7 +225,7 @@ func debugPortmap(ctx context.Context) error {
defer cancel() defer cancel()
portmapper.VerboseLogs = true portmapper.VerboseLogs = true
switch os.Getenv("TS_DEBUG_PORTMAP_TYPE") { switch envknob.String("TS_DEBUG_PORTMAP_TYPE") {
case "": case "":
case "pmp": case "pmp":
portmapper.DisablePCP = true portmapper.DisablePCP = true

View File

@ -171,6 +171,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/derp/derphttp from tailscale.com/cmd/tailscaled+ tailscale.com/derp/derphttp from tailscale.com/cmd/tailscaled+
L tailscale.com/derp/wsconn from tailscale.com/derp/derphttp L tailscale.com/derp/wsconn from tailscale.com/derp/derphttp
tailscale.com/disco from tailscale.com/derp+ tailscale.com/disco from tailscale.com/derp+
tailscale.com/envknob from tailscale.com/cmd/tailscaled+
tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/hostinfo from tailscale.com/control/controlclient+ tailscale.com/hostinfo from tailscale.com/control/controlclient+
tailscale.com/ipn from tailscale.com/client/tailscale+ tailscale.com/ipn from tailscale.com/client/tailscale+

View File

@ -23,12 +23,12 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnserver" "tailscale.com/ipn/ipnserver"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
@ -224,7 +224,7 @@ func statePathOrDefault() string {
func ipnServerOpts() (o ipnserver.Options) { func ipnServerOpts() (o ipnserver.Options) {
// Allow changing the OS-specific IPN behavior for tests // Allow changing the OS-specific IPN behavior for tests
// so we can e.g. test Windows-specific behaviors on Linux. // so we can e.g. test Windows-specific behaviors on Linux.
goos := os.Getenv("TS_DEBUG_TAILSCALED_IPN_GOOS") goos := envknob.String("TS_DEBUG_TAILSCALED_IPN_GOOS")
if goos == "" { if goos == "" {
goos = runtime.GOOS goos = runtime.GOOS
} }
@ -272,13 +272,13 @@ func run() error {
} }
var logf logger.Logf = log.Printf var logf logger.Logf = log.Printf
if v, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_MEMORY")); v { if envknob.Bool("TS_DEBUG_MEMORY") {
logf = logger.RusagePrefixLog(logf) logf = logger.RusagePrefixLog(logf)
} }
logf = logger.RateLimitedFn(logf, 5*time.Second, 5, 100) logf = logger.RateLimitedFn(logf, 5*time.Second, 5, 100)
if args.cleanup { if args.cleanup {
if os.Getenv("TS_PLEASE_PANIC") != "" { if envknob.Bool("TS_PLEASE_PANIC") {
panic("TS_PLEASE_PANIC asked us to panic") panic("TS_PLEASE_PANIC asked us to panic")
} }
dns.Cleanup(logf, args.tunname) dns.Cleanup(logf, args.tunname)
@ -431,11 +431,7 @@ func createEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer)
var wrapNetstack = shouldWrapNetstack() var wrapNetstack = shouldWrapNetstack()
func shouldWrapNetstack() bool { func shouldWrapNetstack() bool {
if e := os.Getenv("TS_DEBUG_WRAP_NETSTACK"); e != "" { if v, ok := envknob.LookupBool("TS_DEBUG_WRAP_NETSTACK"); ok {
v, err := strconv.ParseBool(e)
if err != nil {
log.Fatalf("invalid TS_DEBUG_WRAP_NETSTACK value: %v", err)
}
return v return v
} }
if distro.Get() == distro.Synology { if distro.Get() == distro.Synology {

View File

@ -29,6 +29,7 @@ import (
"golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnserver" "tailscale.com/ipn/ipnserver"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
"tailscale.com/net/dns" "tailscale.com/net/dns"
@ -314,7 +315,7 @@ func startIPNServer(ctx context.Context, logid string) error {
// not called concurrently and is not called again once it // not called concurrently and is not called again once it
// successfully returns an engine. // successfully returns an engine.
getEngine := func() (wgengine.Engine, error) { getEngine := func() (wgengine.Engine, error) {
if msg := os.Getenv("TS_DEBUG_WIN_FAIL"); msg != "" { if msg := envknob.String("TS_DEBUG_WIN_FAIL"); msg != "" {
return nil, fmt.Errorf("pretending to be a service failure: %v", msg) return nil, fmt.Errorf("pretending to be a service failure: %v", msg)
} }
for { for {

View File

@ -21,7 +21,6 @@ import (
"os/exec" "os/exec"
"reflect" "reflect"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -30,6 +29,7 @@ import (
"go4.org/mem" "go4.org/mem"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/control/controlknobs" "tailscale.com/control/controlknobs"
"tailscale.com/envknob"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -874,8 +874,8 @@ func decode(res *http.Response, v interface{}, serverKey key.MachinePublic, mkey
} }
var ( var (
debugMap, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_MAP")) debugMap = envknob.Bool("TS_DEBUG_MAP")
debugRegister, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_REGISTER")) debugRegister = envknob.Bool("TS_DEBUG_REGISTER")
) )
var jsonEscapedZero = []byte(`\u0000`) var jsonEscapedZero = []byte(`\u0000`)
@ -985,26 +985,14 @@ type debug struct {
func initDebug() debug { func initDebug() debug {
return debug{ return debug{
NetMap: envBool("TS_DEBUG_NETMAP"), NetMap: envknob.Bool("TS_DEBUG_NETMAP"),
ProxyDNS: envBool("TS_DEBUG_PROXY_DNS"), ProxyDNS: envknob.Bool("TS_DEBUG_PROXY_DNS"),
StripEndpoints: envBool("TS_DEBUG_STRIP_ENDPOINTS"), StripEndpoints: envknob.Bool("TS_DEBUG_STRIP_ENDPOINTS"),
StripCaps: envBool("TS_DEBUG_STRIP_CAPS"), StripCaps: envknob.Bool("TS_DEBUG_STRIP_CAPS"),
Disco: os.Getenv("TS_DEBUG_USE_DISCO") == "" || envBool("TS_DEBUG_USE_DISCO"), Disco: envknob.BoolDefaultTrue("TS_DEBUG_USE_DISCO"),
} }
} }
func envBool(k string) bool {
e := os.Getenv(k)
if e == "" {
return false
}
v, err := strconv.ParseBool(e)
if err != nil {
panic(fmt.Sprintf("invalid non-bool %q for env var %q", e, k))
}
return v
}
var clockNow = time.Now var clockNow = time.Now
// opt.Bool configs from control. // opt.Bool configs from control.

View File

@ -6,11 +6,10 @@ package controlclient
import ( import (
"log" "log"
"os"
"sort" "sort"
"strconv"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -289,7 +288,7 @@ func cloneNodes(v1 []*tailcfg.Node) []*tailcfg.Node {
return v2 return v2
} }
var debugSelfIPv6Only, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_SELF_V6_ONLY")) var debugSelfIPv6Only = envknob.Bool("TS_DEBUG_SELF_V6_ONLY")
func filterSelfAddresses(in []netaddr.IPPrefix) (ret []netaddr.IPPrefix) { func filterSelfAddresses(in []netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
switch { switch {

View File

@ -7,9 +7,7 @@
package controlknobs package controlknobs
import ( import (
"os" "tailscale.com/envknob"
"strconv"
"tailscale.com/syncs" "tailscale.com/syncs"
) )
@ -17,8 +15,7 @@ import (
var disableUPnP syncs.AtomicBool var disableUPnP syncs.AtomicBool
func init() { func init() {
v, _ := strconv.ParseBool(os.Getenv("TS_DISABLE_UPNP")) SetDisableUPnP(envknob.Bool("TS_DISABLE_UPNP"))
SetDisableUPnP(v)
} }
// DisableUPnP reports the last reported value from control // DisableUPnP reports the last reported value from control

View File

@ -25,7 +25,6 @@ import (
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"os"
"os/exec" "os/exec"
"runtime" "runtime"
"strconv" "strconv"
@ -40,6 +39,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/disco" "tailscale.com/disco"
"tailscale.com/envknob"
"tailscale.com/metrics" "tailscale.com/metrics"
"tailscale.com/syncs" "tailscale.com/syncs"
"tailscale.com/types/key" "tailscale.com/types/key"
@ -48,14 +48,14 @@ import (
"tailscale.com/version" "tailscale.com/version"
) )
var debug, _ = strconv.ParseBool(os.Getenv("DERP_DEBUG_LOGS")) var debug = envknob.Bool("DERP_DEBUG_LOGS")
// verboseDropKeys is the set of destination public keys that should // verboseDropKeys is the set of destination public keys that should
// verbosely log whenever DERP drops a packet. // verbosely log whenever DERP drops a packet.
var verboseDropKeys = map[key.NodePublic]bool{} var verboseDropKeys = map[key.NodePublic]bool{}
func init() { func init() {
keys := os.Getenv("TS_DEBUG_VERBOSE_DROPS") keys := envknob.String("TS_DEBUG_VERBOSE_DROPS")
if keys == "" { if keys == "" {
return return
} }

View File

@ -23,9 +23,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -33,6 +31,7 @@ import (
"go4.org/mem" "go4.org/mem"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/derp" "tailscale.com/derp"
"tailscale.com/envknob"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
"tailscale.com/net/netns" "tailscale.com/net/netns"
"tailscale.com/net/tlsdial" "tailscale.com/net/tlsdial"
@ -190,8 +189,7 @@ func useWebsockets() bool {
return true return true
} }
if dialWebsocketFunc != nil { if dialWebsocketFunc != nil {
v, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_DERP_WS_CLIENT")) return envknob.Bool("TS_DEBUG_DERP_WS_CLIENT")
return v
} }
return false return false
} }

102
envknob/envknob.go Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package envknob provides access to environment-variable tweakable
// debug settings.
//
// These are primarily knobs used by Tailscale developers during
// development or by users when instructed to by Tailscale developers
// when debugging something. They are not a stable interface and may
// be removed or any time.
//
// A related package, control/controlknobs, are knobs that can be
// changed at runtime by the control plane. Sometimes both are used:
// an envknob for the default/explicit value, else falling back
// to the controlknob value.
package envknob
import (
"log"
"os"
"strconv"
"tailscale.com/types/opt"
)
// String returns the named environment variable, using os.Getenv.
//
// In the future it will also track usage for reporting on debug pages.
func String(envVar string) string {
return os.Getenv(envVar)
}
// Bool returns the boolean value of the named environment variable.
// If the variable is not set, it returns false.
// An invalid value exits the binary with a failure.
func Bool(envVar string) bool {
return boolOr(envVar, false)
}
// BoolDefaultTrue is like Bool, but returns true by default if the
// environment variable isn't present.
func BoolDefaultTrue(envVar string) bool {
return boolOr(envVar, true)
}
func boolOr(envVar string, implicitValue bool) bool {
val := os.Getenv(envVar)
if val == "" {
return implicitValue
}
b, err := strconv.ParseBool(val)
if err == nil {
return b
}
log.Fatalf("invalid environment variable %s value %q: %v", envVar, val, err)
panic("unreachable")
}
// LookupBool returns the boolean value of the named environment value.
// The ok result is whether a value was set.
// If the value isn't a valid int, it exits the program with a failure.
func LookupBool(envVar string) (v bool, ok bool) {
val := os.Getenv(envVar)
if val == "" {
return false, false
}
b, err := strconv.ParseBool(val)
if err == nil {
return b, true
}
log.Fatalf("invalid environment variable %s value %q: %v", envVar, val, err)
panic("unreachable")
}
// OptBool is like Bool, but returns an opt.Bool, so the caller can
// distinguish between implicitly and explicitly false.
func OptBool(envVar string) opt.Bool {
b, ok := LookupBool(envVar)
if !ok {
return ""
}
var ret opt.Bool
ret.Set(b)
return ret
}
// LookupInt returns the integer value of the named environment value.
// The ok result is whether a value was set.
// If the value isn't a valid int, it exits the program with a failure.
func LookupInt(envVar string) (v int, ok bool) {
val := os.Getenv(envVar)
if val == "" {
return 0, false
}
v, err := strconv.Atoi(val)
if err == nil {
return v, true
}
log.Fatalf("invalid environment variable %s value %q: %v", envVar, val, err)
panic("unreachable")
}

View File

@ -10,13 +10,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"runtime" "runtime"
"sort" "sort"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"tailscale.com/envknob"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/util/multierr" "tailscale.com/util/multierr"
) )
@ -308,7 +308,7 @@ func OverallError() error {
return overallErrorLocked() return overallErrorLocked()
} }
var fakeErrForTesting = os.Getenv("TS_DEBUG_FAKE_HEALTH_ERROR") var fakeErrForTesting = envknob.String("TS_DEBUG_FAKE_HEALTH_ERROR")
func overallErrorLocked() error { func overallErrorLocked() error {
if !anyInterfaceUp { if !anyInterfaceUp {

View File

@ -27,6 +27,7 @@ import (
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/client/tailscale/apitype" "tailscale.com/client/tailscale/apitype"
"tailscale.com/control/controlclient" "tailscale.com/control/controlclient"
"tailscale.com/envknob"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn" "tailscale.com/ipn"
@ -64,7 +65,7 @@ import (
var controlDebugFlags = getControlDebugFlags() var controlDebugFlags = getControlDebugFlags()
func getControlDebugFlags() []string { func getControlDebugFlags() []string {
if e := os.Getenv("TS_DEBUG_CONTROL_FLAGS"); e != "" { if e := envknob.String("TS_DEBUG_CONTROL_FLAGS"); e != "" {
return strings.Split(e, ",") return strings.Split(e, ",")
} }
return nil return nil
@ -1349,7 +1350,7 @@ func (b *LocalBackend) popBrowserAuthNow() {
} }
// For testing lazy machine key generation. // For testing lazy machine key generation.
var panicOnMachineKeyGeneration, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_PANIC_MACHINE_KEY")) var panicOnMachineKeyGeneration = envknob.Bool("TS_DEBUG_PANIC_MACHINE_KEY")
func (b *LocalBackend) createGetMachinePrivateKeyFunc() func() (key.MachinePrivate, error) { func (b *LocalBackend) createGetMachinePrivateKeyFunc() func() (key.MachinePrivate, error) {
var cache atomic.Value var cache atomic.Value

View File

@ -29,12 +29,12 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"golang.org/x/crypto/acme" "golang.org/x/crypto/acme"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -63,7 +63,7 @@ func (h *Handler) certDir() (string, error) {
return full, nil return full, nil
} }
var acmeDebug, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_ACME")) var acmeDebug = envknob.Bool("TS_DEBUG_ACME")
func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) { func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite { if !h.PermitWrite {

View File

@ -25,13 +25,13 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"golang.org/x/term" "golang.org/x/term"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/envknob"
"tailscale.com/log/filelogger" "tailscale.com/log/filelogger"
"tailscale.com/logtail" "tailscale.com/logtail"
"tailscale.com/logtail/filch" "tailscale.com/logtail/filch"
@ -227,7 +227,7 @@ func runningUnderSystemd() bool {
} }
func redirectStderrToLogPanics() bool { func redirectStderrToLogPanics() bool {
return runningUnderSystemd() || os.Getenv("TS_PLEASE_PANIC") != "" return runningUnderSystemd() || envknob.Bool("TS_PLEASE_PANIC")
} }
// winProgramDataAccessible reports whether the directory (assumed to // winProgramDataAccessible reports whether the directory (assumed to
@ -405,7 +405,7 @@ func New(collection string) *Policy {
} else { } else {
lflags = log.LstdFlags lflags = log.LstdFlags
} }
if v, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_LOG_TIME")); v { if envknob.Bool("TS_DEBUG_LOG_TIME") {
lflags = log.LstdFlags | log.Lmicroseconds lflags = log.LstdFlags | log.Lmicroseconds
} }
if runningUnderSystemd() { if runningUnderSystemd() {
@ -670,7 +670,7 @@ func NewLogtailTransport(host string) *http.Transport {
// TODO(bradfitz): remove this debug knob once we've decided // TODO(bradfitz): remove this debug knob once we've decided
// to upload via HTTP/1 or HTTP/2 (probably HTTP/1). Or we might just enforce // to upload via HTTP/1 or HTTP/2 (probably HTTP/1). Or we might just enforce
// it server-side. // it server-side.
if h1, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_FORCE_H1_LOGS")); h1 { if envknob.Bool("TS_DEBUG_FORCE_H1_LOGS") {
tr.TLSClientConfig = nil // DefaultTransport's was already initialized w/ h2 tr.TLSClientConfig = nil // DefaultTransport's was already initialized w/ h2
tr.ForceAttemptHTTP2 = false tr.ForceAttemptHTTP2 = false
tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{} tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}

View File

@ -7,10 +7,8 @@ package dns
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"sort" "sort"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -19,6 +17,7 @@ import (
"golang.org/x/sys/windows/registry" "golang.org/x/sys/windows/registry"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
) )
@ -36,7 +35,7 @@ const (
versionKey = `SOFTWARE\Microsoft\Windows NT\CurrentVersion` versionKey = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
) )
var configureWSL, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_CONFIGURE_WSL")) var configureWSL = envknob.Bool("TS_DEBUG_CONFIGURE_WSL")
type windowsManager struct { type windowsManager struct {
logf logger.Logf logf logger.Logf

View File

@ -15,14 +15,13 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"os"
"runtime" "runtime"
"strconv"
"sync" "sync"
"time" "time"
"golang.org/x/sync/singleflight" "golang.org/x/sync/singleflight"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
) )
var single = &Resolver{ var single = &Resolver{
@ -100,7 +99,7 @@ func (r *Resolver) ttl() time.Duration {
return 10 * time.Minute return 10 * time.Minute
} }
var debug, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_DNS_CACHE")) var debug = envknob.Bool("TS_DEBUG_DNS_CACHE")
// LookupIP returns the host's primary IP address (either IPv4 or // LookupIP returns the host's primary IP address (either IPv4 or
// IPv6, but preferring IPv4) and optionally its IPv6 address, if // IPv6, but preferring IPv4) and optionally its IPv6 address, if

View File

@ -16,16 +16,15 @@ import (
"log" "log"
"net" "net"
"net/http" "net/http"
"os"
"runtime" "runtime"
"sort" "sort"
"strconv"
"sync" "sync"
"time" "time"
"github.com/tcnksm/go-httpstat" "github.com/tcnksm/go-httpstat"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/derp/derphttp" "tailscale.com/derp/derphttp"
"tailscale.com/envknob"
"tailscale.com/net/interfaces" "tailscale.com/net/interfaces"
"tailscale.com/net/neterror" "tailscale.com/net/neterror"
"tailscale.com/net/netns" "tailscale.com/net/netns"
@ -40,7 +39,7 @@ import (
// Debugging and experimentation tweakables. // Debugging and experimentation tweakables.
var ( var (
debugNetcheck, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_NETCHECK")) debugNetcheck = envknob.Bool("TS_DEBUG_NETCHECK")
) )
// The various default timeouts for things. // The various default timeouts for things.

View File

@ -17,10 +17,11 @@ import (
"errors" "errors"
"log" "log"
"os" "os"
"strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"tailscale.com/envknob"
) )
var counterFallbackOK int32 // atomic var counterFallbackOK int32 // atomic
@ -31,7 +32,7 @@ var counterFallbackOK int32 // atomic
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
var sslKeyLogFile = os.Getenv("SSLKEYLOGFILE") var sslKeyLogFile = os.Getenv("SSLKEYLOGFILE")
var debug, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_TLS_DIAL")) var debug = envknob.Bool("TS_DEBUG_TLS_DIAL")
// Config returns a tls.Config for connecting to a server. // Config returns a tls.Config for connecting to a server.
// If base is non-nil, it's cloned as the base config before // If base is non-nil, it's cloned as the base config before

View File

@ -11,13 +11,12 @@ package tstun
import ( import (
"errors" "errors"
"os"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
"tailscale.com/envknob"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -32,7 +31,7 @@ import (
var tunMTU = 1280 var tunMTU = 1280
func init() { func init() {
if mtu, _ := strconv.Atoi(os.Getenv("TS_DEBUG_MTU")); mtu != 0 { if mtu, ok := envknob.LookupInt("TS_DEBUG_MTU"); ok {
tunMTU = mtu tunMTU = mtu
} }
} }

View File

@ -6,10 +6,10 @@ package portlist
import ( import (
"fmt" "fmt"
"os"
"sort" "sort"
"strconv"
"strings" "strings"
"tailscale.com/envknob"
) )
// Port is a listening port on the machine. // Port is a listening port on the machine.
@ -74,7 +74,7 @@ func (pl List) String() string {
return strings.TrimRight(sb.String(), "\n") return strings.TrimRight(sb.String(), "\n")
} }
var debugDisablePortlist, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_DISABLE_PORTLIST")) var debugDisablePortlist = envknob.Bool("TS_DEBUG_DISABLE_PORTLIST")
func GetList(prev List) (List, error) { func GetList(prev List) (List, error) {
if debugDisablePortlist { if debugDisablePortlist {

View File

@ -10,7 +10,7 @@
check_file() { check_file() {
got=$1 got=$1
for year in `seq 2019 2021`; do for year in `seq 2019 2022`; do
want=$(cat <<EOF want=$(cat <<EOF
// Copyright (c) $year Tailscale Inc & AUTHORS All rights reserved. // Copyright (c) $year Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style

View File

@ -14,6 +14,7 @@ import (
_ "inet.af/netaddr" _ "inet.af/netaddr"
_ "tailscale.com/chirp" _ "tailscale.com/chirp"
_ "tailscale.com/derp/derphttp" _ "tailscale.com/derp/derphttp"
_ "tailscale.com/envknob"
_ "tailscale.com/ipn" _ "tailscale.com/ipn"
_ "tailscale.com/ipn/ipnserver" _ "tailscale.com/ipn/ipnserver"
_ "tailscale.com/logpolicy" _ "tailscale.com/logpolicy"

View File

@ -14,6 +14,7 @@ import (
_ "inet.af/netaddr" _ "inet.af/netaddr"
_ "tailscale.com/chirp" _ "tailscale.com/chirp"
_ "tailscale.com/derp/derphttp" _ "tailscale.com/derp/derphttp"
_ "tailscale.com/envknob"
_ "tailscale.com/ipn" _ "tailscale.com/ipn"
_ "tailscale.com/ipn/ipnserver" _ "tailscale.com/ipn/ipnserver"
_ "tailscale.com/logpolicy" _ "tailscale.com/logpolicy"

View File

@ -14,6 +14,7 @@ import (
_ "inet.af/netaddr" _ "inet.af/netaddr"
_ "tailscale.com/chirp" _ "tailscale.com/chirp"
_ "tailscale.com/derp/derphttp" _ "tailscale.com/derp/derphttp"
_ "tailscale.com/envknob"
_ "tailscale.com/ipn" _ "tailscale.com/ipn"
_ "tailscale.com/ipn/ipnserver" _ "tailscale.com/ipn/ipnserver"
_ "tailscale.com/logpolicy" _ "tailscale.com/logpolicy"

View File

@ -14,6 +14,7 @@ import (
_ "inet.af/netaddr" _ "inet.af/netaddr"
_ "tailscale.com/chirp" _ "tailscale.com/chirp"
_ "tailscale.com/derp/derphttp" _ "tailscale.com/derp/derphttp"
_ "tailscale.com/envknob"
_ "tailscale.com/ipn" _ "tailscale.com/ipn"
_ "tailscale.com/ipn/ipnserver" _ "tailscale.com/ipn/ipnserver"
_ "tailscale.com/logpolicy" _ "tailscale.com/logpolicy"

View File

@ -17,6 +17,7 @@ import (
_ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" _ "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
_ "inet.af/netaddr" _ "inet.af/netaddr"
_ "tailscale.com/derp/derphttp" _ "tailscale.com/derp/derphttp"
_ "tailscale.com/envknob"
_ "tailscale.com/ipn" _ "tailscale.com/ipn"
_ "tailscale.com/ipn/ipnserver" _ "tailscale.com/ipn/ipnserver"
_ "tailscale.com/logpolicy" _ "tailscale.com/logpolicy"

View File

@ -25,6 +25,7 @@ import (
"time" "time"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/metrics" "tailscale.com/metrics"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -70,12 +71,12 @@ func AllowDebugAccess(r *http.Request) bool {
if err != nil { if err != nil {
return false return false
} }
if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == os.Getenv("TS_ALLOW_DEBUG_IP") { if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == envknob.String("TS_ALLOW_DEBUG_IP") {
return true return true
} }
if r.Method == "GET" { if r.Method == "GET" {
urlKey := r.FormValue("debugkey") urlKey := r.FormValue("debugkey")
keyPath := os.Getenv("TS_DEBUG_KEY_PATH") keyPath := envknob.String("TS_DEBUG_KEY_PATH")
if urlKey != "" && keyPath != "" { if urlKey != "" && keyPath != "" {
slurp, err := ioutil.ReadFile(keyPath) slurp, err := ioutil.ReadFile(keyPath)
if err == nil && string(bytes.TrimSpace(slurp)) == urlKey { if err == nil && string(bytes.TrimSpace(slurp)) == urlKey {

View File

@ -14,12 +14,13 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"os"
"strings" "strings"
"sync" "sync"
"time" "time"
"context" "context"
"tailscale.com/envknob"
) )
// Logf is the basic Tailscale logger type: a printf-like func. // Logf is the basic Tailscale logger type: a printf-like func.
@ -83,7 +84,7 @@ type limitData struct {
ele *list.Element // list element used to access this string in the cache ele *list.Element // list element used to access this string in the cache
} }
var disableRateLimit = os.Getenv("TS_DEBUG_LOG_RATE") == "all" var disableRateLimit = envknob.String("TS_DEBUG_LOG_RATE") == "all"
// rateFree are format string substrings that are exempt from rate limiting. // rateFree are format string substrings that are exempt from rate limiting.
// Things should not be added to this unless they're already limited otherwise // Things should not be added to this unless they're already limited otherwise

View File

@ -7,11 +7,11 @@ package filter
import ( import (
"fmt" "fmt"
"os"
"sync" "sync"
"time" "time"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/net/flowtrack" "tailscale.com/net/flowtrack"
"tailscale.com/net/packet" "tailscale.com/net/packet"
"tailscale.com/tstime/rate" "tailscale.com/tstime/rate"
@ -225,7 +225,7 @@ var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10)
// effectively disable the limits on the log rate by setting the limit // effectively disable the limits on the log rate by setting the limit
// to 1 millisecond. This should capture everything. // to 1 millisecond. This should capture everything.
func init() { func init() {
if os.Getenv("TS_DEBUG_FILTER_RATE_LIMIT_LOGS") != "all" { if envknob.String("TS_DEBUG_FILTER_RATE_LIMIT_LOGS") != "all" {
return return
} }

View File

@ -8,8 +8,7 @@
package magicsock package magicsock
import ( import (
"os" "tailscale.com/envknob"
"strconv"
) )
// Various debugging and experimental tweakables, set by environment // Various debugging and experimental tweakables, set by environment
@ -17,25 +16,23 @@ import (
var ( var (
// debugDisco prints verbose logs of active discovery events as // debugDisco prints verbose logs of active discovery events as
// they happen. // they happen.
debugDisco, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_DISCO")) debugDisco = envknob.Bool("TS_DEBUG_DISCO")
// debugOmitLocalAddresses removes all local interface addresses // debugOmitLocalAddresses removes all local interface addresses
// from magicsock's discovered local endpoints. Used in some tests. // from magicsock's discovered local endpoints. Used in some tests.
debugOmitLocalAddresses, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_OMIT_LOCAL_ADDRS")) debugOmitLocalAddresses = envknob.Bool("TS_DEBUG_OMIT_LOCAL_ADDRS")
// debugUseDerpRoute temporarily (2020-03-22) controls whether DERP // debugUseDerpRoute temporarily (2020-03-22) controls whether DERP
// reverse routing is enabled (Issue 150). It will become always true // reverse routing is enabled (Issue 150).
// later. debugUseDerpRoute = envknob.OptBool("TS_DEBUG_ENABLE_DERP_ROUTE")
debugUseDerpRouteEnv = os.Getenv("TS_DEBUG_ENABLE_DERP_ROUTE")
debugUseDerpRoute, _ = strconv.ParseBool(debugUseDerpRouteEnv)
// logDerpVerbose logs all received DERP packets, including their // logDerpVerbose logs all received DERP packets, including their
// full payload. // full payload.
logDerpVerbose, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_DERP")) logDerpVerbose = envknob.Bool("TS_DEBUG_DERP")
// debugReSTUNStopOnIdle unconditionally enables the "shut down // debugReSTUNStopOnIdle unconditionally enables the "shut down
// STUN if magicsock is idle" behavior that normally only triggers // STUN if magicsock is idle" behavior that normally only triggers
// on mobile devices, lowers the shutdown interval, and logs more // on mobile devices, lowers the shutdown interval, and logs more
// verbosely about idle measurements. // verbosely about idle measurements.
debugReSTUNStopOnIdle, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_RESTUN_STOP_ON_IDLE")) debugReSTUNStopOnIdle = envknob.Bool("TS_DEBUG_RESTUN_STOP_ON_IDLE")
// debugAlwaysDERP disables the use of UDP, forcing all peer communication over DERP. // debugAlwaysDERP disables the use of UDP, forcing all peer communication over DERP.
debugAlwaysDERP, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_ALWAYS_USE_DERP")) debugAlwaysDERP = envknob.Bool("TS_DEBUG_ALWAYS_USE_DERP")
) )
// inTest reports whether the running program is a test that set the // inTest reports whether the running program is a test that set the
@ -44,7 +41,4 @@ var (
// Unlike the other debug tweakables above, this one needs to be // Unlike the other debug tweakables above, this one needs to be
// checked every time at runtime, because tests set this after program // checked every time at runtime, because tests set this after program
// startup. // startup.
func inTest() bool { func inTest() bool { return envknob.Bool("IN_TS_TEST") }
inTest, _ := strconv.ParseBool(os.Getenv("IN_TS_TEST"))
return inTest
}

View File

@ -61,8 +61,8 @@ import (
// useDerpRoute reports whether magicsock should enable the DERP // useDerpRoute reports whether magicsock should enable the DERP
// return path optimization (Issue 150). // return path optimization (Issue 150).
func useDerpRoute() bool { func useDerpRoute() bool {
if debugUseDerpRouteEnv != "" { if b, ok := debugUseDerpRoute.Get(); ok {
return debugUseDerpRoute return b
} }
ob := controlclient.DERPRouteFlag() ob := controlclient.DERPRouteFlag()
if v, ok := ob.Get(); ok { if v, ok := ob.Get(); ok {

View File

@ -34,6 +34,7 @@ import (
"inet.af/netstack/tcpip/transport/tcp" "inet.af/netstack/tcpip/transport/tcp"
"inet.af/netstack/tcpip/transport/udp" "inet.af/netstack/tcpip/transport/udp"
"inet.af/netstack/waiter" "inet.af/netstack/waiter"
"tailscale.com/envknob"
"tailscale.com/net/packet" "tailscale.com/net/packet"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
@ -49,7 +50,7 @@ import (
const debugPackets = false const debugPackets = false
var debugNetstack, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_NETSTACK")) var debugNetstack = envknob.Bool("TS_DEBUG_NETSTACK")
// Impl contains the state for the netstack implementation, // Impl contains the state for the netstack implementation,
// and implements wgengine.FakeImpl to act as a userspace network // and implements wgengine.FakeImpl to act as a userspace network

View File

@ -6,9 +6,7 @@ package wgengine
import ( import (
"fmt" "fmt"
"os"
"runtime" "runtime"
"strconv"
"time" "time"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
@ -22,17 +20,6 @@ import (
const tcpTimeoutBeforeDebug = 5 * time.Second const tcpTimeoutBeforeDebug = 5 * time.Second
// debugConnectFailures reports whether the local node should track
// outgoing TCP connections and log which ones fail and why.
func debugConnectFailures() bool {
s := os.Getenv("TS_DEBUG_CONNECT_FAILURES")
if s == "" {
return true
}
v, _ := strconv.ParseBool(s)
return v
}
type pendingOpenFlow struct { type pendingOpenFlow struct {
timer *time.Timer // until giving up on the flow timer *time.Timer // until giving up on the flow

View File

@ -22,6 +22,7 @@ import (
"golang.org/x/time/rate" "golang.org/x/time/rate"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/syncs" "tailscale.com/syncs"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -188,7 +189,7 @@ func useAmbientCaps() bool {
return v >= 7 return v >= 7
} }
var forceIPCommand, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_USE_IP_COMMAND")) var forceIPCommand = envknob.Bool("TS_DEBUG_USE_IP_COMMAND")
// useIPCommand reports whether r should use the "ip" command (or its // useIPCommand reports whether r should use the "ip" command (or its
// fake commandRunner for tests) instead of netlink. // fake commandRunner for tests) instead of netlink.

View File

@ -11,10 +11,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os"
"reflect" "reflect"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -25,6 +23,7 @@ import (
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/control/controlclient" "tailscale.com/control/controlclient"
"tailscale.com/envknob"
"tailscale.com/health" "tailscale.com/health"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/dns" "tailscale.com/net/dns"
@ -366,7 +365,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
} }
e.tundev.PreFilterOut = e.handleLocalPackets e.tundev.PreFilterOut = e.handleLocalPackets
if debugConnectFailures() { if envknob.BoolDefaultTrue("TS_DEBUG_CONNECT_FAILURES") {
if e.tundev.PreFilterIn != nil { if e.tundev.PreFilterIn != nil {
return nil, errors.New("unexpected PreFilterIn already set") return nil, errors.New("unexpected PreFilterIn already set")
} }
@ -550,10 +549,7 @@ func (e *userspaceEngine) pollResolver() {
} }
} }
var ( var debugTrimWireguard = envknob.OptBool("TS_DEBUG_TRIM_WIREGUARD")
debugTrimWireguardEnv = os.Getenv("TS_DEBUG_TRIM_WIREGUARD")
debugTrimWireguard, _ = strconv.ParseBool(debugTrimWireguardEnv)
)
// forceFullWireguardConfig reports whether we should give wireguard // forceFullWireguardConfig reports whether we should give wireguard
// our full network map, even for inactive peers // our full network map, even for inactive peers
@ -563,8 +559,8 @@ var (
// and we haven't got enough time testing it. // and we haven't got enough time testing it.
func forceFullWireguardConfig(numPeers int) bool { func forceFullWireguardConfig(numPeers int) bool {
// Did the user explicitly enable trimmming via the environment variable knob? // Did the user explicitly enable trimmming via the environment variable knob?
if debugTrimWireguardEnv != "" { if b, ok := debugTrimWireguard.Get(); ok {
return !debugTrimWireguard return !b
} }
if opt := controlclient.TrimWGConfig(); opt != "" { if opt := controlclient.TrimWGConfig(); opt != "" {
return !opt.EqualBool(true) return !opt.EqualBool(true)

View File

@ -6,13 +6,12 @@ package wgengine
import ( import (
"log" "log"
"os"
"runtime/pprof" "runtime/pprof"
"strconv"
"strings" "strings"
"time" "time"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/net/dns" "tailscale.com/net/dns"
"tailscale.com/net/dns/resolver" "tailscale.com/net/dns/resolver"
@ -32,7 +31,7 @@ import (
// //
// If they do not, the watchdog crashes the process. // If they do not, the watchdog crashes the process.
func NewWatchdog(e Engine) Engine { func NewWatchdog(e Engine) Engine {
if v, _ := strconv.ParseBool(os.Getenv("TS_DEBUG_DISABLE_WATCHDOG")); v { if envknob.Bool("TS_DEBUG_DISABLE_WATCHDOG") {
return e return e
} }
return &watchdogEngine{ return &watchdogEngine{