mirror of
https://github.com/Mic92/sops-nix.git
synced 2024-12-14 11:57:52 +00:00
fix: create template.path
symlink
This fixes https://github.com/Mic92/sops-nix/issues/653. Note: `main.go` has been slowly accumulating shared logic between vanilla "secrets" and "templates". It feels to me like we could DRY up some of the logic in here by creating some shared "interface" that they both implement. I opted not to try to tackle that here, though.
This commit is contained in:
parent
fe63071416
commit
c9f6b151cc
2 changed files with 44 additions and 22 deletions
|
@ -172,51 +172,51 @@ func readManifest(path string) (*manifest, error) {
|
||||||
return &m, nil
|
return &m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func linksAreEqual(linkTarget, targetFile string, info os.FileInfo, secret *secret) bool {
|
func linksAreEqual(linkTarget, targetFile string, info os.FileInfo, owner int, group int) bool {
|
||||||
validUG := true
|
validUG := true
|
||||||
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||||
validUG = validUG && int(stat.Uid) == secret.owner
|
validUG = validUG && int(stat.Uid) == owner
|
||||||
validUG = validUG && int(stat.Gid) == secret.group
|
validUG = validUG && int(stat.Gid) == group
|
||||||
} else {
|
} else {
|
||||||
panic("Failed to cast fileInfo Sys() to *syscall.Stat_t. This is possibly an unsupported OS.")
|
panic("Failed to cast fileInfo Sys() to *syscall.Stat_t. This is possibly an unsupported OS.")
|
||||||
}
|
}
|
||||||
return linkTarget == targetFile && validUG
|
return linkTarget == targetFile && validUG
|
||||||
}
|
}
|
||||||
|
|
||||||
func symlinkSecret(targetFile string, secret *secret, userMode bool) error {
|
func symlinkSecret(targetFile string, path string, owner int, group int, userMode bool) error {
|
||||||
for {
|
for {
|
||||||
stat, err := os.Lstat(secret.Path)
|
stat, err := os.Lstat(path)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if err = os.Symlink(targetFile, secret.Path); err != nil {
|
if err = os.Symlink(targetFile, path); err != nil {
|
||||||
return fmt.Errorf("cannot create symlink '%s': %w", secret.Path, err)
|
return fmt.Errorf("cannot create symlink '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
if !userMode {
|
if !userMode {
|
||||||
if err = SecureSymlinkChown(secret.Path, targetFile, secret.owner, secret.group); err != nil {
|
if err = SecureSymlinkChown(path, targetFile, owner, group); err != nil {
|
||||||
return fmt.Errorf("cannot chown symlink '%s': %w", secret.Path, err)
|
return fmt.Errorf("cannot chown symlink '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("cannot stat '%s': %w", secret.Path, err)
|
return fmt.Errorf("cannot stat '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
|
if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
linkTarget, err := os.Readlink(secret.Path)
|
linkTarget, err := os.Readlink(path)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("cannot read symlink '%s': %w", secret.Path, err)
|
return fmt.Errorf("cannot read symlink '%s': %w", path, err)
|
||||||
} else if linksAreEqual(linkTarget, targetFile, stat, secret) {
|
} else if linksAreEqual(linkTarget, targetFile, stat, owner, group) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := os.Remove(secret.Path); err != nil {
|
if err := os.Remove(path); err != nil {
|
||||||
return fmt.Errorf("cannot override %s: %w", secret.Path, err)
|
return fmt.Errorf("cannot override %s: %w", path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func symlinkSecrets(targetDir string, secrets []secret, userMode bool) error {
|
func symlinkSecrets(targetDir string, secrets []secret, templates map[string]*template, userMode bool) error {
|
||||||
for i, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
targetFile := filepath.Join(targetDir, secret.Name)
|
targetFile := filepath.Join(targetDir, secret.Name)
|
||||||
if targetFile == secret.Path {
|
if targetFile == secret.Path {
|
||||||
continue
|
continue
|
||||||
|
@ -225,10 +225,25 @@ func symlinkSecrets(targetDir string, secrets []secret, userMode bool) error {
|
||||||
if err := os.MkdirAll(parent, os.ModePerm); err != nil {
|
if err := os.MkdirAll(parent, os.ModePerm); err != nil {
|
||||||
return fmt.Errorf("cannot create parent directory of '%s': %w", secret.Path, err)
|
return fmt.Errorf("cannot create parent directory of '%s': %w", secret.Path, err)
|
||||||
}
|
}
|
||||||
if err := symlinkSecret(targetFile, &secrets[i], userMode); err != nil {
|
if err := symlinkSecret(targetFile, secret.Path, secret.owner, secret.group, userMode); err != nil {
|
||||||
return fmt.Errorf("failed to symlink secret '%s': %w", secret.Path, err)
|
return fmt.Errorf("failed to symlink secret '%s': %w", secret.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, template := range templates {
|
||||||
|
targetFile := filepath.Join(targetDir, RenderedSubdir, template.Name)
|
||||||
|
if targetFile == template.Path {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parent := filepath.Dir(template.Path)
|
||||||
|
if err := os.MkdirAll(parent, os.ModePerm); err != nil {
|
||||||
|
return fmt.Errorf("cannot create parent directory of '%s': %w", template.Path, err)
|
||||||
|
}
|
||||||
|
if err := symlinkSecret(targetFile, template.Path, template.owner, template.group, userMode); err != nil {
|
||||||
|
return fmt.Errorf("failed to symlink template '%s': %w", template.Path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1280,7 +1295,7 @@ func installSecrets(args []string) error {
|
||||||
if isDry {
|
if isDry {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := symlinkSecrets(manifest.SymlinkPath, manifest.Secrets, manifest.UserMode); err != nil {
|
if err := symlinkSecrets(manifest.SymlinkPath, manifest.Secrets, manifest.Templates, manifest.UserMode); err != nil {
|
||||||
return fmt.Errorf("failed to prepare symlinks to secret store: %w", err)
|
return fmt.Errorf("failed to prepare symlinks to secret store: %w", err)
|
||||||
}
|
}
|
||||||
if err := atomicSymlink(*secretDir, manifest.SymlinkPath); err != nil {
|
if err := atomicSymlink(*secretDir, manifest.SymlinkPath); err != nil {
|
||||||
|
|
|
@ -284,9 +284,12 @@ in {
|
||||||
owner = "someuser";
|
owner = "someuser";
|
||||||
group = "somegroup";
|
group = "somegroup";
|
||||||
};
|
};
|
||||||
sops.templates.test_default.content = ''
|
sops.templates.test_default = {
|
||||||
Test value: ${config.sops.placeholder.test_key}
|
content = ''
|
||||||
'';
|
Test value: ${config.sops.placeholder.test_key}
|
||||||
|
'';
|
||||||
|
path = "/etc/externally/linked";
|
||||||
|
};
|
||||||
|
|
||||||
users.groups.somegroup = {};
|
users.groups.somegroup = {};
|
||||||
users.users.someuser = {
|
users.users.someuser = {
|
||||||
|
@ -321,6 +324,10 @@ in {
|
||||||
|
|
||||||
assertEqual(expected, rendered)
|
assertEqual(expected, rendered)
|
||||||
assertEqual(expected_default, rendered_default)
|
assertEqual(expected_default, rendered_default)
|
||||||
|
|
||||||
|
# Confirm that `test_default` was symlinked to the appropriate place.
|
||||||
|
realpath = machine.succeed("realpath /etc/externally/linked").strip()
|
||||||
|
assertEqual(realpath, "/run/secrets.d/1/rendered/test_default")
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue