Connect privately to an Azure container registry using Azure Private Link

Limit access to a registry by assigning virtual network private IP addresses to the registry endpoints and using Azure Private Link. Network traffic between the clients on the virtual network and the registry's private endpoints traverses the virtual network and a private link on the Microsoft backbone network, eliminating exposure from the public internet. Private Link also enables enables private registry access from on-premises through Azure ExpressRoute private peering or a VPN gateway.

You can configure DNS settings for the registry's private endpoints, so that the settings resolve to the registry's allocated private IP address. With DNS configuration, clients and services in the network can continue to access the registry at the registry's fully qualified domain name, such as myregistry.azurecr.io.

This feature is available in the Premium container registry service tier. For information about registry service tiers and limits, see Azure Container Registry tiers.

Things to know

  • Currently, image scanning using Azure Security Center isn't available in a registry configured with a private endpoint.
  • Currently, a maximum of 10 private endpoints can be set up for a registry.

Prerequisites

  • To use the Azure CLI steps in this article, Azure CLI version 2.6.0 or later is recommended. If you need to install or upgrade, see Install Azure CLI. Or run in Azure Cloud Shell.

  • If you don't already have a container registry, create one (Premium tier required) and import a sample image such as hello-world from Docker Hub. For example, use the Azure portal or the Azure CLI to create a registry.

  • To configure registry access using a private link in a different Azure subscription, you need to register the resource provider for Azure Container Registry in that subscription. For example:

    az account set --subscription <Name or ID of subscription of private link>
    
    az provider register --namespace Microsoft.ContainerRegistry
    

The Azure CLI examples in this article use the following environment variables. Substitute values appropriate for your environment. All examples are formatted for the Bash shell:

REGISTRY_NAME=<container-registry-name>
REGISTRY_LOCATION=<container-registry-location> # Azure region such as westeurope where registry created
RESOURCE_GROUP=<resource-group-name>
VM_NAME=<virtual-machine-name>

Create a Docker-enabled virtual machine

For test purposes, use a Docker-enabled Ubuntu VM to access an Azure container registry. To use Azure Active Directory authentication to the registry, also install the Azure CLI on the VM. If you already have an Azure virtual machine, skip this creation step.

You may use the same resource group for your virtual machine and your container registry. This setup simplifies clean-up at the end but isn't required. If you choose to create a separate resource group for the virtual machine and virtual network, run az group create. The following example assumes you've set environment variables for the resource group name and registry location:

az group create --name $RESOURCE_GROUP --location $REGISTRY_LOCATION

Now deploy a default Ubuntu Azure virtual machine with az vm create. The following example creates a VM named myDockerVM.

VM_NAME=myDockerVM

az vm create \
  --resource-group $RESOURCE_GROUP \
  --name $VM_NAME \
  --image UbuntuLTS \
  --admin-username azureuser \
  --generate-ssh-keys

It takes a few minutes for the VM to be created. When the command completes, take note of the publicIpAddress displayed by the Azure CLI. Use this address to make SSH connections to the VM.

Install Docker on the VM

After the VM is running, make an SSH connection to the VM. Replace publicIpAddress with the public IP address of your VM.

ssh azureuser@publicIpAddress

Run the following commands to install Docker on the Ubuntu VM:

sudo apt-get update
sudo apt install docker.io -y

After installation, run the following command to verify that Docker is running properly on the VM:

sudo docker run -it hello-world

Output:

Hello from Docker!
This message shows that your installation appears to be working correctly.
[...]

Install the Azure CLI

Follow the steps in Install Azure CLI with apt to install the Azure CLI on your Ubuntu virtual machine. For example:

curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Exit the SSH connection.

Get network and subnet names

If you don't have them already, you'll need the names of a virtual network and subnet to set up a private link. In this example, you use the same subnet for the VM and the registry's private endpoint. However, in many scenarios you would set up the endpoint in a separate subnet.

When you create a VM, Azure by default creates a virtual network in the same resource group. The name of the virtual network is based on the name of the virtual machine. For example, if you name your virtual machine myDockerVM, the default virtual network name is myDockerVMVNET, with a subnet named myDockerVMSubnet. Set these values in environment variables by running the az network vnet list command:

NETWORK_NAME=$(az network vnet list \
  --resource-group $RESOURCE_GROUP \
  --query '[].{Name: name}' --output tsv)

SUBNET_NAME=$(az network vnet list \
  --resource-group $RESOURCE_GROUP \
  --query '[].{Subnet: subnets[0].name}' --output tsv)

echo NETWORK_NAME=$NETWORK_NAME
echo SUBNET_NAME=$SUBNET_NAME

Disable network policies in subnet

Disable network policies such as network security groups in the subnet for the private endpoint. Update your subnet configuration with az network vnet subnet update:

az network vnet subnet update \
 --name $SUBNET_NAME \
 --vnet-name $NETWORK_NAME \
 --resource-group $RESOURCE_GROUP \
 --disable-private-endpoint-network-policies

Configure the private DNS zone

