|
| 1 | +--- |
| 2 | +title: OIDC with Microsoft Entra ID in Private Subnets |
| 3 | +url: /nginxaas/azure/quickstart/security-controls/private-subnet-oidc-entra/ |
| 4 | +toc: true |
| 5 | +weight: 350 |
| 6 | +nd-content-type: how-to |
| 7 | +nd-product: N4Azure |
| 8 | +--- |
| 9 | + |
| 10 | +## Overview |
| 11 | + |
| 12 | +Learn how to configure F5 NGINXaaS for Azure with OpenID Connect (OIDC) authentication using Microsoft Entra ID when your NGINXaaS deployment is in a private subnet. This guide addresses the networking requirements to enable authentication traffic to reach Microsoft Entra ID endpoints while maintaining security controls. |
| 13 | + |
| 14 | +When NGINXaaS is deployed in a private subnet, authentication traffic must reach external Microsoft Entra ID endpoints at `login.microsoftonline.com`. This guide provides the following solutions to enable this connectivity while controlling outbound traffic: |
| 15 | + |
| 16 | +1. **Azure NAT Gateway with NSG rules** - Lower cost, simpler configuration |
| 17 | +1. **Azure Firewall** - Higher security, more granular control |
| 18 | + |
| 19 | +## Before you begin |
| 20 | + |
| 21 | +To complete this guide, you need to set up the following: |
| 22 | + |
| 23 | +- [An NGINXaaS deployment]({{< ref "/nginxaas-azure/getting-started/create-deployment" >}}) with a private IP address |
| 24 | +- A configured Microsoft Entra ID application registration. See [Configure Entra ID]({{< ref "/nginx/deployment-guides/single-sign-on/entra-id/#entra-setup" >}}) for detailed setup instructions. |
| 25 | +- [SSL/TLS certificates]({{< ref "/nginxaas-azure/getting-started/ssl-tls-certificates/" >}}) configured for your NGINXaaS deployment |
| 26 | +- [Runtime State Sharing]({{< ref "/nginxaas-azure/quickstart/runtime-state-sharing.md" >}}) enabled on the NGINXaaS deployment |
| 27 | + |
| 28 | +## Solution comparison |
| 29 | + |
| 30 | +Choose the networking solution that best fits your security and cost requirements: |
| 31 | + |
| 32 | +{{< table >}} |
| 33 | +| Feature | Azure NAT Gateway | Azure Firewall | |
| 34 | +|---------|-------------------|----------------| |
| 35 | +| **Hourly cost** | Lower - see [NAT Gateway pricing](https://azure.microsoft.com/en-us/pricing/details/azure-nat-gateway/) | Higher - see [Firewall pricing](https://azure.microsoft.com/en-us/pricing/details/azure-firewall/) | |
| 36 | +| **Address space** | Less required | More required (2 additional /26 subnets) | |
| 37 | +| **Security** | Less secure - broader IP range filtering | More secure - precise FQDN-based filtering | |
| 38 | +| **Configuration** | Simple setup, less overhead | More complex configuration, more overhead | |
| 39 | +| **Public IPs** | 1 required | 2 required | |
| 40 | +| **Filtering precision** | Generic rules enabling broader filtering | Surgical precision for traffic filtering | |
| 41 | +{{< /table >}} |
| 42 | + |
| 43 | +## Common configuration steps |
| 44 | + |
| 45 | +Both solutions require these initial steps: |
| 46 | + |
| 47 | +### Configure OIDC in NGINXaaS |
| 48 | + |
| 49 | +1. Follow the standard [OIDC authentication]({{< ref "/nginxaas-azure/quickstart/security-controls/oidc/" >}}) guide with these Microsoft Entra ID specific considerations. |
| 50 | + |
| 51 | +1. Set the `oidc_jwt_keyfile` endpoint in your `openid_connect_configuration.conf`: |
| 52 | + |
| 53 | + ```nginx |
| 54 | + # Use the correct Microsoft Entra ID keys endpoint |
| 55 | + map $host $oidc_jwt_keyfile { |
| 56 | + default "https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys"; |
| 57 | + } |
| 58 | + ``` |
| 59 | + |
| 60 | + {{< call-out "note" >}}The `oidc_jwt_keyfile` endpoint is not listed in the Microsoft App Registration's endpoints pane but is required for proper OIDC configuration.{{< /call-out >}} |
| 61 | + |
| 62 | +1. Configure DNS resolution appropriately: |
| 63 | + - For dual-stack subnets, ensure both IPv4 and IPv6 address spaces are configured |
| 64 | + - Use Azure DNS (127.0.0.1:49153) or configure firewall rules for your preferred DNS service |
| 65 | + |
| 66 | + ```nginx |
| 67 | + http { |
| 68 | + # For IPv4-only deployments |
| 69 | + resolver 127.0.0.1:49153 ipv4=on ipv6=off valid=300s; |
| 70 | +
|
| 71 | + # For dual-stack deployments |
| 72 | + resolver 127.0.0.1:49153 ipv4=on valid=300s; |
| 73 | + } |
| 74 | + ``` |
| 75 | + |
| 76 | + {{< call-out "important" >}}If you plan to use IPv6 addresses on the frontend, ensure your subnet is dual-stack with both IPv4 and IPv6 address spaces. For IPv4-only deployments, set `ipv6=off` in your resolver configuration.{{< /call-out >}} |
| 77 | + |
| 78 | +## Configure connectivity using Azure NAT Gateway |
| 79 | + |
| 80 | +This solution uses Azure NAT Gateway with Network Security Group (NSG) rules to enable controlled outbound connectivity. |
| 81 | + |
| 82 | +### Configure NSG rules |
| 83 | + |
| 84 | +When you create an NGINXaaS deployment, Azure automatically creates and attaches an NSG to the delegated subnet. You need to modify this NSG to allow Microsoft Entra ID connectivity. |
| 85 | + |
| 86 | +1. Add Microsoft IP address ranges to NSG rules: |
| 87 | + |
| 88 | + Navigate to your NGINXaaS subnet's NSG and add inbound and outbound rules for the Microsoft IP addresses listed in the [Microsoft 365 URLs and IP address ranges documentation](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide#microsoft-365-common-and-office-online) under section #56. |
| 89 | + |
| 90 | +1. Create a custom outbound rule to deny general internet access: |
| 91 | + |
| 92 | + - **Priority**: Higher numerical value (lower priority) than the Microsoft IP rules |
| 93 | + - **Action**: Deny |
| 94 | + - **Destination**: Internet |
| 95 | + - **Purpose**: Override the default `AllowInternetOutBound` rule |
| 96 | + |
| 97 | + {{< call-out "important" >}}The default `AllowInternetOutBound` rule cannot be edited, so you must create a higher-priority rule to deny general internet access while allowing the specific Microsoft IP ranges. The priority of the custom deny rule should be a higher numerical value (lower priority) than the Microsoft IP allow rules.{{< /call-out >}} |
| 98 | + |
| 99 | +### Create and configure Azure NAT Gateway |
| 100 | + |
| 101 | +1. Create an Azure NAT Gateway: |
| 102 | + |
| 103 | + ```bash |
| 104 | + # Create Azure NAT Gateway |
| 105 | + az network nat gateway create \ |
| 106 | + --resource-group <resource-group> \ |
| 107 | + --name <nat-gateway-name> \ |
| 108 | + --location <location> \ |
| 109 | + --public-ip-addresses <public-ip-name> |
| 110 | + ``` |
| 111 | + |
| 112 | +1. Associate the Azure NAT Gateway with your NGINXaaS subnet: |
| 113 | + |
| 114 | + ```bash |
| 115 | + # Associate Azure NAT Gateway with subnet |
| 116 | + az network vnet subnet update \ |
| 117 | + --resource-group <resource-group> \ |
| 118 | + --vnet-name <vnet-name> \ |
| 119 | + --name <nginxaas-subnet-name> \ |
| 120 | + --nat-gateway <nat-gateway-name> |
| 121 | + ``` |
| 122 | + |
| 123 | +This configuration allows NGINXaaS to reach Microsoft Entra ID endpoints while blocking general internet access. |
| 124 | + |
| 125 | +{{< call-out "note" >}}Using Azure NAT Gateway with NSG rules still requires allowing broad IP address ranges. Based on Microsoft's documentation, you need to allow at least two /18 subnets and two /19 subnets for complete Microsoft Entra ID connectivity. For more precise filtering, consider using Azure Firewall instead.{{< /call-out >}} |
| 126 | + |
| 127 | +## Configure connectivity using Azure Firewall |
| 128 | + |
| 129 | +This solution provides more granular control using Azure Firewall with DNS-based filtering. |
| 130 | + |
| 131 | +{{< call-out "note" >}}Azure Firewall provides DNS-based filtering capabilities but comes at a significantly higher cost compared to Azure NAT Gateway (approximately 28x cost increase). However, it enables more precise firewall rules for better security.{{< /call-out >}} |
| 132 | + |
| 133 | +### Create firewall subnets |
| 134 | + |
| 135 | +Create two new subnets in your virtual network: |
| 136 | + |
| 137 | +1. **Azure Firewall subnet**: |
| 138 | + - Name: `AzureFirewallSubnet` (immutable) |
| 139 | + - Address space: /26 subnet |
| 140 | + - Purpose: Azure Firewall |
| 141 | + |
| 142 | +1. **Firewall Management subnet**: |
| 143 | + - Name: `AzureFirewallManagementSubnet` (immutable) |
| 144 | + - Address space: /26 subnet |
| 145 | + - Purpose: Firewall Management |
| 146 | + |
| 147 | +```bash |
| 148 | +# Create Azure Firewall subnet |
| 149 | +az network vnet subnet create \ |
| 150 | + --resource-group <resource-group> \ |
| 151 | + --vnet-name <vnet-name> \ |
| 152 | + --name AzureFirewallSubnet \ |
| 153 | + --address-prefixes <firewall-subnet-cidr> |
| 154 | + |
| 155 | +# Create Firewall Management subnet |
| 156 | +az network vnet subnet create \ |
| 157 | + --resource-group <resource-group> \ |
| 158 | + --vnet-name <vnet-name> \ |
| 159 | + --name AzureFirewallManagementSubnet \ |
| 160 | + --address-prefixes <management-subnet-cidr> |
| 161 | +``` |
| 162 | + |
| 163 | +### Create Azure Firewall |
| 164 | + |
| 165 | +1. Create the firewall with Standard SKU (required for DNS proxy functionality): |
| 166 | + |
| 167 | + ```bash |
| 168 | + # Create public IPs for firewall |
| 169 | + az network public-ip create \ |
| 170 | + --name <firewall-public-ip> \ |
| 171 | + --resource-group <resource-group> \ |
| 172 | + --allocation-method Static \ |
| 173 | + --sku Standard |
| 174 | + |
| 175 | + az network public-ip create \ |
| 176 | + --name <firewall-mgmt-public-ip> \ |
| 177 | + --resource-group <resource-group> \ |
| 178 | + --allocation-method Static \ |
| 179 | + --sku Standard |
| 180 | + |
| 181 | + # Create firewall with new policy |
| 182 | + az extension add --name azure-firewall |
| 183 | + az network firewall create \ |
| 184 | + --name <firewall-name> \ |
| 185 | + --resource-group <resource-group> \ |
| 186 | + --vnet-name <vnet-name> \ |
| 187 | + --public-ip <firewall-public-ip> \ |
| 188 | + --firewall-policy <firewall-policy-name> |
| 189 | + ``` |
| 190 | + |
| 191 | + {{< call-out "note" >}}The Standard SKU is required at minimum because it allows Azure Firewall to be configured as a DNS proxy, which is necessary for FQDN-based filtering. During creation, choose to create a new Firewall Policy and use your existing virtual network that contains the NGINXaaS subnet.{{< /call-out >}} |
| 192 | + |
| 193 | +### Configure firewall policy |
| 194 | + |
| 195 | +1. Enable DNS proxy in the firewall policy: |
| 196 | + |
| 197 | + ```bash |
| 198 | + # Enable DNS proxy |
| 199 | + az network firewall policy update \ |
| 200 | + --name <firewall-policy-name> \ |
| 201 | + --resource-group <resource-group> \ |
| 202 | + --enable-dns-proxy true \ |
| 203 | + --dns-servers 168.63.129.16 |
| 204 | + ``` |
| 205 | + |
| 206 | +1. Configure private IP ranges to avoid SNAT for internal traffic: |
| 207 | + |
| 208 | + Navigate to your firewall policy in the Azure portal and under **Private IP ranges**, select "Always" or specify your NGINXaaS subnet IP addresses. |
| 209 | + |
| 210 | +### Create network rules |
| 211 | + |
| 212 | +Create a network rule collection to allow NGINXaaS subnet access to Microsoft Entra ID: |
| 213 | + |
| 214 | +```bash |
| 215 | +# Create network rule collection |
| 216 | +az network firewall policy rule-collection-group create \ |
| 217 | + --name NetworkRuleCollectionGroup \ |
| 218 | + --policy-name <firewall-policy-name> \ |
| 219 | + --resource-group <resource-group> \ |
| 220 | + --priority 200 |
| 221 | + |
| 222 | +# Add network rule for Microsoft Entra ID |
| 223 | +az network firewall policy rule-collection-group collection add-filter-collection \ |
| 224 | + --name EntraIDAccess \ |
| 225 | + --policy-name <firewall-policy-name> \ |
| 226 | + --resource-group <resource-group> \ |
| 227 | + --collection-priority 100 \ |
| 228 | + --rule-collection-group-name NetworkRuleCollectionGroup \ |
| 229 | + --action Allow \ |
| 230 | + --rule-name AllowEntraID \ |
| 231 | + --rule-type NetworkRule \ |
| 232 | + --protocols TCP \ |
| 233 | + --source-addresses <nginxaas-subnet-cidr> \ |
| 234 | + --destination-fqdns login.microsoftonline.com \ |
| 235 | + --destination-ports 443 |
| 236 | +``` |
| 237 | + |
| 238 | +### Configure route table |
| 239 | + |
| 240 | +Direct NGINXaaS subnet traffic through the firewall: |
| 241 | + |
| 242 | +1. Note the private IP address of your Azure Firewall (found in the firewall's overview page). |
| 243 | + |
| 244 | +1. Create a route table: |
| 245 | + |
| 246 | + ```bash |
| 247 | + # Create route table |
| 248 | + az network route-table create \ |
| 249 | + --name <route-table-name> \ |
| 250 | + --resource-group <resource-group> \ |
| 251 | + --location <location> |
| 252 | + |
| 253 | + # Add default route pointing to firewall |
| 254 | + az network route-table route create \ |
| 255 | + --route-table-name <route-table-name> \ |
| 256 | + --resource-group <resource-group> \ |
| 257 | + --name DefaultRoute \ |
| 258 | + --address-prefix 0.0.0.0/0 \ |
| 259 | + --next-hop-type VirtualAppliance \ |
| 260 | + --next-hop-ip-address <firewall-private-ip> |
| 261 | + ``` |
| 262 | + |
| 263 | +1. Associate the route table with the NGINXaaS subnet: |
| 264 | + |
| 265 | + ```bash |
| 266 | + # Associate route table with NGINXaaS subnet |
| 267 | + az network vnet subnet update \ |
| 268 | + --resource-group <resource-group> \ |
| 269 | + --vnet-name <vnet-name> \ |
| 270 | + --name <nginxaas-subnet-name> \ |
| 271 | + --route-table <route-table-name> |
| 272 | + ``` |
| 273 | + |
| 274 | +## Testing the configuration |
| 275 | + |
| 276 | +After implementing either solution, test the OIDC authentication: |
| 277 | + |
| 278 | +1. Access your NGINXaaS deployment URL. |
| 279 | +1. Verify you are redirected to Microsoft Entra ID for authentication. |
| 280 | +1. Complete the login process and confirm successful authentication. |
| 281 | +1. Check NGINXaaS logs for any connectivity issues. |
| 282 | + |
| 283 | +### Troubleshooting |
| 284 | + |
| 285 | +If authentication fails, check the following: |
| 286 | + |
| 287 | +1. **DNS Resolution**: Ensure your firewall rules allow DNS queries. |
| 288 | +1. **Certificate Validation**: Verify that the firewall allows HTTPS traffic to Microsoft endpoints. |
| 289 | +1. **Timeout Settings**: Increase timeout values if experiencing slow authentication responses. |
| 290 | +1. **Route Configuration**: Confirm the route table is properly associated with the NGINXaaS subnet. |
| 291 | + |
| 292 | +## To secure your systems, address the following: |
| 293 | + |
| 294 | +- **Principle of Least Privilege**: Both solutions limit outbound connectivity to only required Microsoft endpoints. |
| 295 | +- **Monitoring**: Implement logging and monitoring for authentication traffic. |
| 296 | +- **Regular Updates**: Keep Microsoft IP address ranges updated in your NSG rules. |
| 297 | +- **Network Segmentation**: Consider additional network segmentation for enhanced security. |
| 298 | + |
| 299 | +## To optimize your systems, we recommend: |
| 300 | + |
| 301 | +- **Azure NAT Gateway**: More cost-effective for basic filtering needs. |
| 302 | +- **Azure Firewall**: Better ROI when you need advanced filtering capabilities. |
| 303 | +- **Public IP Management**: Minimize the number of public IP addresses to reduce costs. |
| 304 | + |
| 305 | +Both solutions enable the minimal connectivity required between NGINXaaS and Microsoft Entra ID for OIDC authentication while maintaining security controls appropriate to your requirements. |
| 306 | + |
| 307 | +## See also |
| 308 | + |
| 309 | +- [Set up OIDC authentication]({{< ref "/nginxaas-azure/quickstart/security-controls/oidc/" >}}) |
| 310 | +- [Single Sign-On with Microsoft Entra ID]({{< ref "/nginx/deployment-guides/single-sign-on/entra-id.md" >}}) |
| 311 | +- [Private Link to Upstreams]({{< ref "/nginxaas-azure/quickstart/security-controls/private-link-to-upstreams.md" >}}) |
| 312 | +- [Microsoft 365 URLs and IP address ranges](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges) |
0 commit comments