diff --git a/cmd/command.go b/cmd/command.go index 0fcf9ad..62e1068 100644 --- a/cmd/command.go +++ b/cmd/command.go @@ -126,7 +126,26 @@ func Command() *cobra.Command { //nolint:cyclop // Generate a login URL for the AWS Console. url, err := console.GenerateLoginURL(creds, flags.duration, location, flags.userAgent) if err != nil { - return err + // There is a very specific failure case where if you attempt + // to generate a Console login URL for an IAM Role, which + // itself was assumed from another IAM Role (referred to as + // "role chaining"), and *also* attempt to include a + // SessionDuration HTTP parameter, then the call will fail. + // + // Since you cannot (programmatically) determine if the + // incoming credentials were the product of role chaining and + // act accordingly, the only remediation is to retry the + // Console login URL generation without the duration input. + // + // This edge-case behavior is only documented in this note: + // | Do not use the SessionDuration HTTP parameter when you get + // | temporary credentials through role chaining. The operation + // | will fail. + // See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html + url, err = console.GenerateLoginURL(creds, 0, location, flags.userAgent) + if err != nil { + return err + } } switch { diff --git a/credentials/credentials.go b/credentials/credentials.go index 83bf88e..9534f8c 100644 --- a/credentials/credentials.go +++ b/credentials/credentials.go @@ -130,19 +130,22 @@ func FederateUser(creds *sts.Credentials, name, policy string, duration time.Dur return nil, err } + input := sts.GetFederationTokenInput{ + Name: aws.String(name), + PolicyArns: []*sts.PolicyDescriptorType{{ + Arn: aws.String(policy), + }}, + } + // The minimum value for the DurationSeconds parameter is 15 minutes. // See https://docs.aws.amazon.com/STS/latest/APIReference/API_GetFederationToken.html#API_GetFederationToken_RequestParameters. const minDuration = 15 * time.Minute - if duration < minDuration { + if duration != 0 && duration < minDuration { duration = minDuration } - input := sts.GetFederationTokenInput{ - DurationSeconds: aws.Int64(int64(duration.Seconds())), - Name: aws.String(name), - PolicyArns: []*sts.PolicyDescriptorType{{ - Arn: aws.String(policy), - }}, + if duration != 0 { + input.DurationSeconds = aws.Int64(int64(duration.Seconds())) } // Configure client.