From 4d5d1b75591fa8b05040610982f7ac1881593849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 17 Nov 2024 19:44:37 +0100 Subject: [PATCH] fix wsl lints --- pkgs/sops-install-secrets/linux.go | 6 ++ pkgs/sops-install-secrets/main.go | 99 +++++++++++++++++++- pkgs/sops-install-secrets/main_test.go | 10 ++ pkgs/sops-install-secrets/sshkeys/convert.go | 1 + 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/pkgs/sops-install-secrets/linux.go b/pkgs/sops-install-secrets/linux.go index 02412d1..9e1e7cf 100644 --- a/pkgs/sops-install-secrets/linux.go +++ b/pkgs/sops-install-secrets/linux.go @@ -27,18 +27,23 @@ func SecureSymlinkChown(symlinkToCheck, expectedTarget string, owner, group int) 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 } @@ -62,6 +67,7 @@ func MountSecretFs(mountpoint string, keysGID int, useTmpfs bool, userMode bool) fstype := "ramfs" fsmagic := RamfsMagic + if useTmpfs { fstype = "tmpfs" fsmagic = TmpfsMagic diff --git a/pkgs/sops-install-secrets/main.go b/pkgs/sops-install-secrets/main.go index 01a7abc..7166006 100644 --- a/pkgs/sops-install-secrets/main.go +++ b/pkgs/sops-install-secrets/main.go @@ -120,7 +120,9 @@ func (f *FormatType) UnmarshalJSON(b []byte) error { if err := json.Unmarshal(b, &s); err != nil { return err } + t := FormatType(s) + switch t { case "": *f = Yaml @@ -165,12 +167,15 @@ func readManifest(path string) (*manifest, error) { if err != nil { return nil, fmt.Errorf("failed to open manifest: %w", err) } + defer file.Close() dec := json.NewDecoder(file) + var m manifest if err := dec.Decode(&m); err != nil { return nil, fmt.Errorf("failed to parse manifest: %w", err) } + return &m, nil } @@ -192,6 +197,7 @@ func createSymlink(targetFile string, path string, owner int, group int, userMod if err = os.Symlink(targetFile, path); err != nil { return fmt.Errorf("cannot create symlink '%s': %w", path, err) } + if !userMode { if err = SecureSymlinkChown(path, targetFile, owner, group); err != nil { return fmt.Errorf("cannot chown symlink '%s': %w", path, err) @@ -201,6 +207,7 @@ func createSymlink(targetFile string, path string, owner int, group int, userMod } else if err != nil { return fmt.Errorf("cannot stat '%s': %w", path, err) } + if stat.Mode()&os.ModeSymlink == os.ModeSymlink { linkTarget, err := os.Readlink(path) if os.IsNotExist(err) { @@ -211,6 +218,7 @@ func createSymlink(targetFile string, path string, owner int, group int, userMod return nil } } + if err := os.Remove(path); err != nil { return fmt.Errorf("cannot override %s: %w", path, err) } @@ -223,10 +231,12 @@ func symlinkSecretsAndTemplates(targetDir string, secrets []secret, templates [] if targetFile == secret.Path { continue } + parent := filepath.Dir(secret.Path) if err := os.MkdirAll(parent, os.ModePerm); err != nil { return fmt.Errorf("cannot create parent directory of '%s': %w", secret.Path, err) } + if err := createSymlink(targetFile, secret.Path, secret.owner, secret.group, userMode); err != nil { return fmt.Errorf("failed to symlink secret '%s': %w", secret.Path, err) } @@ -237,10 +247,12 @@ func symlinkSecretsAndTemplates(targetDir string, secrets []secret, templates [] 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 := createSymlink(targetFile, template.Path, template.owner, template.group, userMode); err != nil { return fmt.Errorf("failed to symlink template '%s': %w", template.Path, err) } @@ -256,7 +268,9 @@ type plainData struct { func recurseSecretKey(keys map[string]interface{}, wantedKey string) (string, error) { var val interface{} + var ok bool + currentKey := wantedKey currentData := keys keyUntilNow := "" @@ -274,23 +288,31 @@ func recurseSecretKey(keys map[string]interface{}, wantedKey string) (string, er } break } + thisKey := currentKey[:slashIndex] + if keyUntilNow == "" { keyUntilNow = thisKey } else { keyUntilNow += "/" + thisKey } + currentKey = currentKey[(slashIndex + 1):] val, ok = currentData[thisKey] + if !ok { return "", fmt.Errorf("the key '%s' cannot be found", keyUntilNow) } + var valWithWrongType map[interface{}]interface{} valWithWrongType, ok = val.(map[interface{}]interface{}) + if !ok { return "", fmt.Errorf("key '%s' does not refer to a dictionary", keyUntilNow) } + currentData = make(map[string]interface{}) + for key, value := range valWithWrongType { currentData[key.(string)] = value } @@ -334,6 +356,7 @@ func decryptSecret(s *secret, sourceFiles map[string]plainData) error { return fmt.Errorf("secret of type %s in %s is not supported", s.Format, s.SopsFile) } } + switch s.Format { case Binary, Dotenv, Ini: s.value = sourceFile.binary @@ -345,9 +368,11 @@ func decryptSecret(s *secret, sourceFiles map[string]plainData) error { if err != nil { return fmt.Errorf("secret %s in %s is not valid: %w", s.Name, s.SopsFile, err) } + s.value = []byte(strVal) } } + sourceFiles[s.SopsFile] = sourceFile return nil } @@ -369,10 +394,12 @@ const ( func prepareSecretsDir(secretMountpoint string, linkName string, keysGID int, userMode bool) (*string, error) { var generation uint64 + linkTarget, err := os.Readlink(linkName) if err == nil { if strings.HasPrefix(linkTarget, secretMountpoint) { targetBasename := filepath.Base(linkTarget) + generation, err = strconv.ParseUint(targetBasename, 10, 64) if err != nil { return nil, fmt.Errorf("cannot parse %s of %s as a number: %w", targetBasename, linkTarget, err) @@ -381,16 +408,20 @@ func prepareSecretsDir(secretMountpoint string, linkName string, keysGID int, us } else if !os.IsNotExist(err) { return nil, fmt.Errorf("cannot access %s: %w", linkName, err) } + generation++ dir := filepath.Join(secretMountpoint, strconv.Itoa(int(generation))) + if _, err := os.Stat(dir); !os.IsNotExist(err) { if err := os.RemoveAll(dir); err != nil { return nil, fmt.Errorf("cannot remove existing %s: %w", dir, err) } } + if err := os.Mkdir(dir, os.FileMode(0o751)); err != nil { return nil, fmt.Errorf("mkdir(): %w", err) } + if !userMode { if err := os.Chown(dir, 0, int(keysGID)); err != nil { return nil, fmt.Errorf("cannot change owner/group of '%s' to 0/%d: %w", dir, keysGID, err) @@ -402,11 +433,13 @@ func prepareSecretsDir(secretMountpoint string, linkName string, keysGID int, us func createParentDirs(parent string, target string, keysGID int, userMode bool) error { dirs := strings.Split(filepath.Dir(target), "/") pathSoFar := parent + for _, dir := range dirs { pathSoFar = filepath.Join(pathSoFar, dir) if err := os.MkdirAll(pathSoFar, 0o751); err != nil { return fmt.Errorf("cannot create directory '%s' for %s: %w", pathSoFar, filepath.Join(parent, target), err) } + if !userMode { if err := os.Chown(pathSoFar, 0, int(keysGID)); err != nil { return fmt.Errorf("cannot own directory '%s' for %s: %w", pathSoFar, filepath.Join(parent, target), err) @@ -423,9 +456,11 @@ func writeSecrets(secretDir string, secrets []secret, keysGID int, userMode bool if err := createParentDirs(secretDir, secret.Name, keysGID, userMode); err != nil { return err } + if err := os.WriteFile(fp, []byte(secret.value), secret.mode); err != nil { return fmt.Errorf("cannot write %s: %w", fp, err) } + if !userMode { if err := os.Chown(fp, secret.owner, secret.group); err != nil { return fmt.Errorf("cannot change owner/group of '%s' to %d/%d: %w", fp, secret.owner, secret.group, err) @@ -440,6 +475,7 @@ func lookupGroup(groupname string) (int, error) { if err != nil { return 0, fmt.Errorf("failed to lookup 'keys' group: %w", err) } + gid, err := strconv.ParseInt(group.Gid, 10, 64) if err != nil { return 0, fmt.Errorf("cannot parse keys gid %s: %w", group.Gid, err) @@ -452,6 +488,7 @@ func lookupKeysGroup() (int, error) { if err1 == nil { return gid, nil } + gid, err2 := lookupGroup("nogroup") if err2 == nil { return gid, nil @@ -486,7 +523,9 @@ func (app *appContext) loadSopsFile(s *secret) (*secretFile, error) { if err != nil { return nil, fmt.Errorf("cannot parse dotenv of '%s': %w", s.SopsFile, err) } + keys = map[string]interface{}{} + for k, v := range env { keys[k] = v } @@ -495,11 +534,11 @@ func (app *appContext) loadSopsFile(s *secret) (*secretFile, error) { return nil, fmt.Errorf("cannot parse json of '%s': %w", s.SopsFile, err) } case Ini: + // TODO: we do not actually check the contents of the ini here... _, err := ini.Load(bytes.NewReader(cipherText)) if err != nil { return nil, fmt.Errorf("cannot parse ini of '%s': %w", s.SopsFile, err) } - // TODO: we do not actually check the contents of the ini here... } return &secretFile{ @@ -515,6 +554,7 @@ func (app *appContext) validateSopsFile(s *secret, file *secretFile) error { s.Name, s.SopsFile, s.Format, file.firstSecret.Format, file.firstSecret.Name) } + if app.checkMode != Manifest && !(s.Format == Binary || s.Format == Dotenv || s.Format == Ini) && s.Key != "" { _, err := recurseSecretKey(file.keys, s.Key) if err != nil { @@ -537,6 +577,7 @@ func validateOwner(owner string) (int, error) { if err != nil { return 0, fmt.Errorf("failed to lookup user '%s': %w", owner, err) } + ownerNr, err := strconv.ParseUint(lookedUp.Uid, 10, 64) if err != nil { return 0, fmt.Errorf("cannot parse uid %s: %w", lookedUp.Uid, err) @@ -549,6 +590,7 @@ func validateGroup(group string) (int, error) { if err != nil { return 0, fmt.Errorf("failed to lookup group '%s': %w", group, err) } + groupNr, err := strconv.ParseUint(lookedUp.Gid, 10, 64) if err != nil { return 0, fmt.Errorf("cannot parse gid %s: %w", lookedUp.Gid, err) @@ -561,6 +603,7 @@ func (app *appContext) validateSecret(secret *secret) error { if err != nil { return err } + secret.mode = mode if app.ignorePasswd || os.Getenv("NIXOS_ACTION") == "dry-activate" { @@ -574,6 +617,7 @@ func (app *appContext) validateSecret(secret *secret) error { if err != nil { return err } + secret.owner = owner } @@ -584,6 +628,7 @@ func (app *appContext) validateSecret(secret *secret) error { if err != nil { return err } + secret.group = group } } @@ -602,6 +647,7 @@ func (app *appContext) validateSecret(secret *secret) error { if err != nil { return err } + app.secretFiles[secret.SopsFile] = *maybeFile file = *maybeFile @@ -631,6 +677,7 @@ func (app *appContext) validateTemplate(template *template) error { if err != nil { return err } + template.mode = mode if app.ignorePasswd || os.Getenv("NIXOS_ACTION") == "dry-activate" { @@ -644,6 +691,7 @@ func (app *appContext) validateTemplate(template *template) error { if err != nil { return err } + template.owner = owner } @@ -654,11 +702,13 @@ func (app *appContext) validateTemplate(template *template) error { if err != nil { return err } + template.group = group } } var templateText string + if template.Content != "" { templateText = template.Content } else if template.File != "" { @@ -666,6 +716,7 @@ func (app *appContext) validateTemplate(template *template) error { if err != nil { return fmt.Errorf("cannot read %s: %w", template.File, err) } + templateText = string(templateBytes) } else { return fmt.Errorf("neither content nor file was specified for template %s", template.Name) @@ -684,6 +735,7 @@ func (app *appContext) validateManifest() error { if len(m.SSHKeyPaths) > 0 { return fmt.Errorf(errorFmt, "sshKeyPaths") } + if m.AgeKeyFile != "" { return fmt.Errorf(errorFmt, "ageKeyFile") } @@ -729,7 +781,9 @@ func atomicSymlink(oldname, newname string) error { if err != nil { return err } + cleanup := true + defer func() { if cleanup { os.RemoveAll(d) @@ -771,6 +825,7 @@ func pruneGenerations(secretsMountPoint, secretsDir string, keepGenerations int) if err != nil { return fmt.Errorf("cannot read %s: %w", secretsMountPoint, err) } + for _, generationName := range generations { generationNum, err := strconv.Atoi(generationName) // Not a number? Not relevant @@ -782,6 +837,7 @@ func pruneGenerations(secretsMountPoint, secretsDir string, keepGenerations int) if generationNum == currentGeneration { continue } + if currentGeneration-keepGenerations >= generationNum { os.RemoveAll(path.Join(secretsMountPoint, generationName)) } @@ -812,6 +868,7 @@ func importSSHKeys(logcfg loggingConfig, keyPaths []string, gpgHome string) erro fmt.Fprintf(os.Stderr, "Cannot read ssh key '%s': %s\n", p, err) continue } + gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) @@ -881,10 +938,12 @@ func symlinkWalk(filename string, linkDirname string, walkFn filepath.WalkFunc) if err != nil { return err } + info, err := os.Lstat(finalPath) if err != nil { return walkFn(path, info, err) } + if info.IsDir() { return symlinkWalk(finalPath, path, walkFn) } @@ -897,6 +956,7 @@ func symlinkWalk(filename string, linkDirname string, walkFn filepath.WalkFunc) func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, secretDir string, secrets []secret, templates []template) error { var restart []string + var reload []string newSecrets := make(map[string]bool) @@ -989,7 +1049,9 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s if err != nil { return err } + defer f.Close() + for _, unit := range list { if _, err = f.WriteString(unit + "\n"); err != nil { return err @@ -998,15 +1060,18 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s } return nil } + var dryPrefix string if isDry { dryPrefix = "/run/nixos/dry-activation" } else { dryPrefix = "/run/nixos/activation" } + if err := writeLines(restart, dryPrefix+"-restart-list"); err != nil { return err } + if err := writeLines(reload, dryPrefix+"-reload-list"); err != nil { return err } @@ -1018,10 +1083,12 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s // Find removed secrets/templates. symlinkRenderedPath := filepath.Join(symlinkPath, RenderedSubdir) + err := symlinkWalk(symlinkPath, symlinkPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } + if info.IsDir() { return nil } @@ -1032,6 +1099,7 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s if err != nil { return err } + isSecret := strings.HasPrefix(rel, "..") if isSecret { @@ -1041,6 +1109,7 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s return nil } } + removedSecrets[path] = true } else { path = strings.TrimPrefix(path, symlinkRenderedPath+string(os.PathSeparator)) @@ -1049,6 +1118,7 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s return nil } } + removedTemplates[path] = true } return nil @@ -1064,6 +1134,7 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s if len(changed) != 1 { s = "s" } + if isDry { fmt.Printf("%s %s%s: ", dryPrefix, noun, s) } else { @@ -1075,6 +1146,7 @@ func handleModifications(isDry bool, logcfg loggingConfig, symlinkPath string, s for key := range changed { keys = append(keys, key) } + sort.Strings(keys) fmt.Println(strings.Join(keys, ", ")) @@ -1104,12 +1176,14 @@ func setupGPGKeyring(logcfg loggingConfig, sshKeys []string, parentDir string) ( if err != nil { return nil, fmt.Errorf("cannot create gpg home in '%s': %w", parentDir, err) } + k := keyring{dir} if err := importSSHKeys(logcfg, sshKeys, dir); err != nil { os.RemoveAll(dir) return nil, err } + os.Setenv("GNUPGHOME", dir) return &k, nil @@ -1117,14 +1191,17 @@ func setupGPGKeyring(logcfg loggingConfig, sshKeys []string, parentDir string) ( func parseFlags(args []string) (*options, error) { var opts options + fs := flag.NewFlagSet(args[0], flag.ContinueOnError) fs.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTION] manifest.json\n", args[0]) fs.PrintDefaults() } + var checkMode string fs.StringVar(&checkMode, "check-mode", "off", `Validate configuration without installing it (possible values: "manifest","sopsfile","off")`) fs.BoolVar(&opts.ignorePasswd, "ignore-passwd", false, `Don't look up anything in /etc/passwd. Causes everything to be owned by root:root or the user executing the tool in user mode`) + if err := fs.Parse(args[1:]); err != nil { return nil, err } @@ -1140,6 +1217,7 @@ func parseFlags(args []string) (*options, error) { flag.Usage() return nil, flag.ErrHelp } + opts.manifest = fs.Arg(0) return &opts, nil } @@ -1147,10 +1225,12 @@ func parseFlags(args []string) (*options, error) { func replaceRuntimeDir(path, rundir string) (ret string) { parts := strings.Split(path, "%%") first := true + for _, part := range parts { if !first { ret += "%" } + first = false ret += strings.ReplaceAll(part, "%r", rundir) } @@ -1168,6 +1248,7 @@ func writeTemplates(targetDir string, templates []template, keysGID int, userMod if err := os.WriteFile(fp, []byte(template.value), template.mode); err != nil { return fmt.Errorf("cannot write %s: %w", fp, err) } + if !userMode { if err := os.Chown(fp, template.owner, template.group); err != nil { return fmt.Errorf("cannot change owner/group of '%s' to %d/%d: %w", fp, template.owner, template.group, err) @@ -1191,16 +1272,21 @@ func installSecrets(args []string) error { if manifest.UserMode { var rundir string rundir, err = RuntimeDir() + if opts.checkMode == Off && err != nil { return fmt.Errorf("cannot figure out runtime directory: %w", err) } + manifest.SecretsMountPoint = replaceRuntimeDir(manifest.SecretsMountPoint, rundir) manifest.SymlinkPath = replaceRuntimeDir(manifest.SymlinkPath, rundir) + var newSecrets []secret + for _, secret := range manifest.Secrets { secret.Path = replaceRuntimeDir(secret.Path, rundir) newSecrets = append(newSecrets, secret) } + manifest.Secrets = newSecrets } @@ -1238,10 +1324,12 @@ func installSecrets(args []string) error { if len(manifest.SSHKeyPaths) != 0 { var keyring *keyring + keyring, err = setupGPGKeyring(manifest.Logging, manifest.SSHKeyPaths, manifest.SecretsMountPoint) if err != nil { return fmt.Errorf("error setting up gpg keyring: %w", err) } + defer keyring.Remove() } else if manifest.GnupgHome != "" { os.Setenv("GNUPGHOME", manifest.GnupgHome) @@ -1253,11 +1341,14 @@ func installSecrets(args []string) error { os.Setenv("SOPS_AGE_KEY_FILE", keyfile) // Create the keyfile var ageFile *os.File + ageFile, err = os.OpenFile(keyfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { return fmt.Errorf("cannot create '%s': %w", keyfile, err) } + defer ageFile.Close() + fmt.Fprintf(ageFile, "# generated by sops-nix at %s\n", time.Now().Format(time.RFC3339)) // Import SSH keys @@ -1271,10 +1362,12 @@ func installSecrets(args []string) error { if manifest.AgeKeyFile != "" { // Read the keyfile var contents []byte + contents, err = os.ReadFile(manifest.AgeKeyFile) if err != nil { return fmt.Errorf("cannot read keyfile '%s': %w", manifest.AgeKeyFile, err) } + // Append it to the file _, err = ageFile.WriteString(string(contents) + "\n") if err != nil { @@ -1294,6 +1387,7 @@ func installSecrets(args []string) error { if err != nil { return fmt.Errorf("failed to prepare new secrets directory: %w", err) } + if err := writeSecrets(*secretDir, manifest.Secrets, keysGID, manifest.UserMode); err != nil { return fmt.Errorf("cannot write secrets: %w", err) } @@ -1314,9 +1408,11 @@ func installSecrets(args []string) error { if err := symlinkSecretsAndTemplates(manifest.SymlinkPath, manifest.Secrets, manifest.Templates, manifest.UserMode); err != nil { return fmt.Errorf("failed to prepare symlinks to secret store: %w", err) } + if err := atomicSymlink(*secretDir, manifest.SymlinkPath); err != nil { return fmt.Errorf("cannot update secrets symlink: %w", err) } + if err := pruneGenerations(manifest.SecretsMountPoint, *secretDir, manifest.KeepGenerations); err != nil { return fmt.Errorf("cannot prune old secrets generations: %w", err) } @@ -1329,6 +1425,7 @@ func main() { if errors.Is(err, flag.ErrHelp) { return } + fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err) os.Exit(1) } diff --git a/pkgs/sops-install-secrets/main_test.go b/pkgs/sops-install-secrets/main_test.go index 99a7aba..cece77d 100644 --- a/pkgs/sops-install-secrets/main_test.go +++ b/pkgs/sops-install-secrets/main_test.go @@ -21,6 +21,7 @@ import ( // ok fails the test if an err is not nil. func ok(tb testing.TB, err error) { tb.Helper() + if err != nil { fmt.Printf("\033[31munexpected error: %s\033[39m\n\n", err.Error()) tb.FailNow() @@ -29,6 +30,7 @@ func ok(tb testing.TB, err error) { func equals(tb testing.TB, exp, act interface{}) { tb.Helper() + if !reflect.DeepEqual(exp, act) { fmt.Printf("\033[31m\texp: %#v\n\n\tgot: %#v\033[39m\n\n", exp, act) tb.FailNow() @@ -41,6 +43,7 @@ func writeManifest(t *testing.T, dir string, m *manifest) string { filename := path.Join(dir, "manifest.json") f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0o755) ok(t, err) + encoder := json.NewEncoder(f) ok(t, encoder.Encode(m)) f.Close() @@ -66,6 +69,7 @@ func (dir testDir) Remove() { func newTestDir(t *testing.T) testDir { t.Helper() + tempdir, err := os.MkdirTemp("", "symlinkDir") ok(t, err) return testDir{tempdir, path.Join(tempdir, "secrets.d"), path.Join(tempdir, "secrets")} @@ -88,15 +92,18 @@ func TestGPG(t *testing.T) { //nolint:paralleltest gpgEnv := append(os.Environ(), fmt.Sprintf("GNUPGHOME=%s", gpgHome)) ok(t, os.Mkdir(gpgHome, os.FileMode(0o700))) + cmd := exec.Command("gpg", "--import", path.Join(assets, "key.asc")) // nolint:gosec cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = gpgEnv ok(t, cmd.Run()) + stopGpgCmd := exec.Command("gpgconf", "--kill", "gpg-agent") stopGpgCmd.Stdout = os.Stdout stopGpgCmd.Stderr = os.Stderr stopGpgCmd.Env = gpgEnv + defer func() { if err := stopGpgCmd.Run(); err != nil { fmt.Printf("failed to stop gpg-agent: %s\n", err) @@ -119,6 +126,7 @@ func TestGPG(t *testing.T) { //nolint:paralleltest } var jsonSecret, binarySecret, dotenvSecret, iniSecret secret + root := "root" // should not create a symlink jsonSecret = yamlSecret @@ -179,6 +187,7 @@ func TestGPG(t *testing.T) { //nolint:paralleltest equals(t, 0o400, int(yamlStat.Mode().Perm())) stat, success := yamlStat.Sys().(*syscall.Stat_t) equals(t, true, success) + content, err := os.ReadFile(yamlSecret.Path) ok(t, err) equals(t, "test_value", string(content)) @@ -195,6 +204,7 @@ func TestGPG(t *testing.T) { //nolint:paralleltest ok(t, err) equals(t, true, jsonStat.Mode().IsRegular()) equals(t, 0o700, int(jsonStat.Mode().Perm())) + if stat, ok := jsonStat.Sys().(*syscall.Stat_t); ok { equals(t, 0, int(stat.Uid)) equals(t, 0, int(stat.Gid)) diff --git a/pkgs/sops-install-secrets/sshkeys/convert.go b/pkgs/sops-install-secrets/sshkeys/convert.go index 070cf1e..495124a 100644 --- a/pkgs/sops-install-secrets/sshkeys/convert.go +++ b/pkgs/sops-install-secrets/sshkeys/convert.go @@ -60,6 +60,7 @@ func SSHPrivateKeyToPGP(sshPrivateKey []byte) (*openpgp.Entity, error) { IssuerKeyId: &gpgKey.PrimaryKey.KeyId, }, } + err = gpgKey.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, gpgKey.PrimaryKey, gpgKey.PrivateKey, nil) if err != nil { return nil, err