Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions cmd/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,14 @@ func Command() *cobra.Command { //nolint:cyclop,funlen
region = "us-east-1"
}

federatePolicy := resolvePolicyAlias(flags.federatePolicy)
partition, consoleDomain, federationURL, ok := credentials.ResolveRegionPartition(region) //nolint:varnamelen
if !ok {
return fmt.Errorf("could not determine partition for region %s", region)
}

// Resolve the IAM policy ARN that will be included along with the
// GetFederationToken request, if a request is made.
federatePolicy := resolvePolicyAlias(flags.federatePolicy, partition)

// If the named profile was configured with user credentials
// (opposed to a role), then the user must be federated before an
Expand All @@ -128,13 +135,13 @@ func Command() *cobra.Command { //nolint:cyclop,funlen

// Resolve the given location alias into a redirect url to a
// service in the AWS Console.
location, ok := resolveLocationAlias(flags.location, region)
location, ok := resolveLocationAlias(flags.location, consoleDomain, region)
if !ok {
return fmt.Errorf("could not resolve location %q", flags.location)
}

// Generate a login URL for the AWS Console.
url, err := console.GenerateLoginURL(creds, flags.duration, location, flags.userAgent)
url, err := console.GenerateLoginURL(creds, federationURL, flags.duration, location, flags.userAgent)
if err != nil {
// There is a very specific failure case where if you attempt
// to generate a Console login URL for an IAM Role, which
Expand All @@ -152,7 +159,7 @@ func Command() *cobra.Command { //nolint:cyclop,funlen
// | 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)
url, err = console.GenerateLoginURL(creds, federationURL, 0, location, flags.userAgent)
if err != nil {
return err
}
Expand Down
72 changes: 42 additions & 30 deletions cmd/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,34 @@ import "strings"
// Console. Used for quickly redirecting the user to the desired service after
// logging in.
var locations = map[string]string{ //nolint:gochecknoglobals
"account": "https://console.aws.amazon.com/billing/home#/account",
"billing": "https://console.aws.amazon.com/billing/home",
"console": "https://{{.region}}.console.aws.amazon.com/console/home?region={{.region}}",
"ec2": "https://{{.region}}.console.aws.amazon.com/ec2/home?region={{.region}}#Home:",
"ecr": "https://{{.region}}.console.aws.amazon.com/ecr/repositories?region={{.region}}",
"eks": "https://{{.region}}.console.aws.amazon.com/eks/home?region={{.region}}#/clusters",
"groups": "https://console.aws.amazon.com/iamv2/home#/groups",
"home": "https://{{.region}}.console.aws.amazon.com/console/home?region={{.region}}",
"iam": "https://console.aws.amazon.com/iamv2/home#/home",
"kms": "https://{{.region}}.console.aws.amazon.com/kms/home?region={{.region}}#/kms/keys",
"org": "https://console.aws.amazon.com/organizations/v2/home/dashboard",
"policies": "https://console.aws.amazon.com/iamv2/home#/policies",
"r53": "https://console.aws.amazon.com/route53/v2/hostedzones#",
"rds": "https://{{.region}}.console.aws.amazon.com/rds/home?region={{.region}}#databases:",
"roles": "https://console.aws.amazon.com/iamv2/home#/roles",
"s3": "https://s3.console.aws.amazon.com/s3/buckets?region={{.region}}",
"support": "https://support.console.aws.amazon.com/support/home",
"users": "https://console.aws.amazon.com/iamv2/home#/users",
"vpn": "https://{{.region}}.console.aws.amazon.com/vpc/home?region={{.region}}#ClientVPNEndpoints:",
"account": "https://{region}.{console}/billing/home?region={region}#/account",
"billing": "https://{region}.{console}/costmanagement/home?region={region}#/home",
"cloudfront": "https://{region}.{console}/cloudfront/v4/home?region={region}#/distributions",
"cloudtrail": "https://{region}.{console}/cloudtrailv2/home?region={region}#/dashboard",
"cloudwatch": "https://{region}.{console}/cloudwatch/home?region={region}#home:",
"console": "https://{region}.{console}/console/home?region={region}",
"ec2": "https://{region}.{console}/ec2/home?region={region}#Instances:",
"ecr": "https://{region}.{console}/ecr/private-registry/repositories?region={region}",
"ecs": "https://{region}.{console}/ecs/v2/clusters?region={region}",
"eip": "https://{region}.{console}/vpcconsole/home?region={region}#Addresses:",
"eks": "https://{region}.{console}/eks/clusters?region={region}",
"groups": "https://{region}.{console}/iam/home?region={region}#/groups",
"home": "https://{region}.{console}/console/home?region={region}",
"iam": "https://{region}.{console}/iam/home?region={region}#/home",
"kms": "https://{region}.{console}/kms/home?region={region}#/kms/home",
"org": "https://{region}.{console}/organizations/v2/home?region={region}",
"policies": "https://{region}.{console}/iam/home?region={region}#/policies",
"r53": "https://{region}.{console}/route53/v2/hostedzones?region={region}",
"rds": "https://{region}.{console}/rds/home?region={region}#databases:",
"roles": "https://{region}.{console}/iam/home?region={region}#/roles",
"s3": "https://{region}.{console}/s3/buckets?region={region}",
"support": "https://support.{console}/support/home?region={region}#/case/history",
"users": "https://{region}.{console}/iam/home?region={region}#/users",
"vpc": "https://{region}.{console}/vpcconsole/home?region={region}#vpcs:",
"vpn": "https://{region}.{console}/vpcconsole/home?region={region}#ClientVPNEndpoints:",
}

func resolveLocationAlias(alias, region string) (string, bool) {
func resolveLocationAlias(alias, consoleDomain, region string) (string, bool) {
var template string

if strings.HasPrefix(alias, "https://") {
Expand All @@ -46,24 +52,30 @@ func resolveLocationAlias(alias, region string) (string, bool) {
return "", false
}

// Replace all region placeholders.
return strings.ReplaceAll(template, "{{.region}}", region), true
// Replace all the placeholders.
return strings.NewReplacer(
"{console}", consoleDomain,
"{region}", region,
).Replace(template), true
}

// policies is a list of aliases that can be resolved to IAM policy ARNs. Used
// for attaching a policy to a federated user session.
var policies = map[string]string{ //nolint:gochecknoglobals
"admin": "arn:aws:iam::aws:policy/AdministratorAccess",
"all": "arn:aws:iam::aws:policy/AdministratorAccess",
"billing": "arn:aws:iam::aws:policy/job-function/Billing",
"readonly": "arn:aws:iam::aws:policy/ReadOnlyAccess",
"ro": "arn:aws:iam::aws:policy/ReadOnlyAccess",
"admin": "arn:{partition}:iam::aws:policy/AdministratorAccess",
"all": "arn:{partition}:iam::aws:policy/AdministratorAccess",
"billing": "arn:{partition}:iam::aws:policy/job-function/Billing",
"readonly": "arn:{partition}:iam::aws:policy/ReadOnlyAccess",
"ro": "arn:{partition}:iam::aws:policy/ReadOnlyAccess",
}

func resolvePolicyAlias(alias string) string {
func resolvePolicyAlias(alias, partition string) string {
template := alias

if result, found := policies[alias]; found {
return result
// Resolve the alias into a URL.
template = result
}

return alias
return strings.ReplaceAll(template, "{partition}", partition)
}
5 changes: 1 addition & 4 deletions console/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ import (
// GenerateLoginURL takes the given sts.Credentials and generates a url.URL
// that can be used to login to the AWS Console.
// See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html.
func GenerateLoginURL(creds *aws.Credentials, duration time.Duration, location, userAgent string) (*url.URL, error) {
// federationURL is the url used for AWS federation actions.
const federationURL = "https://signin.aws.amazon.com/federation"

func GenerateLoginURL(creds *aws.Credentials, federationURL string, duration time.Duration, location, userAgent string) (*url.URL, error) {
// timeout is a hardcoded 15 second window for HTTP requests to complete.
const timeout = 15 * time.Second

Expand Down
43 changes: 43 additions & 0 deletions credentials/partition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Josh Komoroske. All rights reserved.
// Use of this source code is governed by the MIT license,
// a copy of which can be found in the LICENSE.txt file.
// SPDX-License-Identifier: MIT

package credentials

import (
"github.com/aws/aws-sdk-go-v2/service/sts"
)

var partitionURLs = map[string]struct {
consoleDomain string
federationURL string
}{
"aws": {
consoleDomain: "console.aws.amazon.com",
federationURL: "https://signin.aws.amazon.com/federation",
},
"aws-cn": {
// This partition has not been tested.
consoleDomain: "console.amazonaws.cn",
federationURL: "https://signin.amazonaws.cn/federation",
},
"aws-us-gov": {
consoleDomain: "console.amazonaws-us-gov.com",
federationURL: "https://signin.amazonaws-us-gov.com/federation",
},
}

// ResolveRegionPartition uses the given AWS region to determine the corresponding AWS partition, Console URL, and federation URL.
func ResolveRegionPartition(region string) (string, string, string, bool) {
partition := "aws"
if endpoint, err := sts.NewDefaultEndpointResolver().ResolveEndpoint(region, sts.EndpointResolverOptions{}); err == nil {
partition = endpoint.PartitionID
}

if urls, ok := partitionURLs[partition]; ok {
return partition, urls.consoleDomain, urls.federationURL, true
}

return "", "", "", false
}