1Password / shell-plugins

Seamless authentication for every tool in your terminal.
https://developer.1password.com/docs/cli/shell-plugins/
MIT License
519 stars 170 forks source link

AWS Vault importer failing when using encrypted file as backend #248

Open williamhpark opened 1 year ago

williamhpark commented 1 year ago

Platform or tool

AWS

Desired or expected behavior

  1. Add a profile through aws-vault using an encrypted file as the backend: aws-vault add file-profile --backend file
  2. Run op plugin init aws > Import into 1Password...

You should see Encrypted file (file-profile) as one of the import candidates, alongside any other valid import candidates that should show based on where you stored your AWS credentials.

Current behavior

You get a long invalid memory address or nil pointer dereference error in the terminal, and no import candidates (AWS Vault, file importer) are shown.

The error is being triggered when calling keyring.AvailableBackends(). The cause is that in the keyringConfigDefaults var, FilePasswordFunc is not currently initialized. When keyring.AvailableBackends() is called, an attempt is made to access k.passwordFunc even though it doesn't exist.

Relevant log output

encountered error while importing credentials automatically: {locally built plugin panicked: runtime error: invalid memory address or nil pointer dereference
stack trace:
goroutine 55 [running]:
runtime/debug.Stack()
        /usr/local/Cellar/go/1.19.5/libexec/src/runtime/debug/stack.go:24 +0x65
github.com/1Password/shell-plugins/sdk/rpc/server.getPanicDiagnostics({0x100664340, 0x100bee580})
        /Users/williampark/repos/shell-plugins/sdk/rpc/server/server.go:208 +0x27
github.com/1Password/shell-plugins/sdk/rpc/server.(*RPCServer).CredentialImport.func1()
        /Users/williampark/repos/shell-plugins/sdk/rpc/server/server.go:123 +0x39
panic({0x100664340, 0x100bee580})
        /usr/local/Cellar/go/1.19.5/libexec/src/runtime/panic.go:884 +0x212
github.com/99designs/keyring.(*fileKeyring).unlock(0xc0004ae390)
        /Users/williampark/repos/shell-plugins/vendor/github.com/99designs/keyring/file.go:61 +0x7d
github.com/99designs/keyring.(*fileKeyring).Get(0xc0004ae390, {0xc00046e608?, 0x10062f380?})
        /Users/williampark/repos/shell-plugins/vendor/github.com/99designs/keyring/file.go:84 +0xc5
github.com/99designs/aws-vault/v7/vault.(*CredentialKeyring).Get(0xc00048cd60, {0xc00046e608, 0xc})
        /Users/williampark/repos/shell-plugins/vendor/github.com/99designs/aws-vault/v7/vault/credentialkeyring.go:42 +0x9b
github.com/1Password/shell-plugins/plugins/aws.TryAWSVaultBackends.func1({0x1?, 0x1?}, {{0xc00046e0c0, 0x12}, {0x0, 0x0}, {0xc000400a58, 0x6}}, 0xc000410a38)
        /Users/williampark/repos/shell-plugins/plugins/aws/importers.go:165 +0x916
github.com/1Password/shell-plugins/sdk/importer.TryAll.func1({0x1008239d0, 0xc00003a068}, {{0xc00046e0c0, 0x12}, {0x0, 0x0}, {0xc000400a58, 0x6}}, 0x0?)
        /Users/williampark/repos/shell-plugins/sdk/importer/helpers.go:12 +0xb3
github.com/1Password/shell-plugins/sdk/rpc/server.(*RPCServer).CredentialImport(0xc00042c660?, {0x0, {{0xc00046e0c0, 0x12}, {0x0, 0x0}, {0xc000400a58, 0x6}}, {{0x0, 0x0, ...}}}, ...)
        /Users/williampark/repos/shell-plugins/sdk/rpc/server/server.go:142 +0x12e
reflect.Value.call({0xc000280f00?, 0xc0002882c0?, 0x13?}, {0x1007194f0, 0x4}, {0xc000091ef8, 0x3, 0x3?})
        /usr/local/Cellar/go/1.19.5/libexec/src/reflect/value.go:584 +0x8c5
reflect.Value.Call({0xc000280f00?, 0xc0002882c0?, 0x0?}, {0xc0003b46f8?, 0xc0000ac580?, 0xc0003b474c?})
        /usr/local/Cellar/go/1.19.5/libexec/src/reflect/value.go:368 +0xbc
net/rpc.(*service).call(0xc000290780, 0x0?, 0xc00004e7e0?, 0xc00028b7e0, 0xc00028e800, 0xc0003b47d0?, {0x1006c30a0?, 0xc00041e1e0?, 0x1005d24c6?}, {0x1006789c0, ...}, ...)
        /usr/local/Cellar/go/1.19.5/libexec/src/net/rpc/server.go:382 +0x226
created by net/rpc.(*Server).ServeCodec
        /usr/local/Cellar/go/1.19.5/libexec/src/net/rpc/server.go:479 +0x3fe

op CLI version

Internal build based off of 2.16.1

williamhpark commented 1 year ago

A potential solution is to assign FilePasswordFunc similar to how it's done in the AWS Vault codebase:

func fileKeyringPassphrasePrompt(prompt string) (string, error) {
    if password, ok := os.LookupEnv("AWS_VAULT_FILE_PASSPHRASE"); ok {
        return password, nil
    }

    fmt.Fprintf(os.Stderr, "%s: ", prompt)
    b, err := term.ReadPassword(int(os.Stdin.Fd()))
    if err != nil {
        return "", err
    }
    fmt.Println()
    return string(b), nil
}

var keyringConfigDefaults = keyring.Config{
    ...
    FilePasswordFunc:         fileKeyringPassphrasePrompt,
    ...
}

The resultant behaviour of this solution is that after selecting Import into 1Password..., the terminal will hang until the user inputs a password. If the password matches the one used to encrypt the file, the encrypted file shows as an import candidate. The problem with this at the moment is that I was unable to find a straightforward way to to output a prompt to the terminal through os.Stdout, something like Encrypted file passphrase:. Any suggestions on how I can do this?Currently, the terminal just hangs.

If there's currently no good solution for what I just mentioned, a sub-optimal solution could be to assign a dummy function. This way, encrypted file import would not be supported, but at least the long invalid memory address or nil pointer dereference error is avoided:

FilePasswordFunc: func(s string) (string, error) { return "", nil },

williamhpark commented 1 year ago

Branch created here: wpark/248-aws-vault-importer-file-error

AndyTitu commented 1 year ago

This approach looks right to me!