Create a private DNS zone for the private Azure container registry domain. In later steps, you create DNS records for your registry domain in this DNS zone.

To use a private zone to override the default DNS resolution for your Azure container registry, the zone must be named privatelink.azurecr.io. Run the following az network private-dns zone create command to create the private zone:

az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name "privatelink.azurecr.io"

Run az network private-dns link vnet create to associate your private zone with the virtual network. This example creates a link called myDNSLink.

az network private-dns link vnet create \
  --resource-group $RESOURCE_GROUP \
  --zone-name "privatelink.azurecr.io" \
  --name MyDNSLink \
  --virtual-network $NETWORK_NAME \
  --registration-enabled false

Create a private registry endpoint

In this section, create the registry's private endpoint in the virtual network. First, get the resource ID of your registry:

REGISTRY_ID=$(az acr show --name $REGISTRY_NAME \
  --query 'id' --output tsv)

Run the az network private-endpoint create command to create the registry's private endpoint.

The following example creates the endpoint myPrivateEndpoint and service connection myConnection. To specify a container registry resource for the endpoint, pass --group-ids registry:

az network private-endpoint create \
    --name myPrivateEndpoint \
    --resource-group $RESOURCE_GROUP \
    --vnet-name $NETWORK_NAME \
    --subnet $SUBNET_NAME \
    --private-connection-resource-id $REGISTRY_ID \
    --group-ids registry \
    --connection-name myConnection

Get private IP addresses

Run az network private-endpoint show to query the endpoint for the network interface ID:

NETWORK_INTERFACE_ID=$(az network private-endpoint show \
  --name myPrivateEndpoint \
  --resource-group $RESOURCE_GROUP \
  --query 'networkInterfaces[0].id' \
  --output tsv)

Associated with the network interface in this example are two private IP addresses for the container registry: one for the registry itself, and one for the registry's data endpoint. The following az resource show commands get the private IP addresses for the container registry and the registry's data endpoint:

PRIVATE_IP=$(az resource show \
  --ids $NETWORK_INTERFACE_ID \
  --api-version 2019-04-01 \
  --query 'properties.ipConfigurations[1].properties.privateIPAddress' \
  --output tsv)

DATA_ENDPOINT_PRIVATE_IP=$(az resource show \
  --ids $NETWORK_INTERFACE_ID \
  --api-version 2019-04-01 \
  --query 'properties.ipConfigurations[0].properties.privateIPAddress' \
  --output tsv)

Note

If your registry is geo-replicated, query for the additional data endpoint for each registry replica.

Create DNS records in the private zone

The following commands create DNS records in the private zone for the registry endpoint and its data endpoint. For example, if you have a registry named myregistry in the westeurope region, the endpoint names are myregistry.azurecr.io and myregistry.westeurope.data.azurecr.io.

Note

If your registry is geo-replicated, create additonal DNS records for each replica's data endpoint IP.

First run az network private-dns record-set a create to create empty A record sets for the registry endpoint and data endpoint:

az network private-dns record-set a create \
  --name $REGISTRY_NAME \
  --zone-name privatelink.azurecr.io \
  --resource-group $RESOURCE_GROUP

# Specify registry region in data endpoint name
az network private-dns record-set a create \
  --name ${REGISTRY_NAME}.${REGISTRY_LOCATION}.data \
  --zone-name privatelink.azurecr.io \
  --resource-group $RESOURCE_GROUP

Run the az network private-dns record-set a add-record command to create the A records for the registry endpoint and data endpoint:

az network private-dns record-set a add-record \
  --record-set-name $REGISTRY_NAME \
  --zone-name privatelink.azurecr.io \
  --resource-group $RESOURCE_GROUP \
  --ipv4-address $PRIVATE_IP

# Specify registry region in data endpoint name
az network private-dns record-set a add-record \
  --record-set-name ${REGISTRY_NAME}.${REGISTRY_LOCATION}.data \
  --zone-name privatelink.azurecr.io \
  --resource-group $RESOURCE_GROUP \
  --ipv4-address $DATA_ENDPOINT_PRIVATE_IP

The private link is now configured and ready for use.

Set up a private link when you create a registry, or add a private link to an existing registry. The following steps assume you already have a virtual network and subnet set up with a VM for testing. You can also create a new virtual network and subnet.

Create a private endpoint - new registry

  1. When creating a registry in the portal, on the Basics tab, in SKU, select Premium.

  2. Select the Networking tab.

  3. In Network connectivity, select Private endpoint > + Add.

  4. Enter or select the following information:

    Setting Value
    Subscription Select your subscription.
    Resource group Enter the name of an existing group or create a new one.
    Name Enter a unique name.
    Subresource Select registry
    Networking
    Virtual network Select the virtual network where your virtual machine is deployed, such as myDockerVMVNET.
    Subnet Select a subnet, such as myDockerVMSubnet where your virtual machine is deployed.
    Private DNS Integration
    Integrate with private DNS zone Select Yes.
    Private DNS Zone Select (New) privatelink.azurecr.io
  5. Configure the remaining registry settings, and then select Review + Create.

