/
arn.go
72 lines (64 loc) · 2.36 KB
/
arn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Package connector
// Canonicalize code adopted from
// https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/2a9ee95fecab59fab41a0b646a63227d66113434/pkg/arn/arn.go
// At 2022.06.08 to break dependency on aws-iam-authenticator. At the time of adoption the code hasn't changed
// in over 2 years.
package connector
import (
"fmt"
"strings"
awsarn "github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go/aws/endpoints"
)
// Canonicalize validates IAM resources are appropriate for the authenticator
// and converts STS assumed roles into the IAM role resource.
//
// Supported IAM resources are:
// - AWS account: arn:aws:iam::123456789012:root
// - IAM user: arn:aws:iam::123456789012:user/Bob
// - IAM role: arn:aws:iam::123456789012:role/S3Access
// - IAM Assumed role: arn:aws:sts::123456789012:assumed-role/Accounting-Role/Mary (converted to IAM role)
// - Federated user: arn:aws:sts::123456789012:federated-user/Bob
func Canonicalize(arn string) (string, error) {
parsed, err := awsarn.Parse(arn)
if err != nil {
return "", fmt.Errorf("arn '%s' is invalid: '%v'", arn, err)
}
if err := checkPartition(parsed.Partition); err != nil {
return "", fmt.Errorf("arn '%s' does not have a recognized partition", arn)
}
parts := strings.Split(parsed.Resource, "/")
resource := parts[0]
switch parsed.Service {
case "sts":
switch resource {
case "federated-user":
return arn, nil
case "assumed-role":
if len(parts) < 3 {
return "", fmt.Errorf("assumed-role arn '%s' does not have a role", arn)
}
// IAM ARNs can contain paths, part[0] is resource, parts[len(parts)] is the SessionName.
role := strings.Join(parts[1:len(parts)-1], "/")
return fmt.Sprintf("arn:%s:iam::%s:role/%s", parsed.Partition, parsed.AccountID, role), nil
default:
return "", fmt.Errorf("unrecognized resource %s for service sts", parsed.Resource)
}
case "iam":
switch resource {
case "role", "user", "root":
return arn, nil
default:
return "", fmt.Errorf("unrecognized resource %s for service iam", parsed.Resource)
}
}
return "", fmt.Errorf("service %s in arn %s is not a valid service for identities", parsed.Service, arn)
}
func checkPartition(partition string) error {
for _, p := range endpoints.DefaultPartitions() {
if partition == p.ID() {
return nil
}
}
return fmt.Errorf("partition %s is not recognized", partition)
}