From d02a20bd2b64feba137911fecf5e7d8ae096aa0a Mon Sep 17 00:00:00 2001 From: Ricardo Cardenas Date: Mon, 17 May 2021 11:03:09 -0700 Subject: [PATCH] feat(aws): add ability to provide a role session name when generating STS credentials (#11345) * feat(aws): add ability to provide a sessionName to sts credentials Co-authored-by: Brad Vernon Co-authored-by: Jim Kalafut Co-authored-by: Tom Proctor --- builtin/logical/aws/path_user.go | 7 ++++++- builtin/logical/aws/secret_access_keys.go | 22 ++++++++++++++++------ changelog/11345.txt | 3 +++ website/content/api-docs/secret/aws.mdx | 7 ++++++- 4 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 changelog/11345.txt diff --git a/builtin/logical/aws/path_user.go b/builtin/logical/aws/path_user.go index e98ac8e60..05c8730c3 100644 --- a/builtin/logical/aws/path_user.go +++ b/builtin/logical/aws/path_user.go @@ -32,6 +32,10 @@ func pathUser(b *backend) *framework.Path { Description: "Lifetime of the returned credentials in seconds", Default: 3600, }, + "role_session_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Session name to use when assuming role. Max chars: 64", + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -80,6 +84,7 @@ func (b *backend) pathCredsRead(ctx context.Context, req *logical.Request, d *fr } roleArn := d.Get("role_arn").(string) + roleSessionName := d.Get("role_session_name").(string) var credentialType string switch { @@ -125,7 +130,7 @@ func (b *backend) pathCredsRead(ctx context.Context, req *logical.Request, d *fr case !strutil.StrListContains(role.RoleArns, roleArn): return logical.ErrorResponse(fmt.Sprintf("role_arn %q not in allowed role arns for Vault role %q", roleArn, roleName)), nil } - return b.assumeRole(ctx, req.Storage, req.DisplayName, roleName, roleArn, role.PolicyDocument, role.PolicyArns, role.IAMGroups, ttl) + return b.assumeRole(ctx, req.Storage, req.DisplayName, roleName, roleArn, role.PolicyDocument, role.PolicyArns, role.IAMGroups, ttl, roleSessionName) case federationTokenCred: return b.getFederationToken(ctx, req.Storage, req.DisplayName, roleName, role.PolicyDocument, role.PolicyArns, role.IAMGroups, ttl) default: diff --git a/builtin/logical/aws/secret_access_keys.go b/builtin/logical/aws/secret_access_keys.go index 6e6ee9a6a..327eafd9b 100644 --- a/builtin/logical/aws/secret_access_keys.go +++ b/builtin/logical/aws/secret_access_keys.go @@ -141,7 +141,7 @@ func (b *backend) getFederationToken(ctx context.Context, s logical.Storage, func (b *backend) assumeRole(ctx context.Context, s logical.Storage, displayName, roleName, roleArn, policy string, policyARNs []string, - iamGroups []string, lifeTimeInSeconds int64) (*logical.Response, error) { + iamGroups []string, lifeTimeInSeconds int64, roleSessionName string) (*logical.Response, error) { // grab any IAM group policies associated with the vault role, both inline // and managed @@ -165,10 +165,19 @@ func (b *backend) assumeRole(ctx context.Context, s logical.Storage, return logical.ErrorResponse(err.Error()), nil } - username, usernameWarning := genUsername(displayName, roleName, "iam_user") + roleSessionNameWarning := "" + if roleSessionName == "" { + roleSessionName, roleSessionNameWarning = genUsername(displayName, roleName, "iam_user") + } else { + roleSessionName = normalizeDisplayName(roleSessionName) + if len(roleSessionName) > 64 { + roleSessionName = roleSessionName[0:64] + roleSessionNameWarning = "the role session name was truncated to 64 characters to fit within IAM session name length limits" + } + } assumeRoleInput := &sts.AssumeRoleInput{ - RoleSessionName: aws.String(username), + RoleSessionName: aws.String(roleSessionName), RoleArn: aws.String(roleArn), DurationSeconds: &lifeTimeInSeconds, } @@ -187,8 +196,9 @@ func (b *backend) assumeRole(ctx context.Context, s logical.Storage, "access_key": *tokenResp.Credentials.AccessKeyId, "secret_key": *tokenResp.Credentials.SecretAccessKey, "security_token": *tokenResp.Credentials.SessionToken, + "arn": *tokenResp.AssumedRoleUser.Arn, }, map[string]interface{}{ - "username": username, + "username": roleSessionName, "policy": roleArn, "is_sts": true, }) @@ -199,8 +209,8 @@ func (b *backend) assumeRole(ctx context.Context, s logical.Storage, // STS are purposefully short-lived and aren't renewable resp.Secret.Renewable = false - if usernameWarning != "" { - resp.AddWarning(usernameWarning) + if roleSessionNameWarning != "" { + resp.AddWarning(roleSessionNameWarning) } return resp, nil diff --git a/changelog/11345.txt b/changelog/11345.txt new file mode 100644 index 000000000..8ff694ff8 --- /dev/null +++ b/changelog/11345.txt @@ -0,0 +1,3 @@ +```release-note:improvement +secrets/aws: add ability to provide a role session name when generating STS credentials +``` diff --git a/website/content/api-docs/secret/aws.mdx b/website/content/api-docs/secret/aws.mdx index 890356f39..64976776d 100644 --- a/website/content/api-docs/secret/aws.mdx +++ b/website/content/api-docs/secret/aws.mdx @@ -523,6 +523,10 @@ credentials retrieved through `/aws/creds` must be of the `iam_user` type. the Vault role is `assumed_role`. Must match one of the allowed role ARNs in the Vault role. Optional if the Vault role only allows a single AWS role ARN; required otherwise. +- `role_session_name` `(string)` - The role session name to attach to the assumed role ARN. + `role_session_name` is limited to 64 characters; if exceeded, the `role_session_name` in the + assumed role ARN will be truncated to 64 characters. If `role_session_name` is not provided, + then it will be generated dynamically by default. - `ttl` `(string: "3600s")` – Specifies the TTL for the use of the STS token. This is specified as a string with a duration suffix. Valid only when `credential_type` is `assumed_role` or `federation_token`. When not specified, @@ -550,7 +554,8 @@ $ curl \ "data": { "access_key": "AKIA...", "secret_key": "xlCs...", - "security_token": null + "security_token": null, + "arn": "arn:aws:sts::123456789012:assumed-role/DeveloperRole/some-user-supplied-role-session-name" } } ```