Create registry with private endpoint

Create a private endpoint - existing registry

  1. In the portal, navigate to your container registry.

  2. Under Settings, select Networking.

  3. On the Private endpoints tab, select + Private endpoint.

  4. In the Basics tab, enter or select the following information:

    Setting Value
    Project details
    Subscription Select your subscription.
    Resource group Enter the name of an existing group or create a new one.
    Instance details
    Name Enter a name.
    Region Select a region.
  5. Select Next: Resource.

  6. Enter or select the following information:

    Setting Value
    Connection method Select Connect to an Azure resource in my directory.
    Subscription Select your subscription.
    Resource type Select Microsoft.ContainerRegistry/registries.
    Resource Select the name of your registry
    Target subresource Select registry
  7. Select Next: Configuration.

  8. Enter or select the information:

    Setting Value
    Networking
    Virtual network Select the virtual network where your virtual machine is deployed, such as myDockerVMVNET.
    Subnet Select a subnet, such as myDockerVMSubnet where your virtual machine is deployed.
    Private DNS Integration
    Integrate with private DNS zone Select Yes.
    Private DNS Zone Select (New) privatelink.azurecr.io
  9. Select Review + create. You're taken to the Review + create page where Azure validates your configuration.

  10. When you see the Validation passed message, select Create.

After the private endpoint is created, DNS settings in the private zone appear on the Private endpoints page in the portal:

  1. In the portal, navigate to your container registry and select Settings > Networking.
  2. On the Private endpoints tab, select the private endpoint you created.
  3. On the Overview page, review the link settings and custom DNS settings.

Endpoint DNS settings

Your private link is now configured and ready for use.

Disable public access

For many scenarios, disable registry access from public networks. This configuration prevents clients outside the virtual network from reaching the registry endpoints.

Disable public access - CLI

To disable public access using the Azure CLI, run az acr update and set --public-network-enabled to false.

Note

The public-network-enabled argument requires Azure CLI 2.6.0 or later.

az acr update --name $REGISTRY_NAME --public-network-enabled false

Disable public access - portal

  1. In the portal, navigate to your container registry and select Settings > Networking.
  2. On the Public access tab, in Allow public network access, select Disabled. Then select Save.

You should validate that the resources within the subnet of the private endpoint connect to your registry over a private IP address, and have the correct private DNS zone integration.

To validate the private link connection, SSH to the virtual machine you set up in the virtual network.

Run the nslookup command to resolve the IP address of your registry over the private link:

nslookup $REGISTRY_NAME.azurecr.io

Example output shows the registry's IP address in the address space of the subnet:

[...]
myregistry.azurecr.io       canonical name = myregistry.privatelink.azurecr.io.
Name:   myregistry.privatelink.azurecr.io
Address: 10.0.0.6

Compare this result with the public IP address in nslookup output for the same registry over a public endpoint:

[...]
Non-authoritative answer:
Name:   myregistry.westeurope.cloudapp.azure.com
Address: 40.78.103.41

Also verify that you can perform registry operations from the virtual machine in the subnet. Make an SSH connection to your virtual machine, and run az acr login to login to your registry. Depending on your VM configuration, you might need to prefix the following commands with sudo.

az acr login --name $REGISTRY_NAME

Perform registry operations such as docker pull to pull a sample image from the registry. Replace hello-world:v1 with an image and tag appropriate for your registry, prefixed with the registry login server name (all lowercase):

docker pull myregistry.azurecr.io/hello-world:v1

Docker successfully pulls the image to the VM.

Manage private endpoint connections

Manage a registry's private endpoint connections using the Azure portal, or by using commands in the az acr private-endpoint-connection command group. Operations include approve, delete, list, reject, or show details of a registry's private endpoint connections.

For example, to list the private endpoint connections of a registry, run the az acr private-endpoint-connection list command. For example:

az acr private-endpoint-connection list \
  --registry-name $REGISTRY_NAME 

When you set up a private endpoint connection using the steps in this article, the registry automatically accepts connections from clients and services that have RBAC permissions on the registry. You can set up the endpoint to require manual approval of connections. For information about how to approve and reject private endpoint connections, see Manage a Private Endpoint Connection.

Add zone records for replicas

As shown in this article, when you add a private endpoint connection to a registry, DNS records in the privatelink.azurecr.io zone are created for the registry and its data endpoints in the regions where the registry is replicated.

If you later add a new replica, you need to manually add a new zone record for the data endpoint in that region. For example, if you create a replica of myregistry in the northeurope location, add a zone record for myregistry.northeurope.data.azurecr.io. For steps, see Create DNS records in the private zone in this article.

Clean up resources

If you created all the Azure resources in the same resource group and no longer need them, you can optionally delete the resources by using a single az group delete command:

az group delete --name $RESOURCE_GROUP

To clean up your resources in the portal, navigate to your resource group. Once the resource group is loaded, click on Delete resource group to remove the resource group and the resources stored there.

Next steps