From 3b3994f0db979674c2850872bf7acfaeafb9b69e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 27 Sep 2021 13:31:40 -0700 Subject: [PATCH] ipn{,/localapi,ipnlocal}: infer cert dir from state file location This fixes "tailscale cert" on Synology where the var directory is typically like /volume2/@appdata/Tailscale, or any other tailscaled user who specifies a non-standard state file location. This is a interim fix on the way to #2932. Fixes #2927 Updates #2932 Signed-off-by: Brad Fitzpatrick --- ipn/ipnlocal/local.go | 21 ++++++++++++++++++--- ipn/localapi/cert.go | 9 ++++----- ipn/store.go | 3 +++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 3e7dd33c3..733dac82a 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1935,14 +1935,29 @@ func normalizeResolver(cfg dnstype.Resolver) dnstype.Resolver { return cfg } -// tailscaleVarRoot returns the root directory of Tailscale's writable +// TailscaleVarRoot returns the root directory of Tailscale's writable // storage area. (e.g. "/var/lib/tailscale") -func tailscaleVarRoot() string { +// +// It returns an empty string if there's no configured or discovered +// location. +func (b *LocalBackend) TailscaleVarRoot() string { switch runtime.GOOS { case "ios", "android": dir, _ := paths.AppSharedDir.Load().(string) return dir } + // Temporary (2021-09-27) transitional fix for #2927 (Synology + // cert dir) on the way towards a more complete fix + // (#2932). It fixes any case where the state file is provided + // to tailscaled explicitly when it's not in the default + // location. + if fs, ok := b.store.(*ipn.FileStore); ok { + if fp := fs.Path(); fp != "" { + if dir := filepath.Dir(fp); strings.EqualFold(filepath.Base(dir), "tailscale") { + return dir + } + } + } stateFile := paths.DefaultTailscaledStateFile() if stateFile == "" { return "" @@ -1954,7 +1969,7 @@ func (b *LocalBackend) fileRootLocked(uid tailcfg.UserID) string { if v := b.directFileRoot; v != "" { return v } - varRoot := tailscaleVarRoot() + varRoot := b.TailscaleVarRoot() if varRoot == "" { b.logf("peerapi disabled; no state directory") return "" diff --git a/ipn/localapi/cert.go b/ipn/localapi/cert.go index b08117562..26fec06df 100644 --- a/ipn/localapi/cert.go +++ b/ipn/localapi/cert.go @@ -36,7 +36,6 @@ import ( "golang.org/x/crypto/acme" "tailscale.com/ipn/ipnstate" - "tailscale.com/paths" "tailscale.com/types/logger" ) @@ -53,11 +52,11 @@ var ( ) func (h *Handler) certDir() (string, error) { - base := paths.DefaultTailscaledStateFile() - if base == "" { - return "", errors.New("no default DefaultTailscaledStateFile") + d := h.b.TailscaleVarRoot() + if d == "" { + return "", errors.New("no TailscaleVarRoot") } - full := filepath.Join(filepath.Dir(base), "certs") + full := filepath.Join(d, "certs") if err := os.MkdirAll(full, 0700); err != nil { return "", err } diff --git a/ipn/store.go b/ipn/store.go index 1bf8194de..b491ca972 100644 --- a/ipn/store.go +++ b/ipn/store.go @@ -166,6 +166,9 @@ type FileStore struct { cache map[StateKey][]byte } +// Path returns the path that NewFileStore was called with. +func (s *FileStore) Path() string { return s.path } + func (s *FileStore) String() string { return fmt.Sprintf("FileStore(%q)", s.path) } // NewFileStore returns a new file store that persists to path.