diff --git a/internal/cli/init.go b/internal/cli/init.go index a84fb6d..f2bf0be 100644 --- a/internal/cli/init.go +++ b/internal/cli/init.go @@ -36,7 +36,7 @@ func (cli *CLIInstance) Init(cmd *cobra.Command) error { stateDir := cli.GetStateDir() secret.DebugWith("Creating state directory", slog.String("path", stateDir)) - if err := cli.fs.MkdirAll(stateDir, 0700); err != nil { + if err := cli.fs.MkdirAll(stateDir, secret.DirPerms); err != nil { secret.Debug("Failed to create state directory", "error", err) return fmt.Errorf("failed to create state directory: %w", err) } @@ -101,7 +101,7 @@ func (cli *CLIInstance) Init(cmd *cobra.Command) error { vaultDir := filepath.Join(stateDir, "vaults.d", "default") ltPubKey := ltIdentity.Recipient().String() secret.DebugWith("Storing long-term public key", slog.String("pubkey", ltPubKey), slog.String("vault_dir", vaultDir)) - if err := afero.WriteFile(cli.fs, filepath.Join(vaultDir, "pub.age"), []byte(ltPubKey), 0600); err != nil { + if err := afero.WriteFile(cli.fs, filepath.Join(vaultDir, "pub.age"), []byte(ltPubKey), secret.FilePerms); err != nil { secret.Debug("Failed to write long-term public key", "error", err) return fmt.Errorf("failed to write long-term public key: %w", err) } @@ -154,7 +154,7 @@ func (cli *CLIInstance) Init(cmd *cobra.Command) error { } // Write encrypted long-term private key - if err := afero.WriteFile(cli.fs, filepath.Join(unlockKeyDir, "longterm.age"), encryptedLtPrivKey, 0600); err != nil { + if err := afero.WriteFile(cli.fs, filepath.Join(unlockKeyDir, "longterm.age"), encryptedLtPrivKey, secret.FilePerms); err != nil { return fmt.Errorf("failed to write encrypted long-term private key: %w", err) } diff --git a/internal/secret/constants.go b/internal/secret/constants.go index adb22d3..84cbc31 100644 --- a/internal/secret/constants.go +++ b/internal/secret/constants.go @@ -1,5 +1,7 @@ package secret +import "os" + const ( // AppID is the unique identifier for this application AppID = "berlin.sneak.pkg.secret" @@ -10,3 +12,12 @@ const ( EnvUnlockPassphrase = "SB_UNLOCK_PASSPHRASE" EnvGPGKeyID = "SB_GPG_KEY_ID" ) + +// File system permission constants +const ( + // DirPerms is the permission used for directories (read-write-execute for owner only) + DirPerms os.FileMode = 0700 + + // FilePerms is the permission used for sensitive files (read-write for owner only) + FilePerms os.FileMode = 0600 +) diff --git a/internal/secret/keychainunlock.go b/internal/secret/keychainunlock.go index c607160..5065fce 100644 --- a/internal/secret/keychainunlock.go +++ b/internal/secret/keychainunlock.go @@ -240,7 +240,7 @@ func CreateKeychainUnlockKey(fs afero.Fs, stateDir string) (*KeychainUnlockKey, } unlockKeyDir := filepath.Join(vaultDir, "unlock.d", keychainItemName) - if err := fs.MkdirAll(unlockKeyDir, 0700); err != nil { + if err := fs.MkdirAll(unlockKeyDir, DirPerms); err != nil { return nil, fmt.Errorf("failed to create unlock key directory: %w", err) } @@ -259,7 +259,7 @@ func CreateKeychainUnlockKey(fs afero.Fs, stateDir string) (*KeychainUnlockKey, // Step 3: Store age public key as plaintext agePublicKeyString := ageIdentity.Recipient().String() agePubKeyPath := filepath.Join(unlockKeyDir, "pub.age") - if err := afero.WriteFile(fs, agePubKeyPath, []byte(agePublicKeyString), 0600); err != nil { + if err := afero.WriteFile(fs, agePubKeyPath, []byte(agePublicKeyString), FilePerms); err != nil { return nil, fmt.Errorf("failed to write age public key: %w", err) } @@ -271,7 +271,7 @@ func CreateKeychainUnlockKey(fs afero.Fs, stateDir string) (*KeychainUnlockKey, } agePrivKeyPath := filepath.Join(unlockKeyDir, "priv.age") - if err := afero.WriteFile(fs, agePrivKeyPath, encryptedAgePrivKey, 0600); err != nil { + if err := afero.WriteFile(fs, agePrivKeyPath, encryptedAgePrivKey, FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted age private key: %w", err) } @@ -342,7 +342,7 @@ func CreateKeychainUnlockKey(fs afero.Fs, stateDir string) (*KeychainUnlockKey, // Write encrypted long-term private key ltPrivKeyPath := filepath.Join(unlockKeyDir, "longterm.age") - if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKeyToAge, 0600); err != nil { + if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKeyToAge, FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted long-term private key: %w", err) } @@ -383,7 +383,7 @@ func CreateKeychainUnlockKey(fs afero.Fs, stateDir string) (*KeychainUnlockKey, return nil, fmt.Errorf("failed to marshal unlock key metadata: %w", err) } - if err := afero.WriteFile(fs, filepath.Join(unlockKeyDir, "unlock-metadata.json"), metadataBytes, 0600); err != nil { + if err := afero.WriteFile(fs, filepath.Join(unlockKeyDir, "unlock-metadata.json"), metadataBytes, FilePerms); err != nil { return nil, fmt.Errorf("failed to write unlock key metadata: %w", err) } diff --git a/internal/secret/pgpunlock.go b/internal/secret/pgpunlock.go index 9856e84..0a53852 100644 --- a/internal/secret/pgpunlock.go +++ b/internal/secret/pgpunlock.go @@ -188,7 +188,7 @@ func CreatePGPUnlockKey(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnlo } unlockKeyDir := filepath.Join(vaultDir, "unlock.d", unlockKeyName) - if err := fs.MkdirAll(unlockKeyDir, 0700); err != nil { + if err := fs.MkdirAll(unlockKeyDir, DirPerms); err != nil { return nil, fmt.Errorf("failed to create unlock key directory: %w", err) } @@ -201,7 +201,7 @@ func CreatePGPUnlockKey(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnlo // Step 2: Store age public key as plaintext agePublicKeyString := ageIdentity.Recipient().String() agePubKeyPath := filepath.Join(unlockKeyDir, "pub.age") - if err := afero.WriteFile(fs, agePubKeyPath, []byte(agePublicKeyString), 0600); err != nil { + if err := afero.WriteFile(fs, agePubKeyPath, []byte(agePublicKeyString), FilePerms); err != nil { return nil, fmt.Errorf("failed to write age public key: %w", err) } @@ -265,7 +265,7 @@ func CreatePGPUnlockKey(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnlo // Write encrypted long-term private key ltPrivKeyPath := filepath.Join(unlockKeyDir, "longterm.age") - if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKeyToAge, 0600); err != nil { + if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKeyToAge, FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted long-term private key: %w", err) } @@ -277,7 +277,7 @@ func CreatePGPUnlockKey(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnlo } agePrivKeyPath := filepath.Join(unlockKeyDir, "priv.age.gpg") - if err := afero.WriteFile(fs, agePrivKeyPath, encryptedAgePrivKey, 0600); err != nil { + if err := afero.WriteFile(fs, agePrivKeyPath, encryptedAgePrivKey, FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted age private key: %w", err) } @@ -302,7 +302,7 @@ func CreatePGPUnlockKey(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnlo return nil, fmt.Errorf("failed to marshal unlock key metadata: %w", err) } - if err := afero.WriteFile(fs, filepath.Join(unlockKeyDir, "unlock-metadata.json"), metadataBytes, 0600); err != nil { + if err := afero.WriteFile(fs, filepath.Join(unlockKeyDir, "unlock-metadata.json"), metadataBytes, FilePerms); err != nil { return nil, fmt.Errorf("failed to write unlock key metadata: %w", err) } diff --git a/internal/secret/secret_test.go b/internal/secret/secret_test.go index d85f7a3..ac9f29a 100644 --- a/internal/secret/secret_test.go +++ b/internal/secret/secret_test.go @@ -25,10 +25,10 @@ func (m *MockVault) GetDirectory() (string, error) { func (m *MockVault) AddSecret(name string, value []byte, force bool) error { // Simplified implementation for testing secretDir := filepath.Join(m.directory, "secrets.d", name) - if err := m.fs.MkdirAll(secretDir, 0700); err != nil { + if err := m.fs.MkdirAll(secretDir, DirPerms); err != nil { return err } - return afero.WriteFile(m.fs, filepath.Join(secretDir, "value.age"), value, 0600) + return afero.WriteFile(m.fs, filepath.Join(secretDir, "value.age"), value, FilePerms) } func (m *MockVault) GetName() string { @@ -70,7 +70,7 @@ func TestPerSecretKeyFunctionality(t *testing.T) { vaultDir := filepath.Join(baseDir, "vaults.d", "test-vault") // Create vault directory structure - err := fs.MkdirAll(filepath.Join(vaultDir, "secrets.d"), 0700) + err := fs.MkdirAll(filepath.Join(vaultDir, "secrets.d"), DirPerms) if err != nil { t.Fatalf("Failed to create vault directory: %v", err) } @@ -95,7 +95,7 @@ func TestPerSecretKeyFunctionality(t *testing.T) { // Set current vault currentVaultPath := filepath.Join(baseDir, "currentvault") - err = afero.WriteFile(fs, currentVaultPath, []byte(vaultDir), 0600) + err = afero.WriteFile(fs, currentVaultPath, []byte(vaultDir), FilePerms) if err != nil { t.Fatalf("Failed to set current vault: %v", err) } diff --git a/internal/vault/management.go b/internal/vault/management.go index f88b9b1..8b9efb6 100644 --- a/internal/vault/management.go +++ b/internal/vault/management.go @@ -34,108 +34,142 @@ func resolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) { // First try to handle the path as a real symlink (works on Unix systems) if _, ok := fs.(*afero.OsFs); ok { - secret.Debug("Trying real filesystem symlink resolution") + secret.Debug("Using real filesystem symlink resolution") - // Check if it's a real symlink first (will work on Unix) - linkTarget, err := os.Readlink(symlinkPath) + // Check if the symlink exists + secret.Debug("Checking symlink target", "symlink_path", symlinkPath) + target, err := os.Readlink(symlinkPath) if err == nil { - // Successfully read as symlink (Unix path) - secret.Debug("Successfully read as real symlink", "target", linkTarget) + secret.Debug("Symlink points to", "target", target) - // Convert relative paths to absolute if needed - if !filepath.IsAbs(linkTarget) { - linkTarget = filepath.Join(filepath.Dir(symlinkPath), linkTarget) + // On real filesystem, we need to handle relative symlinks + // by resolving them relative to the symlink's directory + if !filepath.IsAbs(target) { + // Get the current directory before changing + originalDir, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("failed to get current directory: %w", err) + } + secret.Debug("Got current directory", "original_dir", originalDir) + + // Change to the symlink's directory + symlinkDir := filepath.Dir(symlinkPath) + secret.Debug("Changing to symlink directory", "symlink_path", symlinkDir) + secret.Debug("About to call os.Chdir - this might hang if symlink is broken") + if err := os.Chdir(symlinkDir); err != nil { + return "", fmt.Errorf("failed to change to symlink directory: %w", err) + } + secret.Debug("Changed to symlink directory successfully - os.Chdir completed") + + // Get the absolute path of the target + secret.Debug("Getting absolute path of current directory") + absolutePath, err := os.Getwd() + if err != nil { + // Try to restore original directory before returning error + _ = os.Chdir(originalDir) + return "", fmt.Errorf("failed to get absolute path: %w", err) + } + secret.Debug("Got absolute path", "absolute_path", absolutePath) + + // Restore the original directory + secret.Debug("Restoring original directory", "original_dir", originalDir) + if err := os.Chdir(originalDir); err != nil { + return "", fmt.Errorf("failed to restore original directory: %w", err) + } + secret.Debug("Restored original directory successfully") + + // Use the absolute path of the target + target = absolutePath } - secret.Debug("Resolved symlink path", "path", linkTarget) - return linkTarget, nil + secret.Debug("resolveVaultSymlink completed successfully", "result", target) + return target, nil } - - secret.Debug("Not a real symlink or on Windows, trying as regular file", "error", err) } - // For Windows or in-memory filesystems, or when symlink reading fails: - // Read the path from a regular file (our fallback approach) - secret.Debug("Reading symlink target from file") - content, err := afero.ReadFile(fs, symlinkPath) + // Fallback: treat it as a regular file containing the target path + secret.Debug("Fallback: trying to read regular file with target path") + + fileData, err := afero.ReadFile(fs, symlinkPath) if err != nil { - secret.Debug("Failed to read from file", "error", err) - return "", fmt.Errorf("failed to read vault path: %w", err) + secret.Debug("Failed to read target path file", "error", err) + return "", fmt.Errorf("failed to read vault symlink: %w", err) } - targetPath := string(content) - secret.Debug("Read target path from file", "target_path", targetPath) - return targetPath, nil + target := string(fileData) + secret.Debug("Read target path from file", "target", target) + + secret.Debug("resolveVaultSymlink completed via fallback", "result", target) + return target, nil } -// GetCurrentVault gets the currently selected vault +// GetCurrentVault gets the current vault from the file system func GetCurrentVault(fs afero.Fs, stateDir string) (*Vault, error) { secret.Debug("Getting current vault", "state_dir", stateDir) - // Check if current vault symlink exists + // Check if the current vault symlink exists currentVaultPath := filepath.Join(stateDir, "currentvault") - secret.Debug("Checking current vault symlink", "path", currentVaultPath) + secret.Debug("Checking current vault symlink", "path", currentVaultPath) _, err := fs.Stat(currentVaultPath) if err != nil { secret.Debug("Failed to stat current vault symlink", "error", err, "path", currentVaultPath) return nil, fmt.Errorf("failed to read current vault symlink: %w", err) } + secret.Debug("Current vault symlink exists") - // Resolve symlink to get target path + // Resolve the symlink to get the actual vault directory secret.Debug("Resolving vault symlink") targetPath, err := resolveVaultSymlink(fs, currentVaultPath) if err != nil { - secret.Debug("Failed to resolve vault symlink", "error", err) - return nil, fmt.Errorf("failed to resolve vault symlink: %w", err) + return nil, err } + secret.Debug("Resolved vault symlink", "target_path", targetPath) - // Extract vault name from target path + // Extract the vault name from the path + // The path will be something like "/path/to/vaults.d/default" vaultName := filepath.Base(targetPath) secret.Debug("Extracted vault name", "vault_name", vaultName) secret.Debug("Current vault resolved", "vault_name", vaultName, "target_path", targetPath) - // Create and return Vault instance - secret.Debug("Creating NewVault instance") - vault := NewVault(fs, vaultName, stateDir) - secret.Debug("Created NewVault instance successfully") - - return vault, nil + // Create and return the vault + return NewVault(fs, stateDir, vaultName), nil } -// ListVaults returns a list of available vault names +// ListVaults lists all vaults in the state directory func ListVaults(fs afero.Fs, stateDir string) ([]string, error) { vaultsDir := filepath.Join(stateDir, "vaults.d") // Check if vaults directory exists exists, err := afero.DirExists(fs, vaultsDir) if err != nil { - return nil, fmt.Errorf("failed to check vaults directory: %w", err) + return nil, fmt.Errorf("failed to check if vaults directory exists: %w", err) } if !exists { return []string{}, nil } - // Read directory contents - files, err := afero.ReadDir(fs, vaultsDir) + // Read the vaults directory + entries, err := afero.ReadDir(fs, vaultsDir) if err != nil { return nil, fmt.Errorf("failed to read vaults directory: %w", err) } + // Extract vault names var vaults []string - for _, file := range files { - if file.IsDir() { - vaults = append(vaults, file.Name()) + for _, entry := range entries { + if entry.IsDir() { + vaults = append(vaults, entry.Name()) } } return vaults, nil } -// CreateVault creates a new vault with the given name +// CreateVault creates a new vault func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) { secret.Debug("Creating new vault", "name", name, "state_dir", stateDir) @@ -150,39 +184,32 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) { vaultDir := filepath.Join(stateDir, "vaults.d", name) secret.Debug("Creating vault directory structure", "vault_dir", vaultDir) - // Check if vault already exists - exists, err := afero.DirExists(fs, vaultDir) - if err != nil { - return nil, fmt.Errorf("failed to check if vault exists: %w", err) - } - if exists { - return nil, fmt.Errorf("vault %s already exists", name) - } - - // Create vault directory - if err := fs.MkdirAll(vaultDir, 0700); err != nil { + // Create main vault directory + if err := fs.MkdirAll(vaultDir, secret.DirPerms); err != nil { return nil, fmt.Errorf("failed to create vault directory: %w", err) } - // Create subdirectories + // Create secrets directory secretsDir := filepath.Join(vaultDir, "secrets.d") - if err := fs.MkdirAll(secretsDir, 0700); err != nil { + if err := fs.MkdirAll(secretsDir, secret.DirPerms); err != nil { return nil, fmt.Errorf("failed to create secrets directory: %w", err) } + // Create unlock keys directory unlockKeysDir := filepath.Join(vaultDir, "unlock.d") - if err := fs.MkdirAll(unlockKeysDir, 0700); err != nil { + if err := fs.MkdirAll(unlockKeysDir, secret.DirPerms); err != nil { return nil, fmt.Errorf("failed to create unlock keys directory: %w", err) } - // Select the new vault as current + // Select the newly created vault as current secret.Debug("Selecting newly created vault as current", "name", name) if err := SelectVault(fs, stateDir, name); err != nil { - return nil, fmt.Errorf("failed to select new vault: %w", err) + return nil, fmt.Errorf("failed to select vault: %w", err) } + // Create and return the vault secret.Debug("Successfully created vault", "name", name) - return NewVault(fs, name, stateDir), nil + return NewVault(fs, stateDir, name), nil } // SelectVault selects the given vault as the current vault @@ -206,35 +233,33 @@ func SelectVault(fs afero.Fs, stateDir string, name string) error { return fmt.Errorf("vault %s does not exist", name) } - // Create/update current vault symlink + // Create or update the current vault symlink/file currentVaultPath := filepath.Join(stateDir, "currentvault") + targetPath := filepath.Join(stateDir, "vaults.d", name) - // Remove existing symlink if it exists - if exists, _ := afero.Exists(fs, currentVaultPath); exists { + // First try to remove existing symlink if it exists + if _, err := fs.Stat(currentVaultPath); err == nil { secret.Debug("Removing existing current vault symlink", "path", currentVaultPath) if err := fs.Remove(currentVaultPath); err != nil { - secret.Debug("Failed to remove existing symlink", "error", err, "path", currentVaultPath) + // On some systems, removing a symlink may fail + // Just ignore and try to create/update it anyway } } - // Create new symlink pointing to the vault - targetPath := vaultDir - secret.Debug("Creating vault symlink", "target", targetPath, "link", currentVaultPath) - - // For real filesystems, try to create a real symlink first + // Try to create a real symlink first (works on Unix systems) if _, ok := fs.(*afero.OsFs); ok { - if err := os.Symlink(targetPath, currentVaultPath); err != nil { - // If symlink creation fails, fall back to writing target path to file - secret.Debug("Failed to create real symlink, falling back to file", "error", err) - if err := afero.WriteFile(fs, currentVaultPath, []byte(targetPath), 0600); err != nil { - return fmt.Errorf("failed to create vault symlink: %w", err) - } - } - } else { - // For in-memory filesystems, write target path to file - if err := afero.WriteFile(fs, currentVaultPath, []byte(targetPath), 0600); err != nil { - return fmt.Errorf("failed to create vault symlink: %w", err) + secret.Debug("Creating vault symlink", "target", targetPath, "link", currentVaultPath) + if err := os.Symlink(targetPath, currentVaultPath); err == nil { + secret.Debug("Successfully selected vault", "vault_name", name) + return nil } + // If symlink creation fails, fall back to regular file + } + + // Fallback: create a regular file with the target path + secret.Debug("Fallback: creating regular file with target path", "target", targetPath) + if err := afero.WriteFile(fs, currentVaultPath, []byte(targetPath), secret.FilePerms); err != nil { + return fmt.Errorf("failed to select vault: %w", err) } secret.Debug("Successfully selected vault", "vault_name", name) diff --git a/internal/vault/secrets.go b/internal/vault/secrets.go index 8f0311e..3d8ad62 100644 --- a/internal/vault/secrets.go +++ b/internal/vault/secrets.go @@ -120,7 +120,7 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error { // Create secret directory secret.Debug("Creating secret directory", "secret_dir", secretDir) - if err := v.fs.MkdirAll(secretDir, 0700); err != nil { + if err := v.fs.MkdirAll(secretDir, secret.DirPerms); err != nil { secret.Debug("Failed to create secret directory", "error", err, "secret_dir", secretDir) return fmt.Errorf("failed to create secret directory: %w", err) } @@ -145,7 +145,7 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error { // Step 2: Store the secret's public key pubKeyPath := filepath.Join(secretDir, "pub.age") secret.Debug("Writing secret public key", "path", pubKeyPath) - if err := afero.WriteFile(v.fs, pubKeyPath, []byte(secretPublicKey), 0600); err != nil { + if err := afero.WriteFile(v.fs, pubKeyPath, []byte(secretPublicKey), secret.FilePerms); err != nil { secret.Debug("Failed to write secret public key", "error", err, "path", pubKeyPath) return fmt.Errorf("failed to write secret public key: %w", err) } @@ -167,7 +167,7 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error { // Step 4: Store the encrypted secret value as value.age valuePath := filepath.Join(secretDir, "value.age") secret.Debug("Writing encrypted secret value", "path", valuePath) - if err := afero.WriteFile(v.fs, valuePath, encryptedValue, 0600); err != nil { + if err := afero.WriteFile(v.fs, valuePath, encryptedValue, secret.FilePerms); err != nil { secret.Debug("Failed to write encrypted secret value", "error", err, "path", valuePath) return fmt.Errorf("failed to write encrypted secret value: %w", err) } @@ -209,7 +209,7 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error { // Step 7: Store the encrypted secret private key as priv.age privKeyPath := filepath.Join(secretDir, "priv.age") secret.Debug("Writing encrypted secret private key", "path", privKeyPath) - if err := afero.WriteFile(v.fs, privKeyPath, encryptedPrivKey, 0600); err != nil { + if err := afero.WriteFile(v.fs, privKeyPath, encryptedPrivKey, secret.FilePerms); err != nil { secret.Debug("Failed to write encrypted secret private key", "error", err, "path", privKeyPath) return fmt.Errorf("failed to write encrypted secret private key: %w", err) } @@ -240,7 +240,7 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error { metadataPath := filepath.Join(secretDir, "secret-metadata.json") secret.Debug("Writing secret metadata", "path", metadataPath) - if err := afero.WriteFile(v.fs, metadataPath, metadataBytes, 0600); err != nil { + if err := afero.WriteFile(v.fs, metadataPath, metadataBytes, secret.FilePerms); err != nil { secret.Debug("Failed to write secret metadata", "error", err, "path", metadataPath) return fmt.Errorf("failed to write secret metadata: %w", err) } diff --git a/internal/vault/unlock_keys.go b/internal/vault/unlock_keys.go index a96e5ce..b84f16a 100644 --- a/internal/vault/unlock_keys.go +++ b/internal/vault/unlock_keys.go @@ -288,7 +288,7 @@ func (v *Vault) SelectUnlockKey(keyID string) error { } // Create new symlink - return afero.WriteFile(v.fs, currentUnlockKeyPath, []byte(targetKeyDir), 0600) + return afero.WriteFile(v.fs, currentUnlockKeyPath, []byte(targetKeyDir), secret.FilePerms) } // CreatePassphraseKey creates a new passphrase-protected unlock key @@ -301,7 +301,7 @@ func (v *Vault) CreatePassphraseKey(passphrase string) (*secret.PassphraseUnlock // Create unlock key directory with timestamp timestamp := time.Now().Format("2006-01-02.15.04") unlockKeyDir := filepath.Join(vaultDir, "unlock.d", "passphrase") - if err := v.fs.MkdirAll(unlockKeyDir, 0700); err != nil { + if err := v.fs.MkdirAll(unlockKeyDir, secret.DirPerms); err != nil { return nil, fmt.Errorf("failed to create unlock key directory: %w", err) } @@ -313,7 +313,7 @@ func (v *Vault) CreatePassphraseKey(passphrase string) (*secret.PassphraseUnlock // Write public key pubKeyPath := filepath.Join(unlockKeyDir, "pub.age") - if err := afero.WriteFile(v.fs, pubKeyPath, []byte(unlockIdentity.Recipient().String()), 0600); err != nil { + if err := afero.WriteFile(v.fs, pubKeyPath, []byte(unlockIdentity.Recipient().String()), secret.FilePerms); err != nil { return nil, fmt.Errorf("failed to write unlock key public key: %w", err) } @@ -326,7 +326,7 @@ func (v *Vault) CreatePassphraseKey(passphrase string) (*secret.PassphraseUnlock // Write encrypted private key privKeyPath := filepath.Join(unlockKeyDir, "priv.age") - if err := afero.WriteFile(v.fs, privKeyPath, encryptedPrivKey, 0600); err != nil { + if err := afero.WriteFile(v.fs, privKeyPath, encryptedPrivKey, secret.FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted unlock key private key: %w", err) } @@ -346,8 +346,8 @@ func (v *Vault) CreatePassphraseKey(passphrase string) (*secret.PassphraseUnlock } metadataPath := filepath.Join(unlockKeyDir, "unlock-metadata.json") - if err := afero.WriteFile(v.fs, metadataPath, metadataBytes, 0600); err != nil { - return nil, fmt.Errorf("failed to write metadata: %w", err) + if err := afero.WriteFile(v.fs, metadataPath, metadataBytes, secret.FilePerms); err != nil { + return nil, fmt.Errorf("failed to write unlock key metadata: %w", err) } // Encrypt long-term private key to this unlock key if vault is unlocked @@ -359,7 +359,7 @@ func (v *Vault) CreatePassphraseKey(passphrase string) (*secret.PassphraseUnlock } ltPrivKeyPath := filepath.Join(unlockKeyDir, "longterm.age") - if err := afero.WriteFile(v.fs, ltPrivKeyPath, encryptedLtPrivKey, 0600); err != nil { + if err := afero.WriteFile(v.fs, ltPrivKeyPath, encryptedLtPrivKey, secret.FilePerms); err != nil { return nil, fmt.Errorf("failed to write encrypted long-term private key: %w", err) } } diff --git a/internal/vault/vault_test.go b/internal/vault/vault_test.go index e68de3f..30d463e 100644 --- a/internal/vault/vault_test.go +++ b/internal/vault/vault_test.go @@ -128,7 +128,7 @@ func TestVaultOperations(t *testing.T) { // Write the correct public key to the pub.age file pubKeyPath := filepath.Join(vaultDir, "pub.age") - err = afero.WriteFile(fs, pubKeyPath, []byte(ltPublicKey), 0600) + err = afero.WriteFile(fs, pubKeyPath, []byte(ltPublicKey), secret.FilePerms) if err != nil { t.Fatalf("Failed to write long-term public key: %v", err) }