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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug Report: Lacking permissions in default policy Deploy-Private-DNS-Zones #794

Closed
steph409 opened this issue Aug 21, 2023 · 8 comments 路 Fixed by #919
Closed

Bug Report: Lacking permissions in default policy Deploy-Private-DNS-Zones #794

steph409 opened this issue Aug 21, 2023 · 8 comments 路 Fixed by #919
Assignees

Comments

@steph409
Copy link
Contributor

steph409 commented Aug 21, 2023

Community Note

  • Please vote on this issue by adding a 馃憤 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Versions

terraform: 1.5.1

azure provider: 3.3.5

module: 1.4.0

Description

Describe the bug

Policy: Deploy-Private-DNS-Zones
There is a build-in policy on the corp-landing-zones management group. It creates an A record in the private DNS zone in the connectivity subscription.

In order to create the A record, the managed identity seems to need two permissions:

  • on the corp-landing-zone, it has network contributor and Private DNS Zone Contributor, and this is sufficient to modify the private endpoints within this subscription.
  • on the connectivity-platform-subscription, it has no permissions. It seems to need the permission Microsoft.Network/privateDnsZones/join/action to add the A record to the existing private DNS zone. This permission seems to be missing and I do not understand where in the code the permissions to the system assigned identities are managed.

Steps to Reproduce

I use the following sample configuration: vwan with custom parameters

where I modified the following lines:

spoke_virtual_network_resource_ids        = [ "/subscriptions/b742bc62-0000-0000-8c93-01d342f48f63/resourceGroups/demo-aks2-stephi/providers/Microsoft.Network/virtualNetworks/aks-network"]

enable_private_dns_zone_virtual_network_link_on_spokes = true 

Additional context

For testing, I have an azure container registry with a private endpoint, lacking a private dns entry. The policy successfully shows, that the a record is missing in the central DNS. When I trigger a remediation task, I get the following error message:

The subscription id starting with b74 is the corp-landing zone, the one with 99d is the connectivity one. The client-id ebd.. is the enterprise application for the policy Deploy-Private-DNS-Zones.

