fix : WAF deployment: private backend ingress and frontend proxy rou…#561
fix : WAF deployment: private backend ingress and frontend proxy rou…#561Ashwal-Microsoft wants to merge 2 commits intodevfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to harden the enablePrivateNetworking=true (“WAF mode”) deployment by preventing browsers from calling the backend Container App endpoint directly, instead routing browser API traffic through the frontend container as an application-layer proxy.
Changes:
- Add an nginx
/api/*reverse proxy in the web container to forward requests to the backend API. - Update infra to make the API ingress internal-only in private networking mode and introduce a private DNS zone for the Container Apps Environment domain.
- Update docs to describe the private networking/WAF traffic flow.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ContentProcessorWeb/nginx-custom.conf | Adds /api/ reverse-proxying from the web container to the backend API URL. |
| src/ContentProcessorWeb/env.sh | Extends APP_* placeholder substitution to /etc/nginx/nginx.conf so nginx can be templated at runtime. |
| infra/main.bicep | Switches CAE to internal + disables public network access in private networking mode; makes API ingress internal-only; adds CAE private DNS zone; updates web env vars for /api base URL. |
| infra/main_custom.bicep | Same as infra/main.bicep for the custom deployment template. |
| docs/TechnicalArchitecture.md | Documents the /api proxy behavior for private networking deployments. |
| docs/DeploymentGuide.md | Documents the expected networking posture and proxying behavior in WAF/private networking deployments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Route browser API calls through the web container so private backend | ||
| # endpoints remain internal-only in WAF/private networking deployments. | ||
| location /api/ { | ||
| proxy_http_version 1.1; | ||
| proxy_set_header Host $host; | ||
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| proxy_set_header X-Forwarded-Proto $scheme; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| proxy_set_header Connection "upgrade"; | ||
| proxy_pass APP_BACKEND_API_URL/; | ||
| } |
There was a problem hiding this comment.
location /api/ relies on proxy_pass APP_BACKEND_API_URL/; being substituted at runtime. If APP_BACKEND_API_URL is not set in the container environment, env.sh will not replace the placeholder and nginx will fail to start due to an invalid proxy_pass URL. Ensure all deployment paths that run this image always set APP_BACKEND_API_URL (even when the app is not using the /api proxy), or adjust the nginx config/entrypoint to provide a safe default when the variable is missing.
| publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' | ||
| internal: enablePrivateNetworking ? true : false |
There was a problem hiding this comment.
Setting the Container Apps Environment to internal: true and publicNetworkAccess: 'Disabled' means the environment (and therefore the web Container App inside it) will be reachable only within the VNet. This conflicts with the template still configuring the web Container App ingress as external (ingressExternal: true later in this file). Either keep the environment external and make only the API app's ingress internal, or also make the web app ingress/internal routing consistent with an internal environment (and document the required WAF/App Gateway in front).
| publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' | |
| internal: enablePrivateNetworking ? true : false | |
| publicNetworkAccess: 'Enabled' | |
| internal: false |
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: enablePrivateNetworking ? false : true |
There was a problem hiding this comment.
With ingressExternal set to false in private networking mode, the API Container App FQDN will no longer be reachable from outside the VNet. The repo's post-deployment scripts currently use CONTAINER_API_APP_FQDN to wait for readiness and register schemas from the deployer's machine; that flow will fail for enablePrivateNetworking=true. Consider updating the post-deployment workflow to call the API through the web app's new /api proxy (using CONTAINER_WEB_APP_FQDN) or to execute the registration from within the VNet (e.g., via the jumpbox).
| ingressExternal: enablePrivateNetworking ? false : true | |
| ingressExternal: true |
| publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' | ||
| internal: enablePrivateNetworking ? true : false |
There was a problem hiding this comment.
Setting the Container Apps Environment to internal: true and publicNetworkAccess: 'Disabled' means the environment (and therefore the web Container App inside it) will be reachable only within the VNet. This conflicts with the template still configuring the web Container App ingress as external (ingressExternal: true later in this file). Either keep the environment external and make only the API app's ingress internal, or also make the web app ingress/internal routing consistent with an internal environment (and document the required WAF/App Gateway in front).
| publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' | |
| internal: enablePrivateNetworking ? true : false | |
| publicNetworkAccess: 'Enabled' | |
| internal: false |
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: enablePrivateNetworking ? false : true |
There was a problem hiding this comment.
With ingressExternal set to false in private networking mode, the API Container App FQDN will no longer be reachable from outside the VNet. The repo's post-deployment scripts currently use CONTAINER_API_APP_FQDN to wait for readiness and register schemas from the deployer's machine; that flow will fail for enablePrivateNetworking=true. Consider updating the post-deployment workflow to call the API through the web app's new /api proxy (using CONTAINER_WEB_APP_FQDN) or to execute the registration from within the VNet (e.g., via the jumpbox).
| ingressExternal: enablePrivateNetworking ? false : true | |
| ingressExternal: true |
…ting
Purpose
When enablePrivateNetworking (WAF mode) is active, the backend Container App URL should not be exposed to the browser. This PR adds an application-layer proxy so the browser only communicates with the frontend App Service.
When deploying these accelerators using the WAF deployment option, the expectation is that the backend APIs hosted on Container Apps are secured behind the WAF and are not directly accessible from the public internet. However, the current implementation leaves the Container App API endpoints publicly exposed, which undermines the security posture of the WAF deployment model.
Expected Behavior
The Web App (frontend) should remain publicly accessible through the WAF/Application Gateway.
All backend Container App API endpoints should be private and only accessible internally (e.g., via Container Apps Environment VNet integration, internal ingress, or Private Endpoints).
External users should not be able to directly call the backend Container App API endpoints from outside the network boundary.
Does this introduce a breaking change?
Golden Path Validation
Deployment Validation
What to Check
Verify that the following are valid
Other Information