diff --git a/builtin/logical/aws/path_user.go b/builtin/logical/aws/path_user.go index c9b43e97ef68..7c3ea936543e 100644 --- a/builtin/logical/aws/path_user.go +++ b/builtin/logical/aws/path_user.go @@ -33,6 +33,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{ @@ -81,6 +85,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 { @@ -126,7 +131,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 3a4a3f0afa0f..0d79ae1240aa 100644 --- a/builtin/logical/aws/secret_access_keys.go +++ b/builtin/logical/aws/secret_access_keys.go @@ -142,7 +142,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 @@ -166,10 +166,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, } @@ -189,8 +198,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, }) @@ -201,8 +211,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 000000000000..8ff694ff89f7 --- /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 27dba78903f6..7cb5da9ddb3c 100644 --- a/website/content/api-docs/secret/aws.mdx +++ b/website/content/api-docs/secret/aws.mdx @@ -524,6 +524,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, @@ -551,7 +555,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" } } ```