tailscale/paths/migrate.go
Aaron Klotz 9ebb5d4205 ipn, paths: ensure that the state directory for Windows has the correct perms
ProgramData has a permissive ACL. For us to safely store machine-wide
state information, we must set a more restrictive ACL on our state directory.
We set the ACL so that only talescaled's user (ie, LocalSystem) and the
Administrators group may access our directory.

We must include Administrators to ensure that logs continue to be easily
accessible; omitting that group would force users to use special tools to
log in interactively as LocalSystem, which is not ideal.

(Note that the ACL we apply matches the ACL that was used for LocalSystem's
AppData\Local).

There are two cases where we need to reset perms: One is during migration
from the old location to the new. The second case is for clean installations
where we are creating the file store for the first time.

Updates #2856

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2021-09-22 14:50:00 -06:00

60 lines
1.7 KiB
Go

// Copyright (c) 2021 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 paths
import (
"os"
"path/filepath"
"tailscale.com/types/logger"
)
// TryConfigFileMigration carefully copies the contents of oldFile to
// newFile, returning the path which should be used to read the config.
// - if newFile already exists, don't modify it just return its path
// - if neither oldFile nor newFile exist, return newFile for a fresh
// default config to be written to.
// - if oldFile exists but copying to newFile fails, return oldFile so
// there will at least be some config to work with.
func TryConfigFileMigration(logf logger.Logf, oldFile, newFile string) string {
_, err := os.Stat(newFile)
if err == nil {
// Common case for a system which has already been migrated.
return newFile
}
if !os.IsNotExist(err) {
logf("TryConfigFileMigration failed; new file: %v", err)
return newFile
}
contents, err := os.ReadFile(oldFile)
if err != nil {
// Common case for a new user.
return newFile
}
if err = MkStateDir(filepath.Dir(newFile)); err != nil {
logf("TryConfigFileMigration failed; MkStateDir: %v", err)
return oldFile
}
err = os.WriteFile(newFile, contents, 0600)
if err != nil {
removeErr := os.Remove(newFile)
if removeErr != nil {
logf("TryConfigFileMigration failed; write newFile no cleanup: %v, remove err: %v",
err, removeErr)
return oldFile
}
logf("TryConfigFileMigration failed; write newFile: %v", err)
return oldFile
}
logf("TryConfigFileMigration: successfully migrated: from %v to %v",
oldFile, newFile)
return newFile
}