mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-27 11:41:14 +00:00
cmd/tailscaled: default --encrypt-state to true if TPM is available (#17376)
Whenever running on a platform that has a TPM (and tailscaled can access it), default to encrypting the state. The user can still explicitly set this flag to disable encryption. Updates https://github.com/tailscale/corp/issues/32909 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
31
cmd/tailscaled/flag.go
Normal file
31
cmd/tailscaled/flag.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package main
|
||||
|
||||
import "strconv"
|
||||
|
||||
// boolFlag is a flag.Value that tracks whether it was ever set.
|
||||
type boolFlag struct {
|
||||
set bool
|
||||
v bool
|
||||
}
|
||||
|
||||
func (b *boolFlag) String() string {
|
||||
if b == nil || !b.set {
|
||||
return "unset"
|
||||
}
|
||||
return strconv.FormatBool(b.v)
|
||||
}
|
||||
|
||||
func (b *boolFlag) Set(s string) error {
|
||||
v, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.v = v
|
||||
b.set = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boolFlag) IsBoolFlag() bool { return true }
|
||||
@@ -120,7 +120,7 @@ var args struct {
|
||||
debug string
|
||||
port uint16
|
||||
statepath string
|
||||
encryptState bool
|
||||
encryptState boolFlag
|
||||
statedir string
|
||||
socketpath string
|
||||
birdSocketPath string
|
||||
@@ -197,7 +197,7 @@ func main() {
|
||||
flag.StringVar(&args.tunname, "tun", defaultTunName(), `tunnel interface name; use "userspace-networking" (beta) to not use TUN`)
|
||||
flag.Var(flagtype.PortValue(&args.port, defaultPort()), "port", "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select")
|
||||
flag.StringVar(&args.statepath, "state", "", "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM; use 'mem:' to not store state and register as an ephemeral node. If empty and --statedir is provided, the default is <statedir>/tailscaled.state. Default: "+paths.DefaultTailscaledStateFile())
|
||||
flag.BoolVar(&args.encryptState, "encrypt-state", defaultEncryptState(), "encrypt the state file on disk; uses TPM on Linux and Windows, on all other platforms this flag is not supported")
|
||||
flag.Var(&args.encryptState, "encrypt-state", `encrypt the state file on disk; when not set encryption will be enabled if supported on this platform; uses TPM on Linux and Windows, on all other platforms this flag is not supported`)
|
||||
flag.StringVar(&args.statedir, "statedir", "", "path to directory for storage of config state, TLS certs, temporary incoming Taildrop files, etc. If empty, it's derived from --state when possible.")
|
||||
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")
|
||||
@@ -275,7 +275,10 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if args.encryptState {
|
||||
if !args.encryptState.set {
|
||||
args.encryptState.v = defaultEncryptState()
|
||||
}
|
||||
if args.encryptState.v {
|
||||
if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
|
||||
log.SetFlags(0)
|
||||
log.Fatalf("--encrypt-state is not supported on %s", runtime.GOOS)
|
||||
@@ -351,7 +354,7 @@ func statePathOrDefault() string {
|
||||
if path == "" && args.statedir != "" {
|
||||
path = filepath.Join(args.statedir, "tailscaled.state")
|
||||
}
|
||||
if path != "" && !store.HasKnownProviderPrefix(path) && args.encryptState {
|
||||
if path != "" && !store.HasKnownProviderPrefix(path) && args.encryptState.v {
|
||||
path = store.TPMPrefix + path
|
||||
}
|
||||
return path
|
||||
@@ -909,6 +912,6 @@ func defaultEncryptState() bool {
|
||||
// (plan9/FreeBSD/etc).
|
||||
return false
|
||||
}
|
||||
v, _ := policyclient.Get().GetBoolean(pkey.EncryptState, false)
|
||||
v, _ := policyclient.Get().GetBoolean(pkey.EncryptState, feature.TPMAvailable())
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -40,3 +40,15 @@ var HookProxySetSelfProxy Hook[func(...string)]
|
||||
// HookProxySetTransportGetProxyConnectHeader is a hook for feature/useproxy to register
|
||||
// [tshttpproxy.SetTransportGetProxyConnectHeader].
|
||||
var HookProxySetTransportGetProxyConnectHeader Hook[func(*http.Transport)]
|
||||
|
||||
// HookTPMAvailable is a hook that reports whether a TPM device is supported
|
||||
// and available.
|
||||
var HookTPMAvailable Hook[func() bool]
|
||||
|
||||
// TPMAvailable reports whether a TPM device is supported and available.
|
||||
func TPMAvailable() bool {
|
||||
if f, ok := HookTPMAvailable.GetOk(); ok {
|
||||
return f()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ var infoOnce = sync.OnceValue(info)
|
||||
|
||||
func init() {
|
||||
feature.Register("tpm")
|
||||
feature.HookTPMAvailable.Set(tpmSupported)
|
||||
hostinfo.RegisterHostinfoNewHook(func(hi *tailcfg.Hostinfo) {
|
||||
hi.TPM = infoOnce()
|
||||
})
|
||||
@@ -51,6 +52,15 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func tpmSupported() bool {
|
||||
tpm, err := open()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
tpm.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
var verboseTPM = envknob.RegisterBool("TS_DEBUG_TPM")
|
||||
|
||||
func info() *tailcfg.TPMInfo {
|
||||
|
||||
@@ -277,15 +277,6 @@ func TestMigrateStateToTPM(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func tpmSupported() bool {
|
||||
tpm, err := open()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
tpm.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
type mockTPMSealProvider struct {
|
||||
path string
|
||||
data map[ipn.StateKey][]byte
|
||||
|
||||
@@ -7559,11 +7559,7 @@ func (b *LocalBackend) stateEncrypted() opt.Bool {
|
||||
case version.IsMacAppStore():
|
||||
return opt.NewBool(true)
|
||||
case version.IsMacSysExt():
|
||||
// MacSys still stores its state in plaintext on disk in addition to
|
||||
// the Keychain. A future release will clean up the on-disk state
|
||||
// files.
|
||||
// TODO(#15830): always return true here once MacSys is fully migrated.
|
||||
sp, _ := b.polc.GetBoolean(pkey.EncryptState, false)
|
||||
sp, _ := b.polc.GetBoolean(pkey.EncryptState, true)
|
||||
return opt.NewBool(sp)
|
||||
default:
|
||||
// Probably self-compiled tailscaled, we don't use the Keychain
|
||||
|
||||
@@ -136,7 +136,9 @@ const (
|
||||
FlushDNSOnSessionUnlock Key = "FlushDNSOnSessionUnlock"
|
||||
|
||||
// EncryptState is a boolean setting that specifies whether to encrypt the
|
||||
// tailscaled state file with a TPM device.
|
||||
// tailscaled state file.
|
||||
// Windows and Linux use a TPM device, Apple uses the Keychain.
|
||||
// It's a noop on other platforms.
|
||||
EncryptState Key = "EncryptState"
|
||||
|
||||
// PostureChecking indicates if posture checking is enabled and the client shall gather
|
||||
|
||||
Reference in New Issue
Block a user