Skip to content

Commit 874fb10

Browse files
feet : SREP-1313 Update isolation workflow to enforce policy Arn from backplane-api assume-role-sequence endpoint
1 parent acf0385 commit 874fb10

File tree

4 files changed

+452
-8
lines changed

4 files changed

+452
-8
lines changed

cmd/ocm-backplane/cloud/common.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/aws/aws-sdk-go-v2/aws/arn"
1919
"github.com/aws/aws-sdk-go-v2/credentials"
2020
"github.com/aws/aws-sdk-go-v2/service/sts"
21+
"github.com/aws/aws-sdk-go-v2/service/sts/types"
2122
ocmsdk "github.com/openshift-online/ocm-sdk-go"
2223
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
2324
BackplaneApi "github.com/openshift/backplane-api/pkg/client"
@@ -211,6 +212,8 @@ func (cfg *QueryConfig) getCloudCredentialsFromBackplaneAPI(ocmToken string) (bp
211212
type assumeChainResponse struct {
212213
AssumptionSequence []namedRoleArn `json:"assumptionSequence"`
213214
CustomerRoleSessionName string `json:"customerRoleSessionName"`
215+
// SessionPolicyArn is the ARN of the session policy
216+
SessionPolicyArn string `json:"sessionPolicyArn"`
214217
}
215218

216219
type namedRoleArn struct {
@@ -319,6 +322,24 @@ func (cfg *QueryConfig) getIsolatedCredentials(ocmToken string) (aws.Credentials
319322
} else {
320323
roleArnSession.RoleSessionName = email
321324
}
325+
// Default to no policy ARNs
326+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{}
327+
if namedRoleArnEntry.Name == CustomerRoleArnName {
328+
roleArnSession.IsCustomerRole = true
329+
// Add the session policy ARN for selected roles
330+
if roleChainResponse.SessionPolicyArn != "" {
331+
logger.Debugf("Adding session policy ARN for role %s: %s", namedRoleArnEntry.Name, roleChainResponse.SessionPolicyArn)
332+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{
333+
{
334+
Arn: aws.String(roleChainResponse.SessionPolicyArn),
335+
},
336+
}
337+
}
338+
} else {
339+
roleArnSession.IsCustomerRole = false
340+
}
341+
roleArnSession.Name = namedRoleArnEntry.Name
342+
322343
assumeRoleArnSessionSequence = append(assumeRoleArnSessionSequence, roleArnSession)
323344
}
324345

@@ -471,7 +492,7 @@ func isIsolatedBackplaneAccess(cluster *cmv1.Cluster, ocmConnection *ocmsdk.Conn
471492
if strings.HasSuffix(baseDomain, "devshiftusgov.com") || strings.HasSuffix(baseDomain, "openshiftusgov.com") {
472493
return false, nil
473494
}
474-
495+
475496
if cluster.Hypershift().Enabled() {
476497
return true, nil
477498
}

cmd/ocm-backplane/cloud/common_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/aws/aws-sdk-go-v2/credentials"
1616
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
1717
"github.com/aws/aws-sdk-go-v2/service/sts"
18+
"github.com/aws/aws-sdk-go-v2/service/sts/types"
1819
"github.com/golang/mock/gomock"
1920
. "github.com/onsi/ginkgo/v2"
2021
. "github.com/onsi/gomega"
@@ -523,3 +524,167 @@ var _ = Describe("isIsolatedBackplaneAccess", func() {
523524
})
524525
})
525526
})
527+
528+
var _ = Describe("PolicyARNs Integration", func() {
529+
var (
530+
testSessionPolicyArn string
531+
)
532+
533+
BeforeEach(func() {
534+
testSessionPolicyArn = "arn:aws:iam::123456789012:policy/TestSessionPolicy"
535+
})
536+
537+
Context("when creating RoleArnSession with SessionPolicyArn", func() {
538+
It("should set PolicyARNs for customer roles", func() {
539+
// Test the logic from common.go lines 327-337
540+
roleChainResponse := assumeChainResponse{
541+
AssumptionSequence: []namedRoleArn{
542+
{
543+
Name: CustomerRoleArnName,
544+
Arn: "arn:aws:iam::123456789012:role/customer-role",
545+
},
546+
},
547+
CustomerRoleSessionName: "customer-session",
548+
SessionPolicyArn: testSessionPolicyArn,
549+
}
550+
551+
// Simulate the logic from getIsolatedCredentials
552+
assumeRoleArnSessionSequence := make([]awsutil.RoleArnSession, 0, len(roleChainResponse.AssumptionSequence))
553+
for _, namedRoleArnEntry := range roleChainResponse.AssumptionSequence {
554+
roleArnSession := awsutil.RoleArnSession{RoleArn: namedRoleArnEntry.Arn}
555+
if namedRoleArnEntry.Name == CustomerRoleArnName || namedRoleArnEntry.Name == OrgRoleArnName {
556+
roleArnSession.RoleSessionName = roleChainResponse.CustomerRoleSessionName
557+
} else {
558+
roleArnSession.RoleSessionName = "test@example.com"
559+
}
560+
561+
// Default to no policy ARNs
562+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{}
563+
if namedRoleArnEntry.Name == CustomerRoleArnName {
564+
roleArnSession.IsCustomerRole = true
565+
// Add the session policy ARN for selected roles
566+
if roleChainResponse.SessionPolicyArn != "" {
567+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{
568+
{
569+
Arn: aws.String(roleChainResponse.SessionPolicyArn),
570+
},
571+
}
572+
}
573+
} else {
574+
roleArnSession.IsCustomerRole = false
575+
}
576+
roleArnSession.Name = namedRoleArnEntry.Name
577+
578+
assumeRoleArnSessionSequence = append(assumeRoleArnSessionSequence, roleArnSession)
579+
}
580+
581+
Expect(len(assumeRoleArnSessionSequence)).To(Equal(1))
582+
583+
customerRole := assumeRoleArnSessionSequence[0]
584+
Expect(customerRole.IsCustomerRole).To(BeTrue())
585+
Expect(customerRole.Name).To(Equal(CustomerRoleArnName))
586+
Expect(len(customerRole.PolicyARNs)).To(Equal(1))
587+
Expect(*customerRole.PolicyARNs[0].Arn).To(Equal(testSessionPolicyArn))
588+
})
589+
590+
It("should not set PolicyARNs for non-customer roles", func() {
591+
roleChainResponse := assumeChainResponse{
592+
AssumptionSequence: []namedRoleArn{
593+
{
594+
Name: "Support-Role-Arn",
595+
Arn: "arn:aws:iam::123456789012:role/support-role",
596+
},
597+
},
598+
CustomerRoleSessionName: "customer-session",
599+
SessionPolicyArn: testSessionPolicyArn,
600+
}
601+
602+
// Simulate the logic from getIsolatedCredentials
603+
assumeRoleArnSessionSequence := make([]awsutil.RoleArnSession, 0, len(roleChainResponse.AssumptionSequence))
604+
for _, namedRoleArnEntry := range roleChainResponse.AssumptionSequence {
605+
roleArnSession := awsutil.RoleArnSession{RoleArn: namedRoleArnEntry.Arn}
606+
if namedRoleArnEntry.Name == CustomerRoleArnName || namedRoleArnEntry.Name == OrgRoleArnName {
607+
roleArnSession.RoleSessionName = roleChainResponse.CustomerRoleSessionName
608+
} else {
609+
roleArnSession.RoleSessionName = "test@example.com"
610+
}
611+
612+
// Default to no policy ARNs
613+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{}
614+
if namedRoleArnEntry.Name == CustomerRoleArnName {
615+
roleArnSession.IsCustomerRole = true
616+
// Add the session policy ARN for selected roles
617+
if roleChainResponse.SessionPolicyArn != "" {
618+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{
619+
{
620+
Arn: aws.String(roleChainResponse.SessionPolicyArn),
621+
},
622+
}
623+
}
624+
} else {
625+
roleArnSession.IsCustomerRole = false
626+
}
627+
roleArnSession.Name = namedRoleArnEntry.Name
628+
629+
assumeRoleArnSessionSequence = append(assumeRoleArnSessionSequence, roleArnSession)
630+
}
631+
632+
Expect(len(assumeRoleArnSessionSequence)).To(Equal(1))
633+
634+
supportRole := assumeRoleArnSessionSequence[0]
635+
Expect(supportRole.IsCustomerRole).To(BeFalse())
636+
Expect(supportRole.Name).To(Equal("Support-Role-Arn"))
637+
Expect(len(supportRole.PolicyARNs)).To(Equal(0))
638+
})
639+
640+
It("should handle empty SessionPolicyArn for customer roles", func() {
641+
roleChainResponse := assumeChainResponse{
642+
AssumptionSequence: []namedRoleArn{
643+
{
644+
Name: CustomerRoleArnName,
645+
Arn: "arn:aws:iam::123456789012:role/customer-role",
646+
},
647+
},
648+
CustomerRoleSessionName: "customer-session",
649+
SessionPolicyArn: "", // Empty session policy ARN
650+
}
651+
652+
// Simulate the logic from getIsolatedCredentials
653+
assumeRoleArnSessionSequence := make([]awsutil.RoleArnSession, 0, len(roleChainResponse.AssumptionSequence))
654+
for _, namedRoleArnEntry := range roleChainResponse.AssumptionSequence {
655+
roleArnSession := awsutil.RoleArnSession{RoleArn: namedRoleArnEntry.Arn}
656+
if namedRoleArnEntry.Name == CustomerRoleArnName || namedRoleArnEntry.Name == OrgRoleArnName {
657+
roleArnSession.RoleSessionName = roleChainResponse.CustomerRoleSessionName
658+
} else {
659+
roleArnSession.RoleSessionName = "test@example.com"
660+
}
661+
662+
// Default to no policy ARNs
663+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{}
664+
if namedRoleArnEntry.Name == CustomerRoleArnName {
665+
roleArnSession.IsCustomerRole = true
666+
// Add the session policy ARN for selected roles
667+
if roleChainResponse.SessionPolicyArn != "" {
668+
roleArnSession.PolicyARNs = []types.PolicyDescriptorType{
669+
{
670+
Arn: aws.String(roleChainResponse.SessionPolicyArn),
671+
},
672+
}
673+
}
674+
} else {
675+
roleArnSession.IsCustomerRole = false
676+
}
677+
roleArnSession.Name = namedRoleArnEntry.Name
678+
679+
assumeRoleArnSessionSequence = append(assumeRoleArnSessionSequence, roleArnSession)
680+
}
681+
682+
Expect(len(assumeRoleArnSessionSequence)).To(Equal(1))
683+
684+
customerRole := assumeRoleArnSessionSequence[0]
685+
Expect(customerRole.IsCustomerRole).To(BeTrue())
686+
Expect(customerRole.Name).To(Equal(CustomerRoleArnName))
687+
Expect(len(customerRole.PolicyARNs)).To(Equal(0))
688+
})
689+
})
690+
})

