ipn/store: automatically migrate between plaintext and encrypted state (#16318)

Add a new `--encrypt-state` flag to `cmd/tailscaled`. Based on that
flag, migrate the existing state file to/from encrypted format if
needed.

Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
Andrew Lytvynov
2025-06-26 17:09:13 -07:00
committed by GitHub
parent d2c1ed22c3
commit 6feb3c35cb
24 changed files with 546 additions and 26 deletions

View File

@@ -10,6 +10,7 @@ import (
"encoding/json"
"errors"
"fmt"
"iter"
"log"
"os"
"path/filepath"
@@ -37,7 +38,7 @@ func init() {
hostinfo.RegisterHostinfoNewHook(func(hi *tailcfg.Hostinfo) {
hi.TPM = infoOnce()
})
store.Register(storePrefix, newStore)
store.Register(store.TPMPrefix, newStore)
}
func info() *tailcfg.TPMInfo {
@@ -103,10 +104,8 @@ func propToString(v uint32) string {
return string(slices.DeleteFunc(chars, func(b byte) bool { return b < ' ' || b > '~' }))
}
const storePrefix = "tpmseal:"
func newStore(logf logger.Logf, path string) (ipn.StateStore, error) {
path = strings.TrimPrefix(path, storePrefix)
path = strings.TrimPrefix(path, store.TPMPrefix)
if err := paths.MkStateDir(filepath.Dir(path)); err != nil {
return nil, fmt.Errorf("creating state directory: %w", err)
}
@@ -205,6 +204,19 @@ func (s *tpmStore) writeSealed() error {
return atomicfile.WriteFile(s.path, buf, 0600)
}
func (s *tpmStore) All() iter.Seq2[ipn.StateKey, []byte] {
return func(yield func(ipn.StateKey, []byte) bool) {
s.mu.Lock()
defer s.mu.Unlock()
for k, v := range s.cache {
if !yield(k, v) {
break
}
}
}
}
// The nested levels of encoding and encryption are confusing, so here's what's
// going on in plain English.
//