Skip to content

Commit

Permalink
Configurable KV/transit engines (#24)
Browse files Browse the repository at this point in the history
Hey!

I wrote a little patch for support configurable engines mountpoints and
keys in Vault

This code allows to:
- set non-standard mountpoints/names for transit/kv engine
- set different key name for transit key (as it's unable to be nested in
transit, while kv allows it)

Naming is inconsistent yet, and not very well-thought, so I'm open for
suggestions :)

---------

Signed-off-by: Tomasz Krzyzanowski <[email protected]>
  • Loading branch information
tomaszkrzyzanowski authored Aug 22, 2024
1 parent f98b86d commit 26a8c7a
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 19 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,28 @@ Note: the `--id` should be identical to your `--key_name` in the previous step.
```bash
./notation verify <myRegistry>/<myRepo>@<digest> -v
```
## hc-vault plugin options (passed as `notation sign (...)` command options
|Option name |Usage |Description |
|---------------|----------------------------------------|-----------------------------------------------------------------------|
|id |`--id <keyName>` |(required) default name for transit key and kv key |
|kvName |`--plugin-config kvName=<name>` |(default: `secret`) custom name for key-value(KVv2) secret engine mount|
|transitName |`--plugin-config transitName=<name>` |(default: `transit`) custom name for transit secret engine mount |
|transitKeyName |`--plugin-config transitKeyName=<name>` |custom name for transit key (overrides `id`) |
## key-helper import options
notation-hashicorp-vault % ./cmd/key-helper/key-helper import --help
import private key to Vault Transit secrets engine and certificates to Vault KV secrets engine
Usage:
key-helper import --key_path <path_to_private key file> --cert_path <path_to__certificate_chain_file> --key_name <HashiCorp_Vault_key_name> [flags]
Flags:
--cert_path string absolute path to the certificate chain file
-h, --help help for import
--key_name string name of the key
--key_path string absolute path to the private key file
--kv_name string name of the KVv2 secret engine mount (default "secret")
--transit_key_name string name of the key in transit engine
--transit_name string name of the transit engine mount (default "transit")
notation-hashicorp-vault %
36 changes: 27 additions & 9 deletions cmd/key-helper/importKey.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,27 @@ func importKeyCommand() *cobra.Command {
if err != nil {
return err
}
kvEngineMountName, err := cmd.Flags().GetString("kv_name")
if err != nil {
return err
}
transitKeyName, err := cmd.Flags().GetString("transit_key_name")
if err != nil {
return err
}
if len(transitKeyName) == 0 {
transitKeyName = keyName
}
transitEngineMountName, err := cmd.Flags().GetString("transit_name")
if err != nil {
return err
}
vaultClient, err := getVaultClient(ctx)
if err != nil {
return err
}
fmt.Println("Successfully got vault client")
wrappingKey, err := getWrappingKey(ctx, vaultClient)
wrappingKey, err := getWrappingKey(ctx, vaultClient, transitEngineMountName)
if err != nil {
return err
}
Expand All @@ -56,11 +71,11 @@ func importKeyCommand() *cobra.Command {
if err != nil {
return err
}
if err := importKeyToTransit(ctx, vaultClient, ciphertext, keyName, keyType); err != nil {
if err := importKeyToTransit(ctx, vaultClient, ciphertext, transitKeyName, keyType, transitEngineMountName); err != nil {
return err
}
fmt.Println("Successfully imported key to transit")
if err := importCertToKV(ctx, vaultClient, certPath, keyName); err != nil {
if err := importCertToKV(ctx, vaultClient, certPath, keyName, kvEngineMountName); err != nil {
return err
}
fmt.Println("Successfully imported cert to kv")
Expand All @@ -70,6 +85,9 @@ func importKeyCommand() *cobra.Command {
importKeyCmd.Flags().String("key_path", "", "absolute path to the private key file")
importKeyCmd.Flags().String("cert_path", "", "absolute path to the certificate chain file")
importKeyCmd.Flags().String("key_name", "", "name of the key")
importKeyCmd.Flags().String("kv_name", "secret", "name of the KVv2 secret engine mount")
importKeyCmd.Flags().String("transit_key_name", "", "name of the key in transit engine")
importKeyCmd.Flags().String("transit_name", "transit", "name of the transit engine mount")
return importKeyCmd
}

Expand All @@ -85,9 +103,9 @@ func getVaultClient(ctx context.Context) (*vault.Client, error) {
})
}

func getWrappingKey(ctx context.Context, client *vault.Client) (string, error) {
func getWrappingKey(ctx context.Context, client *vault.Client, mountName string) (string, error) {
// get transit SE wrapping key
path := "/transit/wrapping_key"
path := "/" + mountName + "/wrapping_key"
resp, err := client.Logical().ReadWithContext(ctx, path)
if err != nil {
return "", err
Expand Down Expand Up @@ -163,8 +181,8 @@ func wrapPrivateKey(wrappingKey string, privateKeyPath string) (string, string,
return base64Ciphertext, keyType, nil
}

func importKeyToTransit(ctx context.Context, client *vault.Client, ciphertext string, keyName string, keyType string) error {
path := fmt.Sprintf("/transit/keys/%s/import", keyName)
func importKeyToTransit(ctx context.Context, client *vault.Client, ciphertext string, keyName string, keyType string, engineMount string) error {
path := fmt.Sprintf("/%s/keys/%s/import", engineMount, keyName)
req := map[string]interface{}{
"allow_plaintext_backup": false,
"allow_rotation": false,
Expand All @@ -181,7 +199,7 @@ func importKeyToTransit(ctx context.Context, client *vault.Client, ciphertext st
return err
}

func importCertToKV(ctx context.Context, client *vault.Client, certPath string, keyName string) error {
func importCertToKV(ctx context.Context, client *vault.Client, certPath string, keyName string, engineMount string) error {
certFile, err := os.Open(certPath)
if err != nil {
return err
Expand All @@ -192,6 +210,6 @@ func importCertToKV(ctx context.Context, client *vault.Client, certPath string,
}
data := make(map[string]interface{})
data["certificate"] = string(bytes)
_, err = client.KVv2("secret").Put(ctx, keyName, data)
_, err = client.KVv2(engineMount).Put(ctx, keyName, data)
return err
}
6 changes: 3 additions & 3 deletions cmd/notation-hc-vault/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func runDescribeKey(ctx context.Context, input io.Reader) (*proto.DescribeKeyRes
}

// get key spec for notation
keySpec, err := notationKeySpec(ctx, req.KeyID)
keySpec, err := notationKeySpec(ctx, req.KeyID, req.PluginConfig)
if err != nil {
return nil, err
}
Expand All @@ -33,8 +33,8 @@ func runDescribeKey(ctx context.Context, input io.Reader) (*proto.DescribeKeyRes
}, nil
}

func notationKeySpec(ctx context.Context, keyID string) (proto.KeySpec, error) {
vaultClient, err := NewVaultClientFromKeyID(keyID)
func notationKeySpec(ctx context.Context, keyID string, pluginConfig map[string]string) (proto.KeySpec, error) {
vaultClient, err := NewVaultClientFromKeyID(keyID, pluginConfig)
if err != nil {
return "", err
}
Expand Down
31 changes: 25 additions & 6 deletions internal/keyvault/keyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import (
type VaultClientWrapper struct {
vaultClient *vault.Client

keyID string
keyID string
kvEngineName string
transitEngineName string
transitKeyID string
}

func NewVaultClientFromKeyID(id string) (*VaultClientWrapper, error) {
func NewVaultClientFromKeyID(id string, pluginConfig map[string]string) (*VaultClientWrapper, error) {
// read addr and token from environment variables
vaultAddr := os.Getenv("VAULT_ADDR")
if len(vaultAddr) < 1 {
Expand All @@ -33,15 +36,31 @@ func NewVaultClientFromKeyID(id string) (*VaultClientWrapper, error) {
return nil, err
}

transitName, ok := pluginConfig["transitName"]
if !ok {
transitName = "transit"
}
kvName, ok := pluginConfig["kvName"]
if !ok {
kvName = "secret"
}
transitKeyName, ok := pluginConfig["transitKeyName"]
if !ok {
transitKeyName = id
}

return &VaultClientWrapper{
vaultClient: client,
keyID: id,
vaultClient: client,
keyID: id,
kvEngineName: kvName,
transitEngineName: transitName,
transitKeyID: transitKeyName,
}, nil
}

func (vw *VaultClientWrapper) GetCertificateChain(ctx context.Context) ([]*x509.Certificate, error) {
// read a certChain
secret, err := vw.vaultClient.KVv2("secret").Get(ctx, vw.keyID)
secret, err := vw.vaultClient.KVv2(vw.kvEngineName).Get(ctx, vw.keyID)
if err != nil {
return nil, err
}
Expand All @@ -63,7 +82,7 @@ func (vw *VaultClientWrapper) SignWithTransit(ctx context.Context, encodedData s
"signature_algorithm": signAlgorithm,
"hash_algorithm": hashAlgorithm,
}
path := "transit/sign/" + vw.keyID
path := vw.transitEngineName + "/sign/" + vw.transitKeyID
resp, err := vw.vaultClient.Logical().WriteWithContext(ctx, path, transitSignReq)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/signature/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func Sign(ctx context.Context, req *proto.GenerateSignatureRequest) (*proto.Gene
}
}

vaultClient, err := keyvault.NewVaultClientFromKeyID(req.KeyID)
vaultClient, err := keyvault.NewVaultClientFromKeyID(req.KeyID, req.PluginConfig)
if err != nil {
return nil, &proto.RequestError{
Code: proto.ErrorCodeGeneric,
Expand Down

0 comments on commit 26a8c7a

Please sign in to comment.