Featured image of post Demystifying Azure Private Link service

Demystifying Azure Private Link service

What is a Private Endpoint? Do I need to deploy a Private Link Service or a Private Endpoint? And how does DNS fit into the equation? This article is designed to demystify Azure Private Link concepts.

Personally, I find Azure Private Link Service incredibly valuable and beneficial. However, I recognize that it can be confusing at first glance, especially when it’s not actively used and becomes a bit fuzzy in memory. That’s precisely why I’m writing this article – to serve as a reference for future instances when I, and others like me, encounter this challenge.

Introduction

Long story short, Azure Private Link enables one-directional access to resources via a virtual network (vNET) privately. A resource published via Private Link (and its associated services) into a vNET cannot access other resources within the vNET. However, other resources within the vNET can access the published resource. A resource can be any Azure PaaS service (such as Azure KeyVault or Azure CosmosDB, a list of supported services can be found here) or any your workload represented by Azure Load Balancer. The traffic goes via the Microsoft backbone network.

Data diagram of resources for Private Link service

This resource is specifically designed for publishing a Load Balancer via Private Link. It’s not getting involved in the setup of Azure native PaaS service, just for Load Balancer. In essence, it’s associated with the Frontend IP of the chosen Load Balancer. Each Private Link Service can only be assigned to a single Load Balancer Frontend IP - a Standard Load Balancer can host up to 8 Private Link Services. Private Link Service is a standard Azure resource that requires deployment within a resource group and location.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
param privatelinkServiceName string
param location string
param loadBalancerFrontEndIpConfigurationResourceId string
param loadbalancerId string


resource privatelinkService 'Microsoft.Network/privateLinkServices@2021-05-01' = {
  name: privatelinkServiceName
  location: location
  properties: {
    enableProxyProtocol: false
    loadBalancerFrontendIpConfigurations: [
      {
        id: loadBalancerFrontEndIpConfigurationResourceId
      }
    ]
    ipConfigurations: [
      {
        name: 'snet-provider-default-1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          privateIPAddressVersion: 'IPv4'
          subnet: {
            id: reference(loadbalancerId, '2019-06-01').frontendIPConfigurations[0].properties.subnet.id
          }
          primary: false
        }
      }
    ]
  }
}

output id string = privatelinkService.id

Azure Private Endpoint resource

Private Endpoint is a critical component in both approaches, whether for Azure PaaS services or for Load Balancers, and it must be present at all times. It serves as an instance (interface) of the published resource within a virtual network (vNET).

A Private Endpoint has an associated resource, namely the Azure Network Interface (NIC) resource. This NIC allocates an IP address within a virtual network (vNET).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
param keyvaultPrivateEndpointName string
param location string
param keyVaultResourceId string
param subnetResourceId string


resource keyVaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2022-01-01' = {
  name: keyvaultPrivateEndpointName
  location: location
  properties: {
    privateLinkServiceConnections: [
      {
        name: keyvaultPrivateEndpointName
        properties: {
          groupIds: [
            'vault'
          ]
          privateLinkServiceId: keyVaultResourceId
        }
      }
    ]
    subnet: {
      id: subnetResourceId
    }
  }
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
param privateEndpointName string
param location string
param vnetName string
param subnetName string
param privateLinkServiceResourceId string


resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-06-01' = {
  name: privateEndpointName
  location: location
  properties: {
    subnet: {
      id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName)
    }
    privateLinkServiceConnections: [
      {
        name: privateEndpointName
        properties: {
          privateLinkServiceId: privateLinkServiceResourceId
        }
      }
    ]
  }
}

module networkInterface '_nic-nested.bicep' = {
  name: 'nested'
  params: {
    nicName: last(split(privateEndpoint.properties.networkInterfaces[0].id, '/'))
  }
}

output ipAddressInVnet string = networkInterface.outputs.ip


// _nic-nested.bicep file:
param nicName string

