approle: add ttl to the secret ID generation response (#10826)

* approle: add ttl to the secret ID generation response

* approle: move TTL derivation into helper func

* changelog: add changelog entry

* docs: update approle docs and api-docs pages
This commit is contained in:
Calvin Leung Huang 2021-02-03 16:32:16 -08:00 committed by GitHub
parent 8351a10154
commit 298b9cde2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 13 deletions

View file

@ -2358,12 +2358,15 @@ func (b *backend) handleRoleSecretIDCommon(ctx context.Context, req *logical.Req
return nil, errwrap.Wrapf("failed to store secret_id: {{err}}", err)
}
return &logical.Response{
resp := &logical.Response{
Data: map[string]interface{}{
"secret_id": secretID,
"secret_id_accessor": secretIDStorage.SecretIDAccessor,
"secret_id_ttl": int64(b.deriveSecretIDTTL(secretIDStorage.SecretIDTTL).Seconds()),
},
}, nil
}
return resp, nil
}
func (b *backend) roleIDLock(roleID string) *locksutil.LockEntry {

View file

@ -1931,3 +1931,92 @@ func TestAppRole_TokenutilUpgrade(t *testing.T) {
})
}
}
func TestAppRole_SecretID_WithTTL(t *testing.T) {
tests := []struct {
name string
roleName string
ttl int64
sysTTLCap bool
}{
{
"zero ttl",
"role-zero-ttl",
0,
false,
},
{
"custom ttl",
"role-custom-ttl",
60,
false,
},
{
"system ttl capped",
"role-sys-ttl-cap",
700000000,
true,
},
}
b, storage := createBackendWithStorage(t)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create role
roleData := map[string]interface{}{
"policies": "default",
"secret_id_ttl": tt.ttl,
}
roleReq := &logical.Request{
Operation: logical.CreateOperation,
Path: "role/" + tt.roleName,
Storage: storage,
Data: roleData,
}
resp, err := b.HandleRequest(context.Background(), roleReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
// Generate secret ID
secretIDReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "role/" + tt.roleName + "/secret-id",
Storage: storage,
}
resp, err = b.HandleRequest(context.Background(), secretIDReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
// Extract the "ttl" value from the response data if it exists
ttlRaw, okTTL := resp.Data["secret_id_ttl"]
if !okTTL {
t.Fatalf("expected TTL value in response")
}
var (
respTTL int64
ok bool
)
respTTL, ok = ttlRaw.(int64)
if !ok {
t.Fatalf("expected ttl to be an integer, got: %s", err)
}
// Verify secret ID response for different cases
switch {
case tt.sysTTLCap:
if respTTL != int64(b.System().MaxLeaseTTL().Seconds()) {
t.Fatalf("expected TTL value to be system's max lease TTL, got: %d", respTTL)
}
default:
if respTTL != tt.ttl {
t.Fatalf("expected TTL value to be %d, got: %d", tt.ttl, respTTL)
}
}
})
}
}

View file

@ -238,15 +238,8 @@ func (b *backend) registerSecretIDEntry(ctx context.Context, s logical.Storage,
secretEntry.CreationTime = currentTime
secretEntry.LastUpdatedTime = currentTime
// If SecretIDTTL is not specified or if it crosses the backend mount's limit,
// cap the expiration to backend's max. Otherwise, use it to determine the
// expiration time.
if secretEntry.SecretIDTTL < time.Duration(0) || secretEntry.SecretIDTTL > b.System().MaxLeaseTTL() {
secretEntry.ExpirationTime = currentTime.Add(b.System().MaxLeaseTTL())
} else if secretEntry.SecretIDTTL != time.Duration(0) {
// Set the ExpirationTime only if SecretIDTTL was set. SecretIDs should not
// expire by default.
secretEntry.ExpirationTime = currentTime.Add(secretEntry.SecretIDTTL)
if ttl := b.deriveSecretIDTTL(secretEntry.SecretIDTTL); ttl != time.Duration(0) {
secretEntry.ExpirationTime = currentTime.Add(ttl)
}
// Before storing the SecretID, store its accessor.
@ -261,6 +254,20 @@ func (b *backend) registerSecretIDEntry(ctx context.Context, s logical.Storage,
return secretEntry, nil
}
// deriveSecretIDTTL determines the secret ID TTL to use based on the system's
// max lease TTL.
//
// If SecretIDTTL is negative or if it crosses the backend mount's limit,
// return to backend's max lease TTL. Otherwise, return the provided secretIDTTL
// value.
func (b *backend) deriveSecretIDTTL(secretIDTTL time.Duration) time.Duration {
if secretIDTTL < time.Duration(0) || secretIDTTL > b.System().MaxLeaseTTL() {
return b.System().MaxLeaseTTL()
}
return secretIDTTL
}
// secretIDAccessorEntry is used to read the storage entry that maps an
// accessor to a secret_id.
func (b *backend) secretIDAccessorEntry(ctx context.Context, s logical.Storage, secretIDAccessor, roleSecretIDPrefix string) (*secretIDAccessorStorageEntry, error) {

3
changelog/10826.txt Normal file
View file

@ -0,0 +1,3 @@
```changelog:changes
auth/approle: Secrets ID generation endpoint now returns `secret_id_ttl` as part of its response.
```

View file

@ -301,7 +301,8 @@ $ curl \
"wrap_info": null,
"data": {
"secret_id_accessor": "84896a0c-1347-aa90-a4f6-aca8b7558780",
"secret_id": "841771dc-11c9-bbc7-bcac-6a3945a69cd9"
"secret_id": "841771dc-11c9-bbc7-bcac-6a3945a69cd9",
"secret_id_ttl": 600
},
"lease_duration": 0,
"renewable": false,

View file

@ -113,6 +113,7 @@ documentation.
$ vault write -f auth/approle/role/my-role/secret-id
secret_id 6a174c20-f6de-a53c-74d2-6018fcceff64
secret_id_accessor c454f7e5-996e-7230-6074-6ef26b7bcf86
secret_id_ttl 10m
```
### Via the API
@ -170,7 +171,8 @@ documentation.
{
"data": {
"secret_id_accessor": "45946873-1d96-a9d4-678c-9229f74386a5",
"secret_id": "37b74931-c4cd-d49a-9246-ccc62d682a25"
"secret_id": "37b74931-c4cd-d49a-9246-ccc62d682a25",
"secret_id_ttl": 600
}
}
```