Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to partial azure identity scopes #529

Merged
merged 13 commits into from
Dec 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
)

var KeyVaultNameRegex = regexp.MustCompile(`^/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft.KeyVault/vaults/([^/]+)$`)
var StorageAccountRegex = regexp.MustCompile(`providers/Microsoft.Storage/storageAccounts/([^/]+)`)

type Agent struct {
*azureagent.Agent
Expand All @@ -34,17 +35,32 @@ func (a *Agent) IntentType() otterizev2alpha1.IntentType {
return otterizev2alpha1.IntentTypeAzure
}

func (a *Agent) getIntentScope(intent otterizev2alpha1.Target) (string, error) {
func (a *Agent) getIntentScope(ctx context.Context, intent otterizev2alpha1.Target) (string, error) {
name := intent.GetTargetServerName()
if !strings.HasPrefix(name, "/") {
return "", errors.Errorf("expected intent name to start with /, got %s", name)
}

// If the scope is already a full scope, validate and return it
if strings.HasPrefix(name, "/subscriptions/") {
// the name is already a full scope
err := a.ValidateScope(ctx, name)
if err != nil {
return "", errors.Wrap(err)
}

return name, nil
}

// If the scope is a partial storage account scope, find the full scope and return it
if match := StorageAccountRegex.FindStringSubmatch(name); len(match) > 1 {
storageAccountScope := fmt.Sprintf("/providers/Microsoft.Storage/storageAccounts/%s", match[1])
fullScope, err := a.GetFullStorageResourceScope(ctx, storageAccountScope, name)
if err != nil {
return "", errors.Wrap(err)
}
return fullScope, nil
}

if strings.HasPrefix(name, "/resourceGroups/") {
// append the subscription ID to the scope
fullScope := fmt.Sprintf("/subscriptions/%s%s", a.Conf.SubscriptionID, name)
Expand Down Expand Up @@ -111,7 +127,7 @@ func (a *Agent) ensureRoleAssignmentsForIntents(ctx context.Context, userAssigne

var expectedScopes []string
for _, intent := range intents {
scope, err := a.getIntentScope(intent)
scope, err := a.getIntentScope(ctx, intent)
if err != nil {
return errors.Wrap(err)
}
Expand Down Expand Up @@ -242,7 +258,7 @@ func (a *Agent) ensureKeyVaultPermissionsForIntents(ctx context.Context, userAss
var expectedIntentsKeyVaults []string

for _, intent := range intents {
scope, err := a.getIntentScope(intent)
scope, err := a.getIntentScope(ctx, intent)
if err != nil {
return errors.Wrap(err)
}
Expand Down Expand Up @@ -342,7 +358,7 @@ func (a *Agent) ensureCustomRolesForIntents(ctx context.Context, userAssignedIde

var expectedScopes []string
for _, intent := range intents {
scope, err := a.getIntentScope(intent)
scope, err := a.getIntentScope(ctx, intent)
if err != nil {
return errors.Wrap(err)
}
Expand All @@ -366,12 +382,6 @@ func (a *Agent) ensureCustomRoleForIntent(ctx context.Context, userAssignedIdent
actions := intent.Azure.Actions
dataActions := intent.Azure.DataActions

// Validate that the scope exists before creating the custom role
err := a.ValidateScope(ctx, scope)
if err != nil {
return errors.Wrap(err)
}

customRoleName := a.GenerateCustomRoleName(userAssignedIdentity, scope)
role, found := a.FindCustomRoleByName(ctx, scope, customRoleName)
if found {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package azurepolicyagent

import (
"context"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault"
Expand Down Expand Up @@ -43,6 +44,7 @@ type AzureAgentPoliciesCustomRolesSuite struct {
mockRoleAssignmentsClient *mock_azureagent.MockAzureARMAuthorizationRoleAssignmentsClient
mockVaultsClient *mock_azureagent.MockAzureARMKeyVaultVaultsClient

subscriptionToResourceClient map[string]azureagent.AzureARMResourcesClient
subscriptionToRoleAssignmentsClient map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient

agent *Agent
Expand Down Expand Up @@ -135,6 +137,9 @@ func (s *AzureAgentPoliciesCustomRolesSuite) SetupTest() {
s.mockRoleAssignmentsClient = mock_azureagent.NewMockAzureARMAuthorizationRoleAssignmentsClient(controller)
s.mockVaultsClient = mock_azureagent.NewMockAzureARMKeyVaultVaultsClient(controller)

s.subscriptionToResourceClient = make(map[string]azureagent.AzureARMResourcesClient)
s.subscriptionToResourceClient[testSubscriptionID] = s.mockResourcesClient

s.subscriptionToRoleAssignmentsClient = make(map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient)
s.subscriptionToRoleAssignmentsClient[testSubscriptionID] = s.mockRoleAssignmentsClient

Expand All @@ -158,6 +163,7 @@ func (s *AzureAgentPoliciesCustomRolesSuite) SetupTest() {
s.mockRoleDefinitionsClient,
s.mockRoleAssignmentsClient,
s.mockVaultsClient,
s.subscriptionToResourceClient,
s.subscriptionToRoleAssignmentsClient,
),
sync.Mutex{},
Expand Down Expand Up @@ -236,7 +242,7 @@ var azureCustomRoleTestCases = []AzureCustomRoleTestCase{

func (s *AzureAgentPoliciesCustomRolesSuite) TestAddRolePolicyFromIntents_CustomRoles() {
for _, testCase := range azureCustomRoleTestCases {
targetScope := "/providers/Microsoft.Storage/storageAccounts/test/blobServices/default/containers/container"
targetScope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/test/blobServices/default/containers/container", testSubscriptionID, testResourceGroup)

s.Run(testCase.Name, func() {
intents := []otterizev2alpha1.Target{
Expand Down Expand Up @@ -267,10 +273,11 @@ func (s *AzureAgentPoliciesCustomRolesSuite) TestAddRolePolicyFromIntents_Custom
s.expectCreateRoleAssignmentReturnsEmpty()
}

s.expectGetByIDReturnsResource(targetScope)

// Make sure the custom role is created
var customRoleDefinition armauthorization.RoleDefinition
if testCase.UpdateExpected {
s.expectGetByIDReturnsResource(targetScope)
s.expectCreateOrUpdateRoleDefinitionWriteRoleDefinition(&customRoleDefinition)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type AzureAgentIdentitiesSuite struct {
mockRoleAssignmentsClient *mock_azureagent.MockAzureARMAuthorizationRoleAssignmentsClient
mockVaultsClient *mock_azureagent.MockAzureARMKeyVaultVaultsClient

subscriptionToResourceClient map[string]azureagent.AzureARMResourcesClient
subscriptionToRoleAssignmentsClient map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient

agent *Agent
Expand All @@ -47,6 +48,9 @@ func (s *AzureAgentIdentitiesSuite) SetupTest() {
s.mockRoleAssignmentsClient = mock_azureagent.NewMockAzureARMAuthorizationRoleAssignmentsClient(controller)
s.mockVaultsClient = mock_azureagent.NewMockAzureARMKeyVaultVaultsClient(controller)

s.subscriptionToResourceClient = make(map[string]azureagent.AzureARMResourcesClient)
s.subscriptionToResourceClient[testSubscriptionID] = s.mockResourcesClient

s.subscriptionToRoleAssignmentsClient = make(map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient)
s.subscriptionToRoleAssignmentsClient[testSubscriptionID] = s.mockRoleAssignmentsClient

Expand All @@ -70,6 +74,7 @@ func (s *AzureAgentIdentitiesSuite) SetupTest() {
s.mockRoleDefinitionsClient,
s.mockRoleAssignmentsClient,
s.mockVaultsClient,
s.subscriptionToResourceClient,
s.subscriptionToRoleAssignmentsClient,
),
sync.Mutex{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
"github.com/google/uuid"
otterizev2alpha1 "github.com/otterize/intents-operator/src/operator/api/v2alpha1"
Expand Down Expand Up @@ -33,6 +34,7 @@ type AzureAgentPoliciesKeyVaultSuite struct {
mockRoleAssignmentsClient *mock_azureagent.MockAzureARMAuthorizationRoleAssignmentsClient
mockVaultsClient *mock_azureagent.MockAzureARMKeyVaultVaultsClient

subscriptionToResourceClient map[string]azureagent.AzureARMResourcesClient
subscriptionToRoleAssignmentsClient map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient

agent *Agent
Expand All @@ -51,6 +53,9 @@ func (s *AzureAgentPoliciesKeyVaultSuite) SetupTest() {
s.mockRoleAssignmentsClient = mock_azureagent.NewMockAzureARMAuthorizationRoleAssignmentsClient(controller)
s.mockVaultsClient = mock_azureagent.NewMockAzureARMKeyVaultVaultsClient(controller)

s.subscriptionToResourceClient = make(map[string]azureagent.AzureARMResourcesClient)
s.subscriptionToResourceClient[testSubscriptionID] = s.mockResourcesClient

s.subscriptionToRoleAssignmentsClient = make(map[string]azureagent.AzureARMAuthorizationRoleAssignmentsClient)
s.subscriptionToRoleAssignmentsClient[testSubscriptionID] = s.mockRoleAssignmentsClient

Expand All @@ -74,13 +79,22 @@ func (s *AzureAgentPoliciesKeyVaultSuite) SetupTest() {
s.mockRoleDefinitionsClient,
s.mockRoleAssignmentsClient,
s.mockVaultsClient,
s.subscriptionToResourceClient,
s.subscriptionToRoleAssignmentsClient,
),
sync.Mutex{},
sync.Mutex{},
}
}

func (s *AzureAgentPoliciesKeyVaultSuite) expectGetByIDReturnsResource(scope string) {
s.mockResourcesClient.EXPECT().GetByID(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(armresources.ClientGetByIDResponse{
GenericResource: armresources.GenericResource{
ID: &scope,
},
}, nil)
}

func (s *AzureAgentPoliciesKeyVaultSuite) expectGetUserAssignedIdentityReturnsClientID(clientId string) {
userAssignedIndentityName := s.agent.GenerateUserAssignedIdentityName(testNamespace, testIntentsServiceName)
s.mockUserAssignedIdentitiesClient.EXPECT().Get(gomock.Any(), testResourceGroup, userAssignedIndentityName, nil).Return(
Expand Down Expand Up @@ -265,6 +279,11 @@ func (s *AzureAgentPoliciesKeyVaultSuite) TestAddRolePolicyFromIntents_AzureKeyV
s.expectUpdateKeyVaultAccessPolicyWritesPolicy(testKeyVaultName, testCase.UpdateKind, &updatedPolicy)
}

// The scope should be validated when passing valid intent:
if intents[0].Azure.KeyVaultPolicy != nil {
s.expectGetByIDReturnsResource(scope)
}

// Act
err := s.agent.AddRolePolicyFromIntents(context.Background(), testNamespace, testAccountName, testIntentsServiceName, intents, corev1.Pod{})

Expand Down
Loading
Loading