Back to Checks
azure

Container Registry uses a private endpoint (Private Link)

containerregistry_uses_private_link
Service: containerregistrySeverity: high
OfficialBuilt-inVerified
Categories:internet-exposedtrust-boundaries
Resource Type:microsoft.containerregistry/registries

Azure Container Registry access via Private Endpoints (Azure Private Link). Registries with private endpoint connections use private IPs; others rely on the public endpoint.

Risk

Publicly reachable registries expand attack surface for credential stuffing, token abuse, and scanning. A compromise enables unauthorized pull/push, causing image data leakage and supply-chain tampering. Public routing weakens network isolation, impacting the confidentiality and integrity of images and metadata.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler azure --checks containerregistry_uses_private_link

Recommendation

Use Private Link with private endpoints and set Public network access: Disabled.

  • Restrict access to trusted VNets/subnets
  • Prefer private endpoints over service endpoints
  • Enforce least privilege on registry actions
  • Configure private DNS for the registry FQDN
  • Monitor access logs for defense in depth

Remediation

CLI

az network private-endpoint create --resource-group <example_resource_name> --name <example_resource_name> --vnet-name <example_resource_name> --subnet <example_resource_name> --private-connection-resource-id <example_resource_id> --group-ids registry --connection-name <example_resource_name>

Terraform

resource "azurerm_private_endpoint" "<example_resource_name>" {
  name                = "<example_resource_name>"
  location            = "<location>"
  resource_group_name = "<example_resource_name>"
  subnet_id           = "<example_subnet_id>"

  private_service_connection {
    name                           = "<example_resource_name>"
    private_connection_resource_id = "<example_resource_id>" # Critical: ACR resource ID
    subresource_names              = ["registry"]            # Critical: Target 'registry' subresource to enable Private Link
  }
}

NativeIaC

// Create a Private Endpoint to ACR
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2025-05-01' = {
  name: '<example_resource_name>'
  location: resourceGroup().location
  properties: {
    subnet: {
      id: '<example_subnet_id>'
    }
    privateLinkServiceConnections: [
      {
        name: '<example_resource_name>'
        properties: {
          privateLinkServiceId: '<example_resource_id>' // Critical: ACR resource ID to connect
          groupIds: ['registry']                        // Critical: Target the 'registry' subresource to enable Private Link
        }
      }
    ]
  }
}

Other

1In Azure Portal, go to Container registries > select your registry
2Navigate to Settings > Networking > Private endpoints tab
3Click + Private endpoint, enter a name, select your VNet and Subnet
4Set Resource type to Microsoft.ContainerRegistry/registries and Target subresource to registry
5Click Review + create, then Create

Check Code

from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.containerregistry.containerregistry_client import (
    containerregistry_client,
)


class containerregistry_uses_private_link(Check):
    def execute(self) -> list[Check_Report_Azure]:
        findings = []

        for subscription, registries in containerregistry_client.registries.items():
            subscription_name = containerregistry_client.subscriptions.get(
                subscription, subscription
            )
            for container_registry_info in registries.values():
                report = Check_Report_Azure(
                    metadata=self.metadata(), resource=container_registry_info
                )
                report.subscription = subscription
                report.status = "FAIL"
                report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription_name} ({subscription}) does not use a private link."

                if container_registry_info.private_endpoint_connections:
                    report.status = "PASS"
                    report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription_name} ({subscription}) uses a private link."

                findings.append(report)

        return findings