Transit UX improvements: show key policy, configs on write (#20652)
* Respond with cache size on config write Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Respond with key policy on write This includes creating a key, but also trimming or rotating an existing key. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add changelog entry Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Correctly handle locking around policy formatting Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Validate that responses are non-empty Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> --------- Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
parent
f219d7a77c
commit
f9fdac0345
|
@ -1071,9 +1071,7 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if resp != nil {
|
require.NotNil(t, resp, "expected populated request")
|
||||||
t.Fatal("expected nil response")
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := keysutil.LoadPolicy(context.Background(), storage, path.Join("policy", "testkey"))
|
p, err := keysutil.LoadPolicy(context.Background(), storage, path.Join("policy", "testkey"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1559,9 +1557,7 @@ func TestBadInput(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if resp != nil {
|
require.NotNil(t, resp, "expected populated request")
|
||||||
t.Fatal("expected nil response")
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Path = "decrypt/test"
|
req.Path = "decrypt/test"
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
|
@ -1650,9 +1646,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if resp != nil {
|
require.NotNil(t, resp, "expected populated request")
|
||||||
t.Fatal("expected nil response")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a key with an auto rotate value one day in the future
|
// Write a key with an auto rotate value one day in the future
|
||||||
req = &logical.Request{
|
req = &logical.Request{
|
||||||
|
@ -1667,9 +1661,7 @@ func TestTransit_AutoRotateKeys(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if resp != nil {
|
require.NotNil(t, resp, "expected populated request")
|
||||||
t.Fatal("expected nil response")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the rotation check and ensure none of the keys have rotated
|
// Run the rotation check and ensure none of the keys have rotated
|
||||||
b.checkAutoRotateAfter = time.Now()
|
b.checkAutoRotateAfter = time.Now()
|
||||||
|
|
|
@ -84,7 +84,11 @@ func (b *backend) pathCacheConfigWrite(ctx context.Context, req *logical.Request
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"size": cacheSize,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type configCache struct {
|
type configCache struct {
|
||||||
|
|
|
@ -257,12 +257,14 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
|
||||||
p.Unlock()
|
p.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &logical.Response{}
|
resp, err := b.formatKeyPolicy(p, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !upserted {
|
if !upserted {
|
||||||
resp.AddWarning(fmt.Sprintf("key %s already existed", name))
|
resp.AddWarning(fmt.Sprintf("key %s already existed", name))
|
||||||
}
|
}
|
||||||
|
return resp, nil
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Built-in helper type for returning asymmetric keys
|
// Built-in helper type for returning asymmetric keys
|
||||||
|
@ -290,6 +292,19 @@ func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *f
|
||||||
}
|
}
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
|
contextRaw := d.Get("context").(string)
|
||||||
|
var context []byte
|
||||||
|
if len(contextRaw) != 0 {
|
||||||
|
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.formatKeyPolicy(p, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) formatKeyPolicy(p *keysutil.Policy, context []byte) (*logical.Response, error) {
|
||||||
// Return the response
|
// Return the response
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
|
@ -346,15 +361,6 @@ func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contextRaw := d.Get("context").(string)
|
|
||||||
var context []byte
|
|
||||||
if len(contextRaw) != 0 {
|
|
||||||
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
|
||||||
if err != nil {
|
|
||||||
return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch p.Type {
|
switch p.Type {
|
||||||
case keysutil.KeyType_AES128_GCM96, keysutil.KeyType_AES256_GCM96, keysutil.KeyType_ChaCha20_Poly1305:
|
case keysutil.KeyType_AES128_GCM96, keysutil.KeyType_AES256_GCM96, keysutil.KeyType_ChaCha20_Poly1305:
|
||||||
retKeys := map[string]int64{}
|
retKeys := map[string]int64{}
|
||||||
|
|
|
@ -97,6 +97,8 @@ func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request,
|
||||||
}
|
}
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
|
var warning string
|
||||||
|
|
||||||
originalMinDecryptionVersion := p.MinDecryptionVersion
|
originalMinDecryptionVersion := p.MinDecryptionVersion
|
||||||
originalMinEncryptionVersion := p.MinEncryptionVersion
|
originalMinEncryptionVersion := p.MinEncryptionVersion
|
||||||
originalDeletionAllowed := p.DeletionAllowed
|
originalDeletionAllowed := p.DeletionAllowed
|
||||||
|
@ -113,8 +115,6 @@ func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request,
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
resp = &logical.Response{}
|
|
||||||
|
|
||||||
persistNeeded := false
|
persistNeeded := false
|
||||||
|
|
||||||
minDecryptionVersionRaw, ok := d.GetOk("min_decryption_version")
|
minDecryptionVersionRaw, ok := d.GetOk("min_decryption_version")
|
||||||
|
@ -127,7 +127,7 @@ func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request,
|
||||||
|
|
||||||
if minDecryptionVersion == 0 {
|
if minDecryptionVersion == 0 {
|
||||||
minDecryptionVersion = 1
|
minDecryptionVersion = 1
|
||||||
resp.AddWarning("since Vault 0.3, transit key numbering starts at 1; forcing minimum to 1")
|
warning = "since Vault 0.3, transit key numbering starts at 1; forcing minimum to 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
if minDecryptionVersion != p.MinDecryptionVersion {
|
if minDecryptionVersion != p.MinDecryptionVersion {
|
||||||
|
@ -221,7 +221,14 @@ func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !persistNeeded {
|
if !persistNeeded {
|
||||||
return nil, nil
|
resp, err := b.formatKeyPolicy(p, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if warning != "" {
|
||||||
|
resp.AddWarning(warning)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -231,11 +238,18 @@ func (b *backend) pathKeysConfigWrite(ctx context.Context, req *logical.Request,
|
||||||
return logical.ErrorResponse("min decryption version should not be less then min available version"), nil
|
return logical.ErrorResponse("min decryption version should not be less then min available version"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp.Warnings) == 0 {
|
if err := p.Persist(ctx, req.Storage); err != nil {
|
||||||
return nil, p.Persist(ctx, req.Storage)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, p.Persist(ctx, req.Storage)
|
resp, err = b.formatKeyPolicy(p, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if warning != "" {
|
||||||
|
resp.AddWarning(warning)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathKeysConfigHelpSyn = `Configure a named encryption key`
|
const pathKeysConfigHelpSyn = `Configure a named encryption key`
|
||||||
|
|
|
@ -64,6 +64,7 @@ func (b *backend) pathRotateWrite(ctx context.Context, req *logical.Request, d *
|
||||||
if !b.System().CachingDisabled() {
|
if !b.System().CachingDisabled() {
|
||||||
p.Lock(true)
|
p.Lock(true)
|
||||||
}
|
}
|
||||||
|
defer p.Unlock()
|
||||||
|
|
||||||
if p.Type == keysutil.KeyType_MANAGED_KEY {
|
if p.Type == keysutil.KeyType_MANAGED_KEY {
|
||||||
var keyId string
|
var keyId string
|
||||||
|
@ -78,8 +79,11 @@ func (b *backend) pathRotateWrite(ctx context.Context, req *logical.Request, d *
|
||||||
err = p.Rotate(ctx, req.Storage, b.GetRandomReader())
|
err = p.Rotate(ctx, req.Storage, b.GetRandomReader())
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Unlock()
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.formatKeyPolicy(p, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathRotateHelpSyn = `Rotate named encryption key`
|
const pathRotateHelpSyn = `Rotate named encryption key`
|
||||||
|
|
|
@ -100,7 +100,7 @@ func (b *backend) pathTrimUpdate() framework.OperationFunc {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return b.formatKeyPolicy(p, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
secrets/transit: Respond to writes with updated key policy, cache configuration.
|
||||||
|
```
|
Loading…
Reference in New Issue