diff --git a/pkg/validate/dynamic/dynamic.go b/pkg/validate/dynamic/dynamic.go index d044c61fd..decc44806 100644 --- a/pkg/validate/dynamic/dynamic.go +++ b/pkg/validate/dynamic/dynamic.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network" + "github.com/Azure/checkaccess-v2-go-sdk/client" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" "github.com/apparentlymart/go-cidr/cidr" @@ -24,7 +25,6 @@ import ( apisubnet "github.com/Azure/ARO-RP/pkg/api/util/subnet" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient" - "github.com/Azure/ARO-RP/pkg/util/azureclient/authz/remotepdp" "github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armauthorization" "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/compute" "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/network" @@ -98,7 +98,7 @@ type dynamic struct { resourceSkusClient compute.ResourceSkusClient spNetworkUsage network.UsageClient loadBalancerBackendAddressPoolsClient network.LoadBalancerBackendAddressPoolsClient - pdpClient remotepdp.RemotePDPClient + pdpClient client.RemotePDPClient } type AuthorizerType string @@ -118,7 +118,7 @@ func NewValidator( appID *string, authorizerType AuthorizerType, cred azcore.TokenCredential, - pdpClient remotepdp.RemotePDPClient, + pdpClient client.RemotePDPClient, ) Dynamic { return &dynamic{ log: log, @@ -435,41 +435,61 @@ func (dv *dynamic) validateActionsByOID(ctx context.Context, r *azure.Resource, defer cancel() c := closure{dv: dv, ctx: ctx, resource: r, actions: actions, oid: oid} + if err := c.checkAccessAuthReqPayloadData(); err != nil { + return err + } return wait.PollImmediateUntil(30*time.Second, c.usingCheckAccessV2, timeoutCtx.Done()) } // closure is the closure used in PollImmediateUntil's ConditionalFunc type closure struct { - dv *dynamic - ctx context.Context - resource *azure.Resource - actions []string - oid *string + dv *dynamic + ctx context.Context + resource *azure.Resource + actions []string + oid *string + subjectAttributes client.SubjectAttributes +} + +func (c *closure) checkAccessAuthReqPayloadData() error { + c.dv.log.Info("Prepare payload for CheckAccessV2 AuthZ") + scope := c.dv.azEnv.ResourceManagerEndpoint + "/.default" + t, err := c.dv.checkAccessSubjectInfoCred.GetToken(c.ctx, policy.TokenRequestOptions{Scopes: []string{scope}}) + if err != nil { + c.dv.log.Error("Unable to get the token from AAD: ", err) + return err + } + + tokenClaims, err := token.ExtractClaims(t.Token) + if err != nil { + c.dv.log.Error("Unable to parse the token oid claim: ", err) + return err + } + c.subjectAttributes.ObjectId = tokenClaims.ObjectId + + if tokenClaims.ClaimNames != nil && len(tokenClaims.Groups) == 0 { + c.subjectAttributes.ClaimName = client.GroupExpansion + } else if tokenClaims.ClaimNames == nil && len(tokenClaims.Groups) > 0 { + c.subjectAttributes.Groups = tokenClaims.Groups + } + + return nil } // usingCheckAccessV2 uses the new RBAC checkAccessV2 API func (c closure) usingCheckAccessV2() (bool, error) { c.dv.log.Info("validateActions with CheckAccessV2") - // reusing oid during retries - if c.oid == nil { - scope := c.dv.azEnv.ResourceManagerEndpoint + "/.default" - t, err := c.dv.checkAccessSubjectInfoCred.GetToken(c.ctx, policy.TokenRequestOptions{Scopes: []string{scope}}) - if err != nil { - c.dv.log.Error("Unable to get the token from AAD: ", err) + // reusing oid and groups/claimNames during retries + if c.subjectAttributes.ObjectId == "" || + (c.subjectAttributes.ClaimName == "" && len(c.subjectAttributes.Groups) == 0) { + if err := c.checkAccessAuthReqPayloadData(); err != nil { return false, err } - - oid, err := token.GetObjectId(t.Token) - if err != nil { - c.dv.log.Error("Unable to parse the token oid claim: ", err) - return false, err - } - c.oid = &oid } - authReq := createAuthorizationRequest(*c.oid, c.resource.String(), c.actions...) + authReq := c.dv.pdpClient.CreateAuthorizationRequest(c.resource.String(), c.actions, c.subjectAttributes) results, err := c.dv.pdpClient.CheckAccess(c.ctx, authReq) if err != nil { c.dv.log.Error("Unexpected error when calling CheckAccessV2: ", err) @@ -489,39 +509,19 @@ func (c closure) usingCheckAccessV2() (bool, error) { _, ok := actionsToFind[result.ActionId] if ok { delete(actionsToFind, result.ActionId) - if result.AccessDecision != remotepdp.Allowed { + if result.AccessDecision != client.Allowed { return false, nil } } } if len(actionsToFind) > 0 { - c.dv.log.Infof("The result didn't include permissions %v for object ID: %s", actionsToFind, *c.oid) + c.dv.log.Infof("The result didn't include permissions %v for object ID: %s", actionsToFind, c.subjectAttributes.ObjectId) return false, nil } return true, nil } -func createAuthorizationRequest(subject, resourceId string, actions ...string) remotepdp.AuthorizationRequest { - actionInfos := []remotepdp.ActionInfo{} - for _, action := range actions { - actionInfos = append(actionInfos, remotepdp.ActionInfo{Id: action}) - } - - return remotepdp.AuthorizationRequest{ - Subject: remotepdp.SubjectInfo{ - Attributes: remotepdp.SubjectAttributes{ - ObjectId: subject, - ClaimName: remotepdp.GroupExpansion, // always do group expansion - }, - }, - Actions: actionInfos, - Resource: remotepdp.ResourceInfo{ - Id: resourceId, - }, - } -} - func (dv *dynamic) validateCIDRRanges(ctx context.Context, subnets []Subnet, additionalCIDRs ...string) error { dv.log.Print("ValidateCIDRRanges") diff --git a/pkg/validate/openshiftcluster_validatedynamic.go b/pkg/validate/openshiftcluster_validatedynamic.go index fa9156110..1983e9fd0 100644 --- a/pkg/validate/openshiftcluster_validatedynamic.go +++ b/pkg/validate/openshiftcluster_validatedynamic.go @@ -11,6 +11,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/checkaccess-v2-go-sdk/client" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" "github.com/form3tech-oss/jwt-go" @@ -18,7 +19,7 @@ import ( "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/util/azureclient/authz/remotepdp" + "github.com/Azure/ARO-RP/pkg/util/azureclient" "github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armauthorization" "github.com/Azure/ARO-RP/pkg/util/platformworkloadidentity" "github.com/Azure/ARO-RP/pkg/validate/dynamic" @@ -127,11 +128,20 @@ func (dv *openShiftClusterDynamicValidator) Dynamic(ctx context.Context) error { } aroEnv := dv.env.Environment() - pdpClient := remotepdp.NewRemotePDPClient( + clientOptions := &azcore.ClientOptions{ + Transport: &http.Client{ + Transport: azureclient.NewCustomRoundTripper(http.DefaultTransport), + }, + } + pdpClient, err := client.NewRemotePDPClient( fmt.Sprintf(aroEnv.Endpoint, dv.env.Location()), aroEnv.OAuthScope, fpClientCred, + clientOptions, ) + if err != nil { + return err + } scopes := []string{dv.env.Environment().ResourceManagerScope} var spDynamic dynamic.Dynamic