mirror of
https://github.com/Mic92/sops-nix.git
synced 2026-07-17 06:25:16 +08:00
Fix symlink ownership change on Darwin systems.
This commit is contained in:
parent
c00da36830
commit
b33837ae3c
3 changed files with 37 additions and 61 deletions
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func RuntimeDir() (string, error) {
|
func RuntimeDir() (string, error) {
|
||||||
|
|
@ -23,29 +21,6 @@ func RuntimeDir() (string, error) {
|
||||||
return strings.TrimSuffix(rundir, "/"), nil
|
return strings.TrimSuffix(rundir, "/"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecureSymlinkChown(symlinkToCheck string, expectedTarget string, owner, group int) error {
|
|
||||||
// not sure what O_PATH is needed for anyways
|
|
||||||
fd, err := unix.Open(symlinkToCheck, unix.O_CLOEXEC|unix.O_SYMLINK|unix.O_NOFOLLOW, 0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open %s: %w", symlinkToCheck, err)
|
|
||||||
}
|
|
||||||
defer unix.Close(fd)
|
|
||||||
|
|
||||||
buf := make([]byte, len(expectedTarget)+1) // oversize by one to detect trunc
|
|
||||||
n, err := unix.Readlinkat(fd, "", buf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("couldn't readlinkat %s", symlinkToCheck)
|
|
||||||
}
|
|
||||||
if n > len(expectedTarget) || string(buf[:n]) != expectedTarget {
|
|
||||||
return fmt.Errorf("symlink %s does not point to %s", symlinkToCheck, expectedTarget)
|
|
||||||
}
|
|
||||||
err = unix.Fchownat(fd, "", owner, group, unix.AT_SYMLINK_NOFOLLOW)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot change owner of '%s' to %d/%d: %w", symlinkToCheck, owner, group, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does:
|
// Does:
|
||||||
// mkdir /tmp/mymount
|
// mkdir /tmp/mymount
|
||||||
// NUMSECTORS=128000 # a sector is 512 bytes
|
// NUMSECTORS=128000 # a sector is 512 bytes
|
||||||
|
|
|
||||||
|
|
@ -18,38 +18,6 @@ func RuntimeDir() (string, error) {
|
||||||
return rundir, nil
|
return rundir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecureSymlinkChown(symlinkToCheck, expectedTarget string, owner, group int) error {
|
|
||||||
// fd, err := unix.Open(symlinkToCheck, unix.O_CLOEXEC|unix.O_PATH|unix.O_NOFOLLOW, 0)
|
|
||||||
fd, err := unix.Open(symlinkToCheck, unix.O_CLOEXEC|unix.O_PATH|unix.O_NOFOLLOW, 0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open %s: %w", symlinkToCheck, err)
|
|
||||||
}
|
|
||||||
defer unix.Close(fd)
|
|
||||||
|
|
||||||
buf := make([]byte, len(expectedTarget)+1) // oversize by one to detect trunc
|
|
||||||
n, err := unix.Readlinkat(fd, "", buf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("couldn't readlinkat %s", symlinkToCheck)
|
|
||||||
}
|
|
||||||
if n > len(expectedTarget) || string(buf[:n]) != expectedTarget {
|
|
||||||
return fmt.Errorf("symlink %s does not point to %s", symlinkToCheck, expectedTarget)
|
|
||||||
}
|
|
||||||
stat := unix.Stat_t{}
|
|
||||||
err = unix.Fstat(fd, &stat)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot stat '%s': %w", symlinkToCheck, err)
|
|
||||||
}
|
|
||||||
if stat.Uid == uint32(owner) && stat.Gid == uint32(group) {
|
|
||||||
return nil // already correct
|
|
||||||
}
|
|
||||||
|
|
||||||
err = unix.Fchownat(fd, "", owner, group, unix.AT_EMPTY_PATH)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot change owner of '%s' to %d/%d: %w", symlinkToCheck, owner, group, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MountSecretFs(mountpoint string, keysGID int, useTmpfs bool, userMode bool) error {
|
func MountSecretFs(mountpoint string, keysGID int, useTmpfs bool, userMode bool) error {
|
||||||
if err := os.MkdirAll(mountpoint, 0o751); err != nil {
|
if err := os.MkdirAll(mountpoint, 0o751); err != nil {
|
||||||
return fmt.Errorf("cannot create directory '%s': %w", mountpoint, err)
|
return fmt.Errorf("cannot create directory '%s': %w", mountpoint, err)
|
||||||
|
|
|
||||||
|
|
@ -185,17 +185,50 @@ func linksAreEqual(linkTarget, targetFile string, info os.FileInfo, owner int, g
|
||||||
return linkTarget == targetFile && validUG
|
return linkTarget == targetFile && validUG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SecureSymlinkChown(targetFile string, path string, owner int, group int) error {
|
||||||
|
// Create a temp directory to house the symlink while we change it's
|
||||||
|
// ownership. `os.MkdirTemp` creates a directory with the permissions 0700.
|
||||||
|
// The temp dir is created in the same parent directory of the final
|
||||||
|
// symlink, because the later `os.Rename` operation won't work across disk
|
||||||
|
// devices.
|
||||||
|
dir, err := os.MkdirTemp(filepath.Dir(path), "")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot create temporary symlink directory: %w", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
// Create symlink to `targetFile` in the temp dir, before chowning it.
|
||||||
|
var tmpSymlink = filepath.Join(dir, filepath.Base(path))
|
||||||
|
if err = os.Symlink(targetFile, tmpSymlink); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"cannot create symlink '%s' (pointing to '%s'): %w", path, targetFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Lchown(tmpSymlink, owner, group)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"cannot change owner of symlink '%s' (pointing to '%s') to owner/group: %d/%d: %w",
|
||||||
|
tmpSymlink, targetFile, owner, group, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the chowned symlink to it's final location.
|
||||||
|
err = os.Rename(tmpSymlink, path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot move symlink '%s' to '%s': %w", tmpSymlink, path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func createSymlink(targetFile string, path string, owner int, group int, userMode bool) error {
|
func createSymlink(targetFile string, path string, owner int, group int, userMode bool) error {
|
||||||
for {
|
for {
|
||||||
stat, err := os.Lstat(path)
|
stat, err := os.Lstat(path)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if err = os.Symlink(targetFile, path); err != nil {
|
|
||||||
return fmt.Errorf("cannot create symlink '%s': %w", path, err)
|
|
||||||
}
|
|
||||||
if !userMode {
|
if !userMode {
|
||||||
if err = SecureSymlinkChown(path, targetFile, owner, group); err != nil {
|
if err = SecureSymlinkChown(targetFile, path, owner, group); err != nil {
|
||||||
return fmt.Errorf("cannot chown symlink '%s': %w", path, err)
|
return fmt.Errorf("cannot chown symlink '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
|
} else if err = os.Symlink(targetFile, path); err != nil {
|
||||||
|
return fmt.Errorf("cannot create symlink '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue