I have a Docker infrastructure that uses Nginx 1.27 to serve static files and act as reverse proxy for several related services. Access to different locations is straightforward and is controlled by ensuring the mTLS-provided client certificate passes CA validation and that the metadata in the certificate meets certain requirements.
Everything works fine in development or on cloud VMs using ssl_verify_client
and friends. A challenge is arising trying to migrate these containers to Azure Container Apps which uses Envoy Proxy to terminate TLS connections. Very few configuration options for Envoy Proxy are exposed to end users. It's possible pass connections through Envoy to Nginx so it can handle TLS termination as desired, but they explicitly block this pass-through behavior for ports 80 and 443. I'm trying to run basically everything over 443. Azure provides an option to pass an X-Forwarded-Client-Cert
to the target container (Nginx) if mTLS is used.
I've looked into several options to try to overcome this including an additional proxy, adding load balancers etc., but everything involving the deployment of additional infrastructure looks increasingly complex and kludgy, and invalidates some of the benefits of a managed container service. The Azure docs say to update your applications to read and process the X-Forwarded-Client-Cert
HTTP header.
Reading through the nginx documents, I can't find a good way to validate a client certificate that comes through the X-Forwarded-Client-Cert
HTTP header for cases like this where Nginx isn't allowed to terminate the TLS connection itself.
One approach that comes up is installing the lua-nginx-module
and writing a Lua script to perform the client certificate validation. I've not written Lua before and this seems rather error-prone process for an issue that seems like it should be fairly common.
Is there a better way to handle this than preparing a Lua module. If not, is there something I can use that has already been tested and vetted?
I have a Docker infrastructure that uses Nginx 1.27 to serve static files and act as reverse proxy for several related services. Access to different locations is straightforward and is controlled by ensuring the mTLS-provided client certificate passes CA validation and that the metadata in the certificate meets certain requirements.
Everything works fine in development or on cloud VMs using ssl_verify_client
and friends. A challenge is arising trying to migrate these containers to Azure Container Apps which uses Envoy Proxy to terminate TLS connections. Very few configuration options for Envoy Proxy are exposed to end users. It's possible pass connections through Envoy to Nginx so it can handle TLS termination as desired, but they explicitly block this pass-through behavior for ports 80 and 443. I'm trying to run basically everything over 443. Azure provides an option to pass an X-Forwarded-Client-Cert
to the target container (Nginx) if mTLS is used.
I've looked into several options to try to overcome this including an additional proxy, adding load balancers etc., but everything involving the deployment of additional infrastructure looks increasingly complex and kludgy, and invalidates some of the benefits of a managed container service. The Azure docs say to update your applications to read and process the X-Forwarded-Client-Cert
HTTP header.
Reading through the nginx documents, I can't find a good way to validate a client certificate that comes through the X-Forwarded-Client-Cert
HTTP header for cases like this where Nginx isn't allowed to terminate the TLS connection itself.
One approach that comes up is installing the lua-nginx-module
and writing a Lua script to perform the client certificate validation. I've not written Lua before and this seems rather error-prone process for an issue that seems like it should be fairly common.
Is there a better way to handle this than preparing a Lua module. If not, is there something I can use that has already been tested and vetted?
Share Improve this question asked Mar 14 at 19:36 Jeff12Jeff12 356 bronze badges 2- 1 There are two main approaches to handling X-Forwarded-Client-Cert in Nginx. Option 1- Instead of using Lua scripting, you can delegate certificate validation to an external authentication service using Nginx’s auth_request directive. and Option 2- If you must perform validation inside Nginx without an external service, you can use the Lua module (lua-nginx-module). – Arko Commented Mar 18 at 10:19
- Option 1 is good as Nginx forwards the X-Forwarded-Client-Cert to an authentication service. The service validates the certificate. If valid, Nginx allows access otherwise access is denied. – Arko Commented Mar 18 at 10:20
1 Answer
Reset to default 0As I mentioned in the comment section, when running Nginx within Container Apps, it's somewhat different environment where TLS termination happens through the Envoy proxy ahead of your container, rather than directly by Nginx. Therefore, ssl_verify_client can't be used to do client certificate (mTLS) verification since Nginx never gets to see the actual TLS handshake.
Azure has a workaround of passing the client certificate information through the `X-Forwarded-Client-Cert` HTTP header. Your application (or Nginx) now needs to verify the certificate against this header value.
You have two options as a workaround-
Option 1- Use external authentication service and have Nginx forward cert validation using the auth_request directive i.e. Nginx will get the incoming request and pulls out the X-Forwarded-Client-Cert header and sends an internal sub request to an authentication service. That service validates and parses the certificate (e.g. checks issuer, expiry, and optionally if it's signed by a trusted CA). If valid, the service returns 200 OK, and Nginx permits the request. If invalid, the service returns 401 Unauthorized, and Nginx denies the request.
Example-
location /secure/ {
auth_request /validate_client_cert;
proxy_pass http://your_backend_service;
}
location = /validate_client_cert {
internal;
proxy_pass http://your_auth_service/validate;
proxy_set_header X-Forwarded-Client-Cert $http_x_forwarded_client_cert;
proxy_pass_request_body off;
}
Option 2- If running a standalone auth service is not possible, the second best choice is to use [lua-nginx-module](https://github/openresty/lua-nginx-module) in order to validate the certificate within Nginx itself. This one offers total control, but is slightly complicated. I would suggest option 1.
MS doc- [Ingress in Azure Container Apps](https://learn.microsoft/en-us/azure/container-apps/ingress-overview#tls-termination)