diff --git a/spinifex/arn/iam.go b/spinifex/arn/iam.go index 44a861b89..55b8183de 100644 --- a/spinifex/arn/iam.go +++ b/spinifex/arn/iam.go @@ -23,6 +23,12 @@ func FormatIAMPath(kind IAMResourceType, accountID, resourcePath, name string) s return fmt.Sprintf("arn:aws:iam::%s:%s%s%s", accountID, kind, resourcePath, name) } +// FormatIAMRoot builds the account root ARN, the principal AWS uses for the +// account itself rather than a named IAM resource. +func FormatIAMRoot(accountID string) string { + return fmt.Sprintf("arn:aws:iam::%s:root", accountID) +} + // FormatIAMResource builds an IAM ARN from an already-formed resource // component, such as an OIDC provider issuer host/path. func FormatIAMResource(kind IAMResourceType, accountID, resource string) string { diff --git a/spinifex/arn/iam_test.go b/spinifex/arn/iam_test.go index 29599f553..098efac6d 100644 --- a/spinifex/arn/iam_test.go +++ b/spinifex/arn/iam_test.go @@ -29,6 +29,14 @@ func TestFormatIAMPath(t *testing.T) { } } +func TestFormatIAMRoot(t *testing.T) { + got := arn.FormatIAMRoot("123456789012") + want := "arn:aws:iam::123456789012:root" + if got != want { + t.Fatalf("FormatIAMRoot() = %q, want %q", got, want) + } +} + func TestFormatIAMResource(t *testing.T) { got := arn.FormatIAMResource(arn.IAMOIDCProvider, "123456789012", "issuer.example/id/cluster") want := "arn:aws:iam::123456789012:oidc-provider/issuer.example/id/cluster" diff --git a/spinifex/gateway/ec2/instance/IamInstanceProfileAssociation.go b/spinifex/gateway/ec2/instance/IamInstanceProfileAssociation.go index b09a86263..d0e93ed5d 100644 --- a/spinifex/gateway/ec2/instance/IamInstanceProfileAssociation.go +++ b/spinifex/gateway/ec2/instance/IamInstanceProfileAssociation.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/spinifex/spinifex/arn" "github.com/mulgadc/spinifex/spinifex/awserrors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" spxtypes "github.com/mulgadc/spinifex/spinifex/types" @@ -40,7 +41,7 @@ func resolveAndAuthorizeProfile(spec *ec2.IamInstanceProfileSpecification, iamSv return nil, err } if profile.RoleName != "" && passRoleCheck != nil { - roleARN := fmt.Sprintf("arn:aws:iam::%s:role/%s", profile.AccountID, profile.RoleName) + roleARN := arn.FormatIAMPath(arn.IAMRole, profile.AccountID, "/", profile.RoleName) if err := passRoleCheck(roleARN); err != nil { return nil, err } diff --git a/spinifex/gateway/sts.go b/spinifex/gateway/sts.go index b878615a1..fccaf0db4 100644 --- a/spinifex/gateway/sts.go +++ b/spinifex/gateway/sts.go @@ -2,11 +2,11 @@ package gateway import ( "errors" - "fmt" "log/slog" "net/http" "github.com/aws/aws-sdk-go/service/sts" + spxarn "github.com/mulgadc/spinifex/spinifex/arn" "github.com/mulgadc/spinifex/spinifex/awsec2query" "github.com/mulgadc/spinifex/spinifex/awserrors" gateway_sts "github.com/mulgadc/spinifex/spinifex/gateway/sts" @@ -164,16 +164,16 @@ func buildCallerARN(accountID, identity, principalType, assumedRoleARN string) ( } return assumedRoleARN, nil case principalTypeRoot: - return fmt.Sprintf("arn:aws:iam::%s:root", accountID), nil + return spxarn.FormatIAMRoot(accountID), nil case principalTypeUser: if identity == "root" && accountID == utils.GlobalAccountID { - return fmt.Sprintf("arn:aws:iam::%s:root", accountID), nil + return spxarn.FormatIAMRoot(accountID), nil } if identity == "" { slog.Error("STS_Request: user principal without identity") return "", errors.New(awserrors.ErrorInternalError) } - return fmt.Sprintf("arn:aws:iam::%s:user/%s", accountID, identity), nil + return spxarn.FormatIAMPath(spxarn.IAMUser, accountID, "/", identity), nil default: slog.Error("STS_Request: unknown principal type", "principalType", principalType) return "", errors.New(awserrors.ErrorInternalError) diff --git a/spinifex/handlers/iam/service_impl.go b/spinifex/handlers/iam/service_impl.go index 260073bb2..c1685d27d 100644 --- a/spinifex/handlers/iam/service_impl.go +++ b/spinifex/handlers/iam/service_impl.go @@ -746,7 +746,7 @@ func (s *IAMServiceImpl) SeedBootstrap(data *BootstrapData) error { UserName: "root", UserID: "AIDAAAAAAAAAAAAAAAAA", AccountID: utils.GlobalAccountID, - ARN: fmt.Sprintf("arn:aws:iam::%s:root", utils.GlobalAccountID), + ARN: arn.FormatIAMRoot(utils.GlobalAccountID), Path: "/", CreatedAt: time.Now().UTC().Format(time.RFC3339), AccessKeys: []string{data.AccessKeyID}, diff --git a/spinifex/handlers/sts/presigned_url_verify.go b/spinifex/handlers/sts/presigned_url_verify.go index e94a6e4aa..5df955668 100644 --- a/spinifex/handlers/sts/presigned_url_verify.go +++ b/spinifex/handlers/sts/presigned_url_verify.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/service/iam" "github.com/mulgadc/bluebottle/pkg/sigv4" + "github.com/mulgadc/spinifex/spinifex/arn" "github.com/mulgadc/spinifex/spinifex/awserrors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" ) @@ -153,7 +154,7 @@ func (s *STSServiceImpl) resolvePrincipalForVerify(accessKeyID string) (*Presign userARN := aws.StringValue(userOut.User.Arn) userID := aws.StringValue(userOut.User.UserId) if userARN == "" { - userARN = fmt.Sprintf("arn:aws:iam::%s:user/%s", ak.AccountID, ak.UserName) + userARN = arn.FormatIAMPath(arn.IAMUser, ak.AccountID, "/", ak.UserName) } return &PresignedCallerIdentity{ AccountID: ak.AccountID,