f3911cce66
* Rename path_config -> path_keys_config Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add config/keys to disable upserting Transit would allow anyone with Create permissions on the encryption endpoint to automatically create new encryption keys. This becomes hard to reason about for operators, especially if typos are subtly introduced (e.g., my-key vs my_key) -- there is no way to merge these two keys afterwards. Add the ability to globally disable upserting, so that if the applications using Transit do not need the capability, it can be globally disallowed even under permissive policies. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add documentation on disabling upsert Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add changelog entry Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Update website/content/api-docs/secret/transit.mdx Co-authored-by: tjperry07 <tjperry07@users.noreply.github.com> * Update website/content/api-docs/secret/transit.mdx Co-authored-by: tjperry07 <tjperry07@users.noreply.github.com> Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> Co-authored-by: tjperry07 <tjperry07@users.noreply.github.com>
238 lines
7.1 KiB
Go
238 lines
7.1 KiB
Go
package transit
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/keysutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func (b *backend) pathKeysConfig() *framework.Path {
|
|
return &framework.Path{
|
|
Pattern: "keys/" + framework.GenericNameRegex("name") + "/config",
|
|
Fields: map[string]*framework.FieldSchema{
|
|
"name": {
|
|
Type: framework.TypeString,
|
|
Description: "Name of the key",
|
|
},
|
|
|
|
"min_decryption_version": {
|
|
Type: framework.TypeInt,
|
|
Description: `If set, the minimum version of the key allowed
|
|
to be decrypted. For signing keys, the minimum
|
|
version allowed to be used for verification.`,
|
|
},
|
|
|
|
"min_encryption_version": {
|
|
Type: framework.TypeInt,
|
|
Description: `If set, the minimum version of the key allowed
|
|
to be used for encryption; or for signing keys,
|
|
to be used for signing. If set to zero, only
|
|
the latest version of the key is allowed.`,
|
|
},
|
|
|
|
"deletion_allowed": {
|
|
Type: framework.TypeBool,
|
|
Description: "Whether to allow deletion of the key",
|
|
},
|
|
|
|
"exportable": {
|
|
Type: framework.TypeBool,
|
|
Description: `Enables export of the key. Once set, this cannot be disabled.`,
|
|
},
|
|
|
|
"allow_plaintext_backup": {
|
|
Type: framework.TypeBool,
|
|
Description: `Enables taking a backup of the named key in plaintext format. Once set, this cannot be disabled.`,
|
|
},
|
|
|
|
"auto_rotate_period": {
|
|
Type: framework.TypeDurationSecond,
|
|
Description: `Amount of time the key should live before
|
|
being automatically rotated. A value of 0
|
|
disables automatic rotation for the key.`,
|
|
},
|
|
},
|
|
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.UpdateOperation: b.pathKeysConfigWrite,
|
|
},
|
|
|
|
HelpSynopsis: pathKeysConfigHelpSyn,
|
|
HelpDescription: pathKeysConfigHelpDesc,
|
|
}
|
|
}
|
|
|
|
func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (resp *logical.Response, retErr error) {
|
|
name := d.Get("name").(string)
|
|
|
|
// Check if the policy already exists before we lock everything
|
|
p, _, err := b.GetPolicy(ctx, keysutil.PolicyRequest{
|
|
Storage: req.Storage,
|
|
Name: name,
|
|
}, b.GetRandomReader())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if p == nil {
|
|
return logical.ErrorResponse(
|
|
fmt.Sprintf("no existing key named %s could be found", name)),
|
|
logical.ErrInvalidRequest
|
|
}
|
|
if !b.System().CachingDisabled() {
|
|
p.Lock(true)
|
|
}
|
|
defer p.Unlock()
|
|
|
|
originalMinDecryptionVersion := p.MinDecryptionVersion
|
|
originalMinEncryptionVersion := p.MinEncryptionVersion
|
|
originalDeletionAllowed := p.DeletionAllowed
|
|
originalExportable := p.Exportable
|
|
originalAllowPlaintextBackup := p.AllowPlaintextBackup
|
|
|
|
defer func() {
|
|
if retErr != nil || (resp != nil && resp.IsError()) {
|
|
p.MinDecryptionVersion = originalMinDecryptionVersion
|
|
p.MinEncryptionVersion = originalMinEncryptionVersion
|
|
p.DeletionAllowed = originalDeletionAllowed
|
|
p.Exportable = originalExportable
|
|
p.AllowPlaintextBackup = originalAllowPlaintextBackup
|
|
}
|
|
}()
|
|
|
|
resp = &logical.Response{}
|
|
|
|
persistNeeded := false
|
|
|
|
minDecryptionVersionRaw, ok := d.GetOk("min_decryption_version")
|
|
if ok {
|
|
minDecryptionVersion := minDecryptionVersionRaw.(int)
|
|
|
|
if minDecryptionVersion < 0 {
|
|
return logical.ErrorResponse("min decryption version cannot be negative"), nil
|
|
}
|
|
|
|
if minDecryptionVersion == 0 {
|
|
minDecryptionVersion = 1
|
|
resp.AddWarning("since Vault 0.3, transit key numbering starts at 1; forcing minimum to 1")
|
|
}
|
|
|
|
if minDecryptionVersion != p.MinDecryptionVersion {
|
|
if minDecryptionVersion > p.LatestVersion {
|
|
return logical.ErrorResponse(
|
|
fmt.Sprintf("cannot set min decryption version of %d, latest key version is %d", minDecryptionVersion, p.LatestVersion)), nil
|
|
}
|
|
p.MinDecryptionVersion = minDecryptionVersion
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
minEncryptionVersionRaw, ok := d.GetOk("min_encryption_version")
|
|
if ok {
|
|
minEncryptionVersion := minEncryptionVersionRaw.(int)
|
|
|
|
if minEncryptionVersion < 0 {
|
|
return logical.ErrorResponse("min encryption version cannot be negative"), nil
|
|
}
|
|
|
|
if minEncryptionVersion != p.MinEncryptionVersion {
|
|
if minEncryptionVersion > p.LatestVersion {
|
|
return logical.ErrorResponse(
|
|
fmt.Sprintf("cannot set min encryption version of %d, latest key version is %d", minEncryptionVersion, p.LatestVersion)), nil
|
|
}
|
|
p.MinEncryptionVersion = minEncryptionVersion
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
// Check here to get the final picture after the logic on each
|
|
// individually. MinDecryptionVersion will always be 1 or above.
|
|
if p.MinEncryptionVersion > 0 &&
|
|
p.MinEncryptionVersion < p.MinDecryptionVersion {
|
|
return logical.ErrorResponse(
|
|
fmt.Sprintf("cannot set min encryption/decryption values; min encryption version of %d must be greater than or equal to min decryption version of %d", p.MinEncryptionVersion, p.MinDecryptionVersion)), nil
|
|
}
|
|
|
|
allowDeletionInt, ok := d.GetOk("deletion_allowed")
|
|
if ok {
|
|
allowDeletion := allowDeletionInt.(bool)
|
|
if allowDeletion != p.DeletionAllowed {
|
|
p.DeletionAllowed = allowDeletion
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
// Add this as a guard here before persisting since we now require the min
|
|
// decryption version to start at 1; even if it's not explicitly set here,
|
|
// force the upgrade
|
|
if p.MinDecryptionVersion == 0 {
|
|
p.MinDecryptionVersion = 1
|
|
persistNeeded = true
|
|
}
|
|
|
|
exportableRaw, ok := d.GetOk("exportable")
|
|
if ok {
|
|
exportable := exportableRaw.(bool)
|
|
// Don't unset the already set value
|
|
if exportable && !p.Exportable {
|
|
p.Exportable = exportable
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
allowPlaintextBackupRaw, ok := d.GetOk("allow_plaintext_backup")
|
|
if ok {
|
|
allowPlaintextBackup := allowPlaintextBackupRaw.(bool)
|
|
// Don't unset the already set value
|
|
if allowPlaintextBackup && !p.AllowPlaintextBackup {
|
|
p.AllowPlaintextBackup = allowPlaintextBackup
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
autoRotatePeriodRaw, ok, err := d.GetOkErr("auto_rotate_period")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
autoRotatePeriod := time.Second * time.Duration(autoRotatePeriodRaw.(int))
|
|
// Provided value must be 0 to disable or at least an hour
|
|
if autoRotatePeriod != 0 && autoRotatePeriod < time.Hour {
|
|
return logical.ErrorResponse("auto rotate period must be 0 to disable or at least an hour"), nil
|
|
}
|
|
|
|
if autoRotatePeriod != p.AutoRotatePeriod {
|
|
p.AutoRotatePeriod = autoRotatePeriod
|
|
persistNeeded = true
|
|
}
|
|
}
|
|
|
|
if !persistNeeded {
|
|
return nil, nil
|
|
}
|
|
|
|
switch {
|
|
case p.MinAvailableVersion > p.MinEncryptionVersion:
|
|
return logical.ErrorResponse("min encryption version should not be less than min available version"), nil
|
|
case p.MinAvailableVersion > p.MinDecryptionVersion:
|
|
return logical.ErrorResponse("min decryption version should not be less then min available version"), nil
|
|
}
|
|
|
|
if len(resp.Warnings) == 0 {
|
|
return nil, p.Persist(ctx, req.Storage)
|
|
}
|
|
|
|
return resp, p.Persist(ctx, req.Storage)
|
|
}
|
|
|
|
const pathKeysConfigHelpSyn = `Configure a named encryption key`
|
|
|
|
const pathKeysConfigHelpDesc = `
|
|
This path is used to configure the named key. Currently, this
|
|
supports adjusting the minimum version of the key allowed to
|
|
be used for decryption via the min_decryption_version parameter.
|
|
`
|