Skip to main content
emnode
Compliance Medium severity MCSB NS-2

Microsoft Defender for Cloud · Azure Container Registry

Container registries should use private link

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Container registries should use private link" check?

Flags any Azure Container Registry that has no approved private endpoint connection, so the registry login server is still reached over its public endpoint rather than a private IP inside your virtual network. It inspects the registry's 'privateEndpointConnections' property and passes only when at least one connection is present and approved. It does not check whether public network access is separately disabled, and it does not evaluate firewall rules, IP allow lists or service endpoints, so a registry can pass this control while still being reachable publicly if you have not also turned public access off.

Why does "Container registries should use private link" matter?

Container registries hold the images that run your production workloads, often baked with embedded secrets, internal package feeds and proprietary code. A public registry endpoint is internet-reachable and authenticates with credentials or tokens that leak, get committed to repositories or sit in CI logs, putting your entire image supply chain one stolen credential away from a stranger. Private link removes that exposure: pulls and pushes traverse a private IP on the Microsoft backbone instead of the public internet, so an attacker outside your network cannot even reach the login server to attempt authentication. That shrinks the attack surface and keeps image traffic off paths your network controls cannot see.

How do I fix "Container registries should use private link"?

  1. Confirm the registry is on the Premium service tier, since private link is a Premium-only feature: run 'az acr update --name <registry> --sku Premium' to upgrade if it is on Basic or Standard.
  2. Create a private endpoint for the registry in your virtual network with 'az network private-endpoint create --name <pe-name> --resource-group <rg> --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id <registry-resource-id> --group-id registry --connection-name <conn>', then add the matching privatelink.azurecr.io private DNS zone so the login server resolves to the private IP.
  3. Once the private endpoint is approved and DNS resolves internally, lock the public path with 'az acr update --name <registry> --public-network-enabled false' so the registry is reachable only through private link. To enforce this at scale, assign the built-in audit policy by exact display name: pid=$(az policy definition list --query "[?displayName=='Container registries should use private link'].name" -o tsv); az policy assignment create --name acr-private-link --policy "$pid" --scope <scope>.

Remediation script · bash

# 1. Lock down an existing Premium registry: add a private endpoint, then close the public door.
RG=platform-rg
ACR=prodimagesacr
VNET=hub-vnet
SUBNET=registry-subnet

# Create the private endpoint into your VNet (registry must be Premium).
ACR_ID=$(az acr show --name "$ACR" --query id -o tsv)
az network private-endpoint create \
  --resource-group "$RG" --name "${ACR}-pe" \
  --vnet-name "$VNET" --subnet "$SUBNET" \
  --private-connection-resource-id "$ACR_ID" \
  --group-id registry \
  --connection-name "${ACR}-conn"

# Close the public door but keep trusted services (Defender, ACR Tasks, import) reaching it.
az acr update --name "$ACR" \
  --default-action Deny \
  --public-network-enabled false \
  --allow-trusted-services true

# 2. Create a NEW registry encrypted with a customer-managed key (CMK is create-time only).
#    Prereqs already in place: user-assigned identity granted 'Key Vault Crypto Service
#    Encryption User' on a key vault with soft-delete + purge protection, and an RSA key.
az acr create --resource-group "$RG" --name regulatedacr --sku Premium \
  --identity "$identityID" --key-encryption-key "$keyID"

# 3. Ratchet it shut estate-wide: audit any registry that allows unrestricted network access.
pid=$(az policy definition list \
  --query "[?displayName=='Container registries should not allow unrestricted network access'].name" -o tsv)
az policy assignment create \
  --name audit-acr-network \
  --policy "$pid" \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000"

Full walkthrough (console steps, edge cases and verification) in the lesson Harden Azure Container Registry.

Is "Container registries should use private link" a false positive?

A registry that exists only to serve images to public consumers, for example an open-source project distributing community images, has a legitimate reason to stay on the public endpoint and will flag here. The control assumes a private supply chain, so exempt these registries deliberately with a documented policy exemption rather than leaving the finding to age, and keep anonymous pull and content trust settings reviewed since the network is intentionally open.