Read secret environment variables once per command, then unset them (closes #60)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
init and vault create put the mnemonic into the process environment for vault.CreateVault to read back, so every program they ran, gpg included, inherited it, and SB_SECRET_MNEMONIC and SB_UNLOCK_PASSPHRASE were read at 13 places and never unset. Each command that may need them now reads both once, in its RunE, into locked buffers on the CLI Instance, and unsets them at once. The buffers are passed down: vault.CreateVault takes the mnemonic, a Vault carries Mnemonic and UnlockPassphrase, and the PGP, keychain and Secure Enclave unlocker constructors take both. Nothing below the command reads the environment. README warns against both variables. Model: opus-5-5
This commit is contained in:
+13
-7
@@ -3,12 +3,12 @@ package vault
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"filippo.io/age"
|
||||
"git.eeqj.de/sneak/secret/internal/secret"
|
||||
"git.eeqj.de/sneak/secret/pkg/agehd"
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
@@ -18,6 +18,13 @@ type Vault struct {
|
||||
fs afero.Fs
|
||||
stateDir string
|
||||
longTermKey *age.X25519Identity // In-memory long-term key when unlocked
|
||||
// Mnemonic, when not nil, is what the long-term key is derived from
|
||||
// instead of the current unlocker. The caller destroys it.
|
||||
Mnemonic *memguard.LockedBuffer
|
||||
// UnlockPassphrase, when not nil, is given to the current unlocker
|
||||
// when that is a passphrase unlocker, which otherwise prompts for it.
|
||||
// The caller destroys it.
|
||||
UnlockPassphrase *memguard.LockedBuffer
|
||||
}
|
||||
|
||||
// NewVault creates a new Vault instance
|
||||
@@ -66,9 +73,8 @@ func (v *Vault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
|
||||
|
||||
secret.Debug("Vault is locked, attempting to unlock", "vault_name", v.Name)
|
||||
|
||||
// Try to derive from environment mnemonic first
|
||||
if envMnemonic := os.Getenv(secret.EnvMnemonic); envMnemonic != "" {
|
||||
return v.deriveLongTermKeyFromMnemonic(envMnemonic)
|
||||
if v.Mnemonic != nil {
|
||||
return v.deriveLongTermKeyFromMnemonic(v.Mnemonic.String())
|
||||
}
|
||||
|
||||
// No mnemonic available, try to use current unlocker
|
||||
@@ -181,9 +187,9 @@ func (v *Vault) NumSecrets() (int, error) {
|
||||
// deriveLongTermKeyFromMnemonic derives the long-term key from the given
|
||||
// mnemonic, verifies it against the vault metadata, and caches it in memory.
|
||||
func (v *Vault) deriveLongTermKeyFromMnemonic(
|
||||
envMnemonic string,
|
||||
mnemonic string,
|
||||
) (*age.X25519Identity, error) {
|
||||
secret.Debug("Using mnemonic from environment for long-term key derivation",
|
||||
secret.Debug("Using mnemonic for long-term key derivation",
|
||||
"vault_name", v.Name)
|
||||
|
||||
// Load vault metadata to get the derivation index
|
||||
@@ -199,7 +205,7 @@ func (v *Vault) deriveLongTermKeyFromMnemonic(
|
||||
return nil, fmt.Errorf("failed to load vault metadata: %w", err)
|
||||
}
|
||||
|
||||
ltIdentity, err := agehd.DeriveIdentity(envMnemonic, metadata.DerivationIndex)
|
||||
ltIdentity, err := agehd.DeriveIdentity(mnemonic, metadata.DerivationIndex)
|
||||
if err != nil {
|
||||
secret.Debug("Failed to derive long-term key from mnemonic",
|
||||
"error", err, "vault_name", v.Name)
|
||||
|
||||
Reference in New Issue
Block a user