pkg/awsutil/sts.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/aws/aws-sdk-go-v2/credentials"
1818
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
1919
"github.com/aws/aws-sdk-go-v2/service/sts"
20+
"github.com/aws/aws-sdk-go-v2/service/sts/types"
2021
"github.com/openshift/backplane-cli/pkg/utils"
2122
)
2223

@@ -97,12 +98,16 @@ func AssumeRole(
9798
roleSessionName string,
9899
roleArn string,
99100
inlinePolicy *PolicyDocument,
101+
policyARNs []types.PolicyDescriptorType,
100102
) (aws.Credentials, error) {
101103
assumeRoleProvider := stscreds.NewAssumeRoleProvider(stsClient, roleArn, func(options *stscreds.AssumeRoleOptions) {
102104
options.RoleSessionName = roleSessionName
103105
if inlinePolicy != nil {
104106
options.Policy = aws.String(inlinePolicy.String())
105107
}
108+
if len(policyARNs) > 0 {
109+
options.PolicyARNs = policyARNs
110+
}
106111
})
107112
result, err := assumeRoleProvider.Retrieve(context.TODO())
108113
if err != nil {
@@ -123,8 +128,11 @@ var DefaultSTSClientProviderFunc STSClientProviderFunc = func(optnFns ...func(op
123128
}
124129

125130
type RoleArnSession struct {
131+
Name string
126132
RoleSessionName string
127133
RoleArn string
134+
IsCustomerRole bool
135+
PolicyARNs []types.PolicyDescriptorType
128136
}
129137

130138
func AssumeRoleSequence(
@@ -142,8 +150,14 @@ func AssumeRoleSequence(
142150
var lastCredentials aws.Credentials
143151

144152
for i, roleArnSession := range roleArnSessionSequence {
145-
logger.Debug("Assuming role in sequence: ", roleArnSession.RoleArn, " ", roleArnSession.RoleSessionName)
146-
result, err := AssumeRole(nextClient, roleArnSession.RoleSessionName, roleArnSession.RoleArn, inlinePolicy)
153+
154+
logger.Debugf("Assuming role in sequence name:%s role:%s sessionName:%s isCustomerRole:%t",
155+
roleArnSession.Name,
156+
roleArnSession.RoleArn,
157+
roleArnSession.RoleSessionName,
158+
roleArnSession.IsCustomerRole,
159+
)
160+
result, err := AssumeRole(nextClient, roleArnSession.RoleSessionName, roleArnSession.RoleArn, inlinePolicy, roleArnSession.PolicyARNs)
147161
retryCount := 0
148162
for err != nil {
149163
// IAM policy updates can take a few seconds to resolve, and the sts.Client in AWS' Go SDK doesn't refresh itself on retries.
@@ -156,7 +170,10 @@ func AssumeRoleSequence(
156170
return aws.Credentials{}, fmt.Errorf("failed to create client with credentials for role %v: %w", roleArnSession.RoleArn, err)
157171
}
158172

159-
result, err = AssumeRole(nextClient, roleArnSession.RoleSessionName, roleArnSession.RoleArn, inlinePolicy)
173+
result, err = AssumeRole(nextClient, roleArnSession.RoleSessionName, roleArnSession.RoleArn, inlinePolicy, roleArnSession.PolicyARNs)
174+
if err != nil {
175+
logger.Debugf("failed to create client with credentials for role %s: name:%s %v", roleArnSession.RoleArn, roleArnSession.Name, err)
176+
}
160177
retryCount++
161178
} else {
162179
return aws.Credentials{}, fmt.Errorf("failed to assume role %v: %w", roleArnSession.RoleArn, err)
@@ -167,7 +184,7 @@ func AssumeRoleSequence(
167184
if i < len(roleArnSessionSequence)-1 {
168185
nextClient, err = createAssumeRoleSequenceClient(stsClientProviderFunc, lastCredentials, proxyURL)
169186
if err != nil {
170-
return aws.Credentials{}, fmt.Errorf("failed to create client with credentials for role %v: %w", roleArnSession.RoleArn, err)
187+
return aws.Credentials{}, fmt.Errorf("failed to create client with credentials for role %v: name:%v %w", roleArnSession.RoleArn, roleArnSession.RoleSessionName, err)
171188
}
172189
}
173190
}

0 commit comments

Comments
 (0)