resource networkInterface 'Microsoft.Network/networkInterfaces@2021-08-01' existing = {
  name: nicName
}

output ip string = networkInterface.properties.ipConfigurations[0].properties.privateIPAddress

Why To Care about DNS

The seamless utilization of an Azure PaaS resource via Private Link, without any additional configuration, relies on correct DNS configuration.

Let’s show it on example - when you deploy a Blob Storage, it gets a hostname like: <<something>>.blob.core.windows.net.

Whenever you access the hostname myblobstorage.blob.core.windows.net, it automatically resolves to the assigned public IP address of the Blob Storage service. However, with Private Endpoint, a Network Interface (NIC) representing the Blob Storage is deployed within your vNET, allocated with a private IP address from your vNET range. Therefore, you need to access the Blob Storage using this private IP.

Consider a scenario where you have a Blob Storage with the hostname myblobstorage.blob.core.windows.net, and public access is blocked. If you perform an HTTPS call for https://myblobstorage.blob.core.windows.net/ from a resource within the vNET:

  • In a standard setup, the hostname myblobstorage.blob.core.windows.net would be translated by a public DNS server to the associated Azure public IP address (e.g., 20.60.181.xxx) and routed via the vNET’s configured internet routing method. However, since the request appears to arrive from the “public internet” rather than the vNET, the Blob Storage service would refuse it due to disabled public access.
1
2
3
4
5
6
7
8
> nslookup myblobstorage.blob.core.windows.net

Server: 168.63.129.16

Non-authoritative answer:
Name: blob.xxxxxx.store.core.windows.net
Addresses: 
    20.60.181.xxx
  • Alternatively, if you wish to use the internal IP address directly, you could perform an HTTPS call for https://10.0.1.2/ (the private IP address within the vNET). In this case, TLS certificate validation comes in the game. The request would be routed to the Blob Storage server correctly, and the server would respond with a TLS certificate issued for myblobstorage.blob.core.windows.net hostname. However, the web client initiating the request will validate the certificate and encounter a TLS error regarding the mismatching certificate (as the certificate is not issued for 10.0.1.2). While turning off certificate validation could be an option, it introduces vulnerability to Man-in-the-Middle (MiTM) attacks, which is not advisable.

To tackle this challenge, the Private Link concept relies on “overwriting DNS records” within the vNET. For this purpose, Azure provides a built-in service called Azure Private DNS Zone. This service allows hosting specific DNS zones, while forwarding the rest of the requests for resolutions to standard DNS servers.

When your vNET utilizes default Azure DNS resolvers, you simply need to create the Azure Private DNS Zone for the selected PaaS service. Corresponding records for the PaaS resource should be configured within this private DNS zone. As a result, any DNS resolution requests originating from the selected vNET will automatically be overwritten to use Private Link’s IP records.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> nslookup myblobstorage.blob.core.windows.net

Server: 168.63.129.16

Non-authoritative answer:
Name: blob.xxxxxx.store.core.windows.net
Addresses: 
    10.0.1.2
Aliases:
    myblobstorage.privatelink.store.core.windows.net

In case your vNET utilizes a custom DNS resolvers, you may set up a forwarder within a Azure vNET, integrated with Azure Private DNS Zone and you may configure your environment to even resolve resources via Private Link from on-premises. More details about Azure DNS Private Resolver can be found here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
param privateDnsZoneName string
param keyVaultResourceId string
param virtualNetworkResourceId string

resource keyVaultPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: privateDnsZoneName
  location: 'global'
  properties: {}
}

resource privateEndpointDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-01-01' = {
  name: 'vault-PrivateDnsZoneGroup'
  parent: keyVaultPrivateEndpoint
  properties: {
    privateDnsZoneConfigs: [
      {
        name: privateDnsZoneName
        properties: {
          privateDnsZoneId: keyVaultPrivateDnsZone.id
        }
      }
    ]
  }
}

resource keyVaultPrivateDnsZoneVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  name: uniqueString(keyVaultResourceId)
  parent: keyVaultPrivateDnsZone
  location: 'global'
  properties: {
    registrationEnabled: false
    virtualNetwork: {
      id: virtualNetworkResourceId
    }
  }
}

Key Takeaways

The Vital Role of vNET

Without a virtual network (vNET), consuming a resource via Private Link isn’t possible. The vNET serves as the essential conduit facilitating communication between the published resource and the consuming resource, such as a virtual machine (VM) or Azure Function.

DNS Private Zones

As mentioned earlier, configuring the corresponding Azure DNS Private Zone is crucial for ensuring the smooth operation of Private Endpoint. List of DNS zones for Azure PaaS services, such as privatelink.vaultcore.azure.net for KeyVault, can be found in Microsoft Docs here.

For debugging a effective resolution of DNS records within a vNET you may utilize my open-source tool AzFuncDebugger.

Ingress vs. Egress traffic

Azure Private Link communication is incoming-only from a resource perspective. This means that the resource accessed via Private Link cannot initiate communication with any other resource within the virtual network. Therefore, Private Link should not be considered as an option for routing bi-directional communication between two resources within the virtual network.

For instance, if you access a resource like KeyVault from an AppService, you could utilize publishing KeyVault via Private Endpoint (incoming traffic from KeyVault perspective). However, for outbound traffic from the AppService, you would need to employ standard vNET outbound traffic integration.

Sub-resource Targets

Some Azure PaaS services consist of multiple sub-resources (subdomains), as seen with Azure Storage, which has privatelink.blob.core.windows.net for Blob, privatelink.file.core.windows.net for File, and so on.

This implies that to consume all sub-resources of an Azure Storage resource, you would need to create multiple Private Endpoints, each with its corresponding DNS records, to ensure full functionality.

The full list of sub-resources for Azure PaaS resources can be found here.

One significant benefit of Azure Private Link worth emphasizing is its capability to publish a service from one region to another by creating a Private Endpoint in a different region. Because the traffic travels via the Microsoft backbone network, the additional latency incurred is minimal. However, it’s important to keep in mind that splitting resources of your workload across regions may decrease the resiliency of the workload, as it becomes dependent on two regions.

Another remarkable use case is the ability for Private Link connections to be cross-tenant. As an Azure ISV provider, you can construct a highly intricate SaaS solution and expose only a load balancer to the customer. This load balancer allows the customer to privately consume the SaaS service within their vNETs via Private Link.

Example of SaaS solution published to customer via Private Link

Multiple Endpoints

Multiple Private Endpoints can be associated with a single Private Link Service, with a maximum of up to 1000 instances. For instance, this capability allows you to deploy a single Load Balancer for your SaaS solution and share it among multiple customers.

Infrastructure as Code Support

All resources related to Azure Private Link have full support to be deployed via IaC tooling like Azure ARM, Bicep or Terraform. See example within this article to deploy resources via Azure Bicep.

Approvals

Since Azure Private Link supports the creation of cross-tenant connections, establishing a secure connection requires initiating a new Private Link connection from the consumer’s tenant and obtaining approval from the tenant that owns the Private Link Service with resources.

If the consumer has RBAC permissions on the published resources via the Private Link Service, the connection will be automatically approved. However, if the consumer does not have such permissions, manual approval from the customer is required. In this scenario, the Azure Private Link Center interface in the Azure Portal allows customers to view all pending connections and approve or reject Private Link connections as needed.

Azure Private Link Center

Costs

Please keep in mind that Private Endpoints incur additional costs, priced at $0.01 per hour for each Private Endpoint. Additionally, there are additional costs associated with the processing of inbound and outbound data transferred via Private Endpoints, which are charged per GB. It’s important to note that these costs could potentially accumulate to significant amounts in certain scenarios. On the other hand, utilizing a Private Link Service itself is free of charge.

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy