Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d72043a859 |
No files matched your search
@@ -232,18 +232,13 @@ func TestCrossVaultMoveUnlocksBeforeLocking(t *testing.T) {
|
|||||||
// TestCrossVaultMoveChecksAgainOnceLocked runs a move between vaults, given
|
// TestCrossVaultMoveChecksAgainOnceLocked runs a move between vaults, given
|
||||||
// the mnemonic, as TestCommandsCheckAgainOnceLocked runs the commands in its
|
// the mnemonic, as TestCommandsCheckAgainOnceLocked runs the commands in its
|
||||||
// table: once the move waits for the state directory lock, the test removes
|
// table: once the move waits for the state directory lock, the test removes
|
||||||
// the source secret or the destination vault, or creates the destination
|
// the source secret or the destination vault, and the move must then fail
|
||||||
// vault again from another mnemonic, and the move must then fail and change
|
// and change nothing.
|
||||||
// nothing.
|
|
||||||
//
|
//
|
||||||
//nolint:paralleltest // waitingForLock sees any test's command waiting for the lock
|
//nolint:paralleltest // waitingForLock sees any test's command waiting for the lock
|
||||||
func TestCrossVaultMoveChecksAgainOnceLocked(t *testing.T) {
|
func TestCrossVaultMoveChecksAgainOnceLocked(t *testing.T) {
|
||||||
mnemonic := testMnemonicBuffer(t)
|
mnemonic := testMnemonicBuffer(t)
|
||||||
|
|
||||||
otherMnemonic := memguard.NewBufferFromBytes([]byte("legal winner thank " +
|
|
||||||
"year wave sausage worth useful legal winner thank yellow"))
|
|
||||||
t.Cleanup(otherMnemonic.Destroy)
|
|
||||||
|
|
||||||
move := func(cli *Instance) error {
|
move := func(cli *Instance) error {
|
||||||
cli.Mnemonic = mnemonic
|
cli.Mnemonic = mnemonic
|
||||||
|
|
||||||
@@ -261,19 +256,6 @@ func TestCrossVaultMoveChecksAgainOnceLocked(t *testing.T) {
|
|||||||
return fs.RemoveAll(testStateDir + "/vaults.d/other")
|
return fs.RemoveAll(testStateDir + "/vaults.d/other")
|
||||||
}, vault.ErrVaultNotFound)
|
}, vault.ErrVaultNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("destination vault created again from another mnemonic", func(t *testing.T) {
|
|
||||||
requireChecksAgainOnceLocked(t, move, func(fs afero.Fs) error {
|
|
||||||
err := fs.RemoveAll(testStateDir + "/vaults.d/other")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = vault.CreateVault(fs, testStateDir, "other", otherMnemonic, nil)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}, errVaultCreatedAgain)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCrossVaultMoveRefusesExistingSecretBeforeUnlocking checks that a move
|
// TestCrossVaultMoveRefusesExistingSecretBeforeUnlocking checks that a move
|
||||||
|
|||||||
+5
-13
@@ -1084,13 +1084,10 @@ func (cli *Instance) sameDirectory(dir, other string) (bool, error) {
|
|||||||
|
|
||||||
// moveSecretCrossVault handles moving between two different vaults. Its
|
// moveSecretCrossVault handles moving between two different vaults. Its
|
||||||
// caller, MoveSecret, has already checked both secret names and that both
|
// caller, MoveSecret, has already checked both secret names and that both
|
||||||
// vaults exist. The copy needs the source vault's long-term key and is
|
// vaults exist. The copy needs the source vault's long-term key; the
|
||||||
// encrypted to the destination vault's long-term public key. The
|
// destination vault is unlocked only to check that it can be. Either can ask
|
||||||
// destination vault is unlocked to check that the copy will open with what
|
// for a passphrase, so both happen before it takes the state directory lock,
|
||||||
// the user gave, and, once the state directory lock is held, that it still
|
// and no other command waits while the user answers.
|
||||||
// has the key unlocked. Either unlock can ask for a passphrase, so both
|
|
||||||
// happen before the lock is taken, and no other command waits while the
|
|
||||||
// user answers.
|
|
||||||
func (cli *Instance) moveSecretCrossVault(
|
func (cli *Instance) moveSecretCrossVault(
|
||||||
cmd *cobra.Command,
|
cmd *cobra.Command,
|
||||||
srcVault *vault.Vault, srcSecretName string,
|
srcVault *vault.Vault, srcSecretName string,
|
||||||
@@ -1111,7 +1108,7 @@ func (cli *Instance) moveSecretCrossVault(
|
|||||||
destVault.Mnemonic, destVault.UnlockPassphrase = cli.Mnemonic, cli.UnlockPassphrase
|
destVault.Mnemonic, destVault.UnlockPassphrase = cli.Mnemonic, cli.UnlockPassphrase
|
||||||
|
|
||||||
// Unlock destination vault (will fail if neither mnemonic nor unlocker available)
|
// Unlock destination vault (will fail if neither mnemonic nor unlocker available)
|
||||||
destIdentity, err := destVault.GetOrDeriveLongTermKey()
|
_, err = destVault.GetOrDeriveLongTermKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to unlock destination vault '%s': %w", destVault.Name, err)
|
return fmt.Errorf("failed to unlock destination vault '%s': %w", destVault.Name, err)
|
||||||
}
|
}
|
||||||
@@ -1128,11 +1125,6 @@ func (cli *Instance) moveSecretCrossVault(
|
|||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
err = cli.checkLongTermKey(destVault, destIdentity)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli.copySecretAndRemoveSource(
|
return cli.copySecretAndRemoveSource(
|
||||||
cmd, srcVault, srcSecretName, destVault, destSecretName, force)
|
cmd, srcVault, srcSecretName, destVault, destSecretName, force)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"filippo.io/age"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sneak.berlin/go/secret/internal/secret"
|
"sneak.berlin/go/secret/internal/secret"
|
||||||
@@ -443,41 +442,29 @@ func (cli *Instance) unlockThenLock(vlt *vault.Vault) (func(), error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cli.checkLongTermKey(vlt, ltIdentity)
|
_, err = cli.existingVault(vlt.GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
release()
|
release()
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return release, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkLongTermKey checks, for the vault vlt unlocked as ltIdentity before
|
|
||||||
// the state directory lock was taken, that vlt still exists and that its
|
|
||||||
// long-term public key is still that of ltIdentity. Another command may have
|
|
||||||
// removed the vault in the meantime, or removed it and created it again with
|
|
||||||
// another long-term key. The caller holds the lock.
|
|
||||||
func (cli *Instance) checkLongTermKey(
|
|
||||||
vlt *vault.Vault, ltIdentity *age.X25519Identity,
|
|
||||||
) error {
|
|
||||||
_, err := cli.existingVault(vlt.GetName())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vaultDir, _ := vlt.GetDirectory()
|
vaultDir, _ := vlt.GetDirectory()
|
||||||
|
|
||||||
ltPubKey, err := afero.ReadFile(cli.fs, filepath.Join(vaultDir, "pub.age"))
|
ltPubKey, err := afero.ReadFile(cli.fs, filepath.Join(vaultDir, "pub.age"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read long-term public key: %w", err)
|
release()
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("failed to read long-term public key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(ltPubKey) != ltIdentity.Recipient().String() {
|
if string(ltPubKey) != ltIdentity.Recipient().String() {
|
||||||
return fmt.Errorf("vault '%s' %w", vlt.GetName(), errVaultCreatedAgain)
|
release()
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("vault '%s' %w", vlt.GetName(), errVaultCreatedAgain)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return release, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoSelectUnlocker selects the newly created unlocker as current,
|
// autoSelectUnlocker selects the newly created unlocker as current,
|
||||||
|
|||||||
Reference in new issue
Block a user