open-vault/builtin/logical/pki/path_manage_keys.go
Steven Clark 9607c5be97
Use backendUUID instead of mount points for managed keys (OSS) (#15441)
- Remove all references to mount point within PKI
 - Leverage the mount's backend UUID now instead of a mount point for all
   managed key lookups.
2022-05-16 12:48:54 -04:00

215 lines
6.2 KiB
Go

package pki
import (
"context"
"strings"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/certutil"
"github.com/hashicorp/vault/sdk/logical"
)
func pathGenerateKey(b *backend) *framework.Path {
return &framework.Path{
Pattern: "keys/generate/(internal|exported|kms)",
Fields: map[string]*framework.FieldSchema{
keyNameParam: {
Type: framework.TypeString,
Description: "Optional name to be used for this key",
},
keyTypeParam: {
Type: framework.TypeString,
Default: "rsa",
Description: `The type of key to use; defaults to RSA. "rsa"
"ec" and "ed25519" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec", "ed25519"},
DisplayAttrs: &framework.DisplayAttributes{
Value: "rsa",
},
},
keyBitsParam: {
Type: framework.TypeInt,
Default: 0,
Description: `The number of bits to use. Allowed values are
0 (universal default); with rsa key_type: 2048 (default), 3072, or
4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with
ed25519.`,
},
"managed_key_name": {
Type: framework.TypeString,
Description: `The name of the managed key to use when the exported
type is kms. When kms type is the key type, this field or managed_key_id
is required. Ignored for other types.`,
},
"managed_key_id": {
Type: framework.TypeString,
Description: `The name of the managed key to use when the exported
type is kms. When kms type is the key type, this field or managed_key_name
is required. Ignored for other types.`,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathGenerateKeyHandler,
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},
HelpSynopsis: pathGenerateKeyHelpSyn,
HelpDescription: pathGenerateKeyHelpDesc,
}
}
const (
pathGenerateKeyHelpSyn = `Generate a new private key used for signing.`
pathGenerateKeyHelpDesc = `This endpoint will generate a new key pair of the specified type (internal, exported, or kms).`
)
func (b *backend) pathGenerateKeyHandler(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Since we're planning on updating issuers here, grab the lock so we've
// got a consistent view.
b.issuersLock.Lock()
defer b.issuersLock.Unlock()
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Can not generate keys until migration has completed"), nil
}
keyName, err := getKeyName(ctx, req.Storage, data)
if err != nil { // Fail Immediately if Key Name is in Use, etc...
return nil, err
}
exportPrivateKey := false
var keyBundle certutil.KeyBundle
var actualPrivateKeyType certutil.PrivateKeyType
switch {
case strings.HasSuffix(req.Path, "/exported"):
exportPrivateKey = true
fallthrough
case strings.HasSuffix(req.Path, "/internal"):
keyType := data.Get(keyTypeParam).(string)
keyBits := data.Get(keyBitsParam).(int)
keyBits, _, err := certutil.ValidateDefaultOrValueKeyTypeSignatureLength(keyType, keyBits, 0)
if err != nil {
return logical.ErrorResponse("Validation for key_type, key_bits failed: %s", err.Error()), nil
}
// Internal key generation, stored in storage
keyBundle, err = certutil.CreateKeyBundle(keyType, keyBits, b.GetRandomReader())
if err != nil {
return nil, err
}
actualPrivateKeyType = keyBundle.PrivateKeyType
case strings.HasSuffix(req.Path, "/kms"):
keyId, err := getManagedKeyId(data)
if err != nil {
return nil, err
}
keyBundle, actualPrivateKeyType, err = createKmsKeyBundle(ctx, b, keyId)
if err != nil {
return nil, err
}
default:
return logical.ErrorResponse("Unknown type of key to generate"), nil
}
privateKeyPemString, err := keyBundle.ToPrivateKeyPemString()
if err != nil {
return nil, err
}
key, _, err := importKey(ctx, b, req.Storage, privateKeyPemString, keyName, keyBundle.PrivateKeyType)
if err != nil {
return nil, err
}
responseData := map[string]interface{}{
keyIdParam: key.ID,
keyNameParam: key.Name,
keyTypeParam: string(actualPrivateKeyType),
}
if exportPrivateKey {
responseData["private_key"] = privateKeyPemString
}
return &logical.Response{
Data: responseData,
}, nil
}
func pathImportKey(b *backend) *framework.Path {
return &framework.Path{
Pattern: "keys/import",
Fields: map[string]*framework.FieldSchema{
keyNameParam: {
Type: framework.TypeString,
Description: "Optional name to be used for this key",
},
"pem_bundle": {
Type: framework.TypeString,
Description: `PEM-format, unencrypted secret key`,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathImportKeyHandler,
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},
HelpSynopsis: pathImportKeyHelpSyn,
HelpDescription: pathImportKeyHelpDesc,
}
}
const (
pathImportKeyHelpSyn = `Import the specified key.`
pathImportKeyHelpDesc = `This endpoint allows importing a specified issuer key from a pem bundle.
If name is set, that will be set on the key.`
)
func (b *backend) pathImportKeyHandler(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Since we're planning on updating issuers here, grab the lock so we've
// got a consistent view.
b.issuersLock.Lock()
defer b.issuersLock.Unlock()
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Cannot import keys until migration has completed"), nil
}
keyValueInterface, isOk := data.GetOk("pem_bundle")
if !isOk {
return logical.ErrorResponse("keyValue must be set"), nil
}
keyValue := keyValueInterface.(string)
keyName := data.Get(keyNameParam).(string)
key, existed, err := importKeyFromBytes(ctx, b, req.Storage, keyValue, keyName)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
resp := logical.Response{
Data: map[string]interface{}{
keyIdParam: key.ID,
keyNameParam: key.Name,
keyTypeParam: key.PrivateKeyType,
},
}
if existed {
resp.AddWarning("Key already imported, use key/ endpoint to update name.")
}
return &resp, nil
}