{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. 
Please list deployment operations for details. 
Please see https://aka.ms/arm-deployment-operations for usage details.",
"details":[{"code":"LinkedAuthorizationFailed","message":
"The client 'ebd474c7-0000-0000-0000-fee25352e235' with object id 'ebd474c7-0000-0000-0000-fee25352e235' 
has permission to perform action 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups/write' on scope
 '/subscriptions/b742bc62-0000-0000-0000-01d342f48f63/resourcegroups/demo-aks2-stephi/providers/Microsoft.Network/privateEndpoints/containerregistryaksstephi-endpoint/privateDnsZoneGroups/deployedByPolicy'; 
 however, it does not have permission to perform action 'Microsoft.Network/privateDnsZones/join/action'
  on the linked scope(s)
   '/subscriptions/99d115a2-0000-0000-0000-38b2feb4bcb7/resourceGroups/slcorp-dns/providers/Microsoft.Network/privateDnsZones/privatelink.azurecr.io' 
   or the linked scope(s) are invalid."}]}

I validated the scopes and the scope and this is exactly where the private dns zone is.

Happy to give further details if this helps.

@steph409
Copy link
Contributor Author

I digged a little deeper and found three role assignments looking at my terraform state:

tf state list | grep 'Deploy-Private'

I got three role assignments similar to this one:

module.caf-enterprise-scale.module.role_assignments_for_policy["/providers/Microsoft.Management/managementGroups/slcorp-corp/providers/Microsoft.Authorization/policyAssignments/Deploy-Private-DNS-Zones"].azurerm_role_assignment.for_policy["/providers/Microsoft.Management/managementGroups/slcorp-corp/providers/Microsoft.Authorization/roleAssignments/31cc3591-0000-565f-ba5f-8adaf847da5d"]

By inspecting the state, I found that those three role assignments are:

    role_definition_name = "Private DNS Zone Contributor"
    scope                = "/providers/Microsoft.Management/managementGroups/slcorp-corp"

    role_definition_name = "Network Contributor"
    scope                = "/providers/Microsoft.Management/managementGroups/slcorp-corp"

    role_definition_name = "Contributor"
    scope                = "/providers/Microsoft.Management/managementGroups/slcorp-corp"

If I understand correctly how it is supposed to work, the first one should not be granted on the slcorp-corp management group, but rather on the slcorp-connectivity as depicted in this blog entry.

I am having difficulty to find where the role assignments is defined, I will look further if I have time!

@JefferyMitchell
Copy link
Collaborator

@steph409 Can you confirm you are using module 1.4 and not module 4.2?

@steph409
Copy link
Contributor Author

sorry it's 4.2.0!

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 馃憢 Needs attention from the maintainers and removed Needs: Author Feedback labels Aug 25, 2023
@hlokensgard
Copy link

hlokensgard commented Sep 11, 2023

I have solved this issue by the following code for the latest release of the module.
@steph409

# The policy "Deploy-Private-DNS-Zones" needs permission on the connectivity subscription to add the dns records for private dns zones.
# First retrieving the identity from the module
# Then giving the role "Private DNS Zone Contributor" on the resource group that contain the dns records to the identity
locals {
  # Since the module has a pre-defined naming standard we can predict the name the resource group for the connectivity resources have two possible ways to get the prefix. The first is from the root_id that is inherited from the enterprise scale module.
  connectivity_prefix =  var.root_id
  dns_resource_group  = "/subscriptions/${var.subscription_id_connectivity}/resourceGroups/${local.connectivity_prefix}-dns"
  # Name of the policy assignment
  policy_deploy_private_dns_zones_name = "Deploy-Private-DNS-Zones"
  # Name of the management group that the policy assignment is assigned to
  policy_management_scope_name = "${var.root_id}-corp"
  # Creating the id of the policy assignment
  policy_assignment_id = "/providers/Microsoft.Management/managementGroups/${local.policy_management_scope_name}/providers/Microsoft.Authorization/policyAssignments/${local.policy_deploy_private_dns_zones_name}"
  # Getting the identity of the policy assignment
  policy_assignment_principal_id = module.enterprise_scale.azurerm_management_group_policy_assignment.enterprise_scale[local.policy_assignment_id].identity[0].principal_id 
}

resource "azurerm_role_assignment" "policy_assignment_private_dns_zone_contributor" {
  provider             = azurerm.connectivity
  scope                = local.dns_resource_group
  role_definition_name = "Private DNS Zone Contributor"
  principal_id         = local.policy_assignment_principal_id
}

@matt-FFFFFF matt-FFFFFF added Needs: Author Feedback and removed Needs: Attention 馃憢 Needs attention from the maintainers labels Feb 15, 2024
@qaiserali
Copy link

I'm also getting a similar error when creating a private AKS cluster
'Service principal or user-assigned identity must be given permission to read and write to custom private dns zone'.

Can this be solved using the module instead of implementing the workaround?

@andrewdmay
Copy link

Not sure why this has been set back to Needs Author Feedback, because the original submitter confirmed they were using 4.2.0 not 1.4.0.

This is still an issue with the 5.x releases, and I'm using the workaround detailed by hlokensgard.

To answer steph409's question about how where the role assignments come from, I believe for the policy set they are the set of the roleDefinitionIds from the individual policies, and they are automatically assigned at the same scope as the policy assignment. However, because in this case the Private DNS Zones are in the Connectivity subscription which isn't within the policy assignment scope, the policies don't have permission to create the DNS records which is why they fail.

I think there's an ability to specify a custom role assignment to the policy assignment, but I haven't looked into it in detail.

@steph409
Copy link
Contributor Author

Hi,
I have been working with the workaround for quite some time as well.

@matt-FFFFFF do you know how this is solved in bicep/portal landing zone? they must have the same issue, would be interesting to know if they have solved this by adding the permission in the module.

What we need in the module is something like this:

# if there is a connectivity management group, and there is a policy assignment for private dns zones,
# # then we need this additional permission for the policy to function correctly
resource "azurerm_role_assignment" "fix_dns_automation" {
  for_each             = { for idx, v in azurerm_management_group_policy_assignment.enterprise_scale : idx => v if strcontains(idx, "Deploy-Private-DNS") }
  role_definition_name = "Private DNS Zone Contributor"
  scope                = "/providers/Microsoft.Management/managementGroups/${var.root_id}-connectivity"
  principal_id         = each.value.identity[0].principal_id
}

alternatively, we could set the scope of the role assignment to the connectivity subscription if one is provided:

resource "azurerm_role_assignment" "fix_dns_automation" {
  for_each = { for idx, v in azurerm_management_group_policy_assignment.enterprise_scale : idx => v
  if strcontains(idx, "Deploy-Private-DNS") && var.subscription_id_connectivity != "" }
  role_definition_name = "Private DNS Zone Contributor"
  scope                = "/subscriptions/${var.subscription_id_connectivity}"
  principal_id         = each.value.identity[0].principal_id
}

happy to discuss it!

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 馃憢 Needs attention from the maintainers and removed Needs: Author Feedback labels Feb 26, 2024
@matt-FFFFFF
Copy link
Member

@steph409

It's best practice to perform the role assignment at the resource level. This is what the portal does when you manually assign a policy. It uses the assignPermissions metadata property on the policy assignment parameter.

Bicep has a list of well role assignments and they create the role assignment manually.

In the new version of the ALZ Terraform module/provider we are able to detect this property and perform the role assignments programmatically.

With this module we could use a method similar to the one you suggest above.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Needs: Attention 馃憢 Needs attention from the maintainers label Mar 4, 2024
@ATuckwell ATuckwell self-assigned this Mar 27, 2024
ATuckwell added a commit that referenced this issue Mar 28, 2024
@ATuckwell ATuckwell linked a pull request Apr 2, 2024 that will close this issue
5 tasks
ATuckwell added a commit that referenced this issue Apr 10, 2024
ATuckwell added a commit that referenced this issue Apr 11, 2024
Co-authored-by: github-actions <action@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants