forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix transit byok tool, add docs, tests (hashicorp#19373)
* Fix Vault Transit BYOK helper argument parsing This commit fixes the following issues with the importer: - More than two arguments were not supported, causing the CLI to error out and resulting in a failure to import RSA keys. - The @file notation support was not accepted for KEY, meaning unencrypted keys had to be manually specified on the CLI. - Parsing of additional argument data was done in a non-standard way. - Fix parsing of command line options and ensure only relevant options are included. Additionally, some error messages and help text was clarified. Signed-off-by: Alexander Scheel <[email protected]> * Add missing documentation on Transit CLI to website Signed-off-by: Alexander Scheel <[email protected]> * Add tests for Transit BYOK vault subcommand Signed-off-by: Alexander Scheel <[email protected]> * Add changelog Signed-off-by: Alexander Scheel <[email protected]> * Appease CI Signed-off-by: Alexander Scheel <[email protected]> --------- Signed-off-by: Alexander Scheel <[email protected]>
- Loading branch information
Showing
7 changed files
with
347 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:bug | ||
cli/transit: Fix import, import-version command invocation | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package command | ||
|
||
import ( | ||
"bytes" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"testing" | ||
|
||
"github.com/hashicorp/vault/api" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// Validate the `vault transit import` command works. | ||
func TestTransitImport(t *testing.T) { | ||
t.Parallel() | ||
|
||
client, closer := testVaultServer(t) | ||
defer closer() | ||
|
||
if err := client.Sys().Mount("transit", &api.MountInput{ | ||
Type: "transit", | ||
}); err != nil { | ||
t.Fatalf("transit mount error: %#v", err) | ||
} | ||
|
||
rsa1, rsa2, aes128, aes256 := generateKeys(t) | ||
|
||
type testCase struct { | ||
variant string | ||
path string | ||
key []byte | ||
args []string | ||
shouldFail bool | ||
} | ||
tests := []testCase{ | ||
{ | ||
"import", | ||
"transit/keys/rsa1", | ||
rsa1, | ||
[]string{"type=rsa-2048"}, | ||
false, /* first import */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/rsa1", | ||
rsa2, | ||
[]string{"type=rsa-2048"}, | ||
true, /* already exists */ | ||
}, | ||
{ | ||
"import-version", | ||
"transit/keys/rsa1", | ||
rsa2, | ||
[]string{"type=rsa-2048"}, | ||
false, /* new version */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/rsa2", | ||
rsa2, | ||
[]string{"type=rsa-4096"}, | ||
true, /* wrong type */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/rsa2", | ||
rsa2, | ||
[]string{"type=rsa-2048"}, | ||
false, /* new name */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/aes1", | ||
aes128, | ||
[]string{"type=aes128-gcm96"}, | ||
false, /* first import */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/aes1", | ||
aes256, | ||
[]string{"type=aes256-gcm96"}, | ||
true, /* already exists */ | ||
}, | ||
{ | ||
"import-version", | ||
"transit/keys/aes1", | ||
aes256, | ||
[]string{"type=aes256-gcm96"}, | ||
true, /* new version, different type */ | ||
}, | ||
{ | ||
"import-version", | ||
"transit/keys/aes1", | ||
aes128, | ||
[]string{"type=aes128-gcm96"}, | ||
false, /* new version */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/aes2", | ||
aes256, | ||
[]string{"type=aes128-gcm96"}, | ||
true, /* wrong type */ | ||
}, | ||
{ | ||
"import", | ||
"transit/keys/aes2", | ||
aes256, | ||
[]string{"type=aes256-gcm96"}, | ||
false, /* new name */ | ||
}, | ||
} | ||
|
||
for index, tc := range tests { | ||
t.Logf("Running test case %d: %v", index, tc) | ||
execTransitImport(t, client, tc.variant, tc.path, tc.key, tc.args, tc.shouldFail) | ||
} | ||
} | ||
|
||
func execTransitImport(t *testing.T, client *api.Client, method string, path string, key []byte, data []string, expectFailure bool) { | ||
t.Helper() | ||
|
||
keyBase64 := base64.StdEncoding.EncodeToString(key) | ||
|
||
var args []string | ||
args = append(args, "transit") | ||
args = append(args, method) | ||
args = append(args, path) | ||
args = append(args, keyBase64) | ||
args = append(args, data...) | ||
|
||
stdout := bytes.NewBuffer(nil) | ||
stderr := bytes.NewBuffer(nil) | ||
runOpts := &RunOptions{ | ||
Stdout: stdout, | ||
Stderr: stderr, | ||
Client: client, | ||
} | ||
|
||
code := RunCustom(args, runOpts) | ||
combined := stdout.String() + stderr.String() | ||
|
||
if code != 0 { | ||
if !expectFailure { | ||
t.Fatalf("Got unexpected failure from test (ret %d): %v", code, combined) | ||
} | ||
} else { | ||
if expectFailure { | ||
t.Fatalf("Expected failure, got success from test (ret %d): %v", code, combined) | ||
} | ||
} | ||
} | ||
|
||
func generateKeys(t *testing.T) (rsa1 []byte, rsa2 []byte, aes128 []byte, aes256 []byte) { | ||
t.Helper() | ||
|
||
priv1, err := rsa.GenerateKey(rand.Reader, 2048) | ||
require.NotNil(t, priv1, "failed generating RSA 1 key") | ||
require.NoError(t, err, "failed generating RSA 1 key") | ||
|
||
rsa1, err = x509.MarshalPKCS8PrivateKey(priv1) | ||
require.NotNil(t, rsa1, "failed marshaling RSA 1 key") | ||
require.NoError(t, err, "failed marshaling RSA 1 key") | ||
|
||
priv2, err := rsa.GenerateKey(rand.Reader, 2048) | ||
require.NotNil(t, priv2, "failed generating RSA 2 key") | ||
require.NoError(t, err, "failed generating RSA 2 key") | ||
|
||
rsa2, err = x509.MarshalPKCS8PrivateKey(priv2) | ||
require.NotNil(t, rsa2, "failed marshaling RSA 2 key") | ||
require.NoError(t, err, "failed marshaling RSA 2 key") | ||
|
||
aes128 = make([]byte, 128/8) | ||
_, err = rand.Read(aes128) | ||
require.NoError(t, err, "failed generating AES 128 key") | ||
|
||
aes256 = make([]byte, 256/8) | ||
_, err = rand.Read(aes256) | ||
require.NoError(t, err, "failed generating AES 256 key") | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--- | ||
layout: docs | ||
page_title: transit import and transit import-version - Command | ||
description: |- | ||
The "transit import" and "transit import-version" commands import the | ||
specified key into Transit, via the Transit BYOK mechanism. | ||
--- | ||
|
||
# transit import and transit import-version | ||
|
||
The `transit import` and `transit import-version` commands import the | ||
specified key into Transit, via the [Transit BYOK | ||
mechanism](/vault/docs/secrets/transit#bring-your-own-key-byok). The former | ||
imports this key as a new key, failing if it already exists, whereas the | ||
latter will only update an existing key in Transit to a new version of the | ||
key material. | ||
|
||
This needs access to read the transit mount's wrapping key (at | ||
`transit/wrapping_key`) and the ability to write to either import | ||
endpoints (either `transit/keys/:name/import` or | ||
`transit/keys/:name/import_version`). | ||
|
||
## Examples | ||
|
||
Imports a 2048-bit RSA key as a new key: | ||
|
||
``` | ||
$ vault transit import transit/keys/test-key @test-key type=rsa-2048 | ||
Retrieving transit wrapping key. | ||
Wrapping source key with ephemeral key. | ||
Encrypting ephemeral key with transit wrapping key. | ||
Submitting wrapped key to Vault transit. | ||
Success! | ||
``` | ||
|
||
Imports a new version of an existing key: | ||
|
||
``` | ||
$ vault transit import-version transit/keys/test-key @test-key-updated | ||
Retrieving transit wrapping key. | ||
Wrapping source key with ephemeral key. | ||
Encrypting ephemeral key with transit wrapping key. | ||
Submitting wrapped key to Vault transit. | ||
Success! | ||
``` | ||
|
||
## Usage | ||
|
||
This command does not have any unique flags and respects core Vault CLI | ||
commands. See `vault transit import -help` for more information. | ||
|
||
This command requires two positional arguments: | ||
|
||
1. `PATH`, the path to the transit key to import in the format of | ||
`<mount>/keys/<key-name>`, where `<mount>` is the path to the mount | ||
(using `-namespace=<ns>` to specify any namespaces), and `<key-name>` | ||
is the desired name of the key. | ||
2. `KEY`, the key material to import in Standard Base64 encoding (either | ||
of a raw key in the case of symmetric keys such as AES, or of the DER | ||
encoded format for asymmetric keys such as RSA). If the value for `KEY` | ||
begins with an `@`, the CLI argument is assumed to be a path to a file | ||
on disk to be read. |
Oops, something went wrong.