Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No certificate in X-ARR-ClientCert header when called from Azure API Management

I have configured an Azure website (with one ApiController) to use client-certificate authentication using the instructions provided here. Summarizing: you set the clientCertEnabled flag to true and from then on your website starts asking for a client-authentication-certificate.

Works great, however, now I want to access the client certificate that is sent to the server. According to the MSDN article, it should be available in the X-ARR-ClientCert request header, except that it isn't!!

This means that anyone with a client-authentication-certificate can access my API, which is undesirable in my case.

So how do I retrieve the client-authentication-certificate that a client sends to my Web API?

UPDATE 1: I'm actually calling my API through Azure API Management. I configured APIM with my client-authentication-certificate and APIM calls my API without issues. However, when the API is called from APIM, no X-ARR-ClientCert header is set. When called directly via Fiddler, I do see the header. So APIM is calling my API in some different way?!?

UPDATE 2: I went through everything again and produced some logging. First the relevant part of the DelegatingHandler class I'm logging from:

protected override async Task<HttpResponseMessage>
    SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  Trace.TraceInformation("Going to validate client certificate.");

  var x509Certificate2 = request.GetClientCertificate();
  Trace.TraceInformation("Client cert: {0}", x509Certificate2 == null
    ? "<null>"
    : x509Certificate2.Subject);

  try
  {
    var headerKeys = string.Join("|", request.Headers.Select(h => h.Key));
    Trace.TraceInformation("Header keys: {0}", headerKeys);
  ...

And the resulting log output:

2015-12-07T08:08:24  PID[8464] Information Going to validate client certificate.
2015-12-07T08:08:24  PID[8464] Information Client cert: <null>
2015-12-07T08:08:24  PID[8464] Information Header keys:
  Connection|Host|Max-Forwards|Conf-Organisation-Key|Ocp-Apim-Subscription-Key|
  X-Forwarded-For|X-LiveUpgrade|X-ARR-LOG-ID|DISGUISED-HOST|X-SITE-DEPLOYMENT-ID|
  X-Original-URL

So there's no client certificate and also no X-ARR-ClientCert header.

UPDATE 3: And here's the log that results when I go to my actual API directly with a client-authentication-certificate:

2015-12-07T09:16:45  PID[8464] Information Going to validate client certificate.
2015-12-07T09:16:45  PID[8464] Information Client cert: [email protected]
2015-12-07T09:16:45  PID[8464] Information Header keys:
  Connection|Accept|Accept-Encoding|Accept-Language|Cookie|Host|Max-Forwards|
  User-Agent|Upgrade-Insecure-Requests|DNT|X-LiveUpgrade|X-ARR-LOG-ID|DISGUISED-HOST|
  X-SITE-DEPLOYMENT-ID|X-Original-URL|X-Forwarded-For|X-ARR-SSL|X-ARR-ClientCert

Both a client certificate directly from the request and the expected X-ARR-ClientCert header.

UPDATE 4: In the end this happened to be my own mistake (of course). I was convinced that the url for the backend was https while in fact it was http. Client certificate authentication only works over https so in hindsight it made perfect sense no certificate was found in the backend...

like image 212
Ronald Wildenberg Avatar asked Dec 03 '15 16:12

Ronald Wildenberg


3 Answers

You also need to enable client certificate authentication for the Azure hosted API - this step will ensure Azure carries over any X-ARR-ClientCert headers coming on the incoming request.

Otherwise Azure removes the X-ARR-ClientCert header from Request.Headers before it reaches your API.

Note: this setup is only available for paid subscriptions

  1. Go to https://resources.azure.com/ and select your desired azure account
  2. Select subscriptions -> resource groups -> your_resource_group -> providers -> microsoft.web -> sites -> your_website
  3. Enter in Read/Write mode from top button, enter in Edit mode then set the property "clientCertEnabled": true under properties.
"properties": {
    "name": "my-site",
    "state": "Running",
    "hostNames": [
      "my-site.azurewebsites.net",
      "(string)"
    ],
    ...
    "clientCertEnabled": true,
    ...
 }

Documentation: https://learn.microsoft.com/en-us/azure/app-service-web/app-service-web-configure-tls-mutual-auth

like image 165
mihai_omega Avatar answered Oct 18 '22 04:10

mihai_omega


Do you really want to manage client certificates for all users of the API? I understand using a client-cert to ensure only APIM can talk directly to your backend API. Usually developers who expose APIs via API Management use API keys to control access to the API. Using this approach enables policies to be applied based on different configured "products".

If you have the infrastructure in place to manage cert creation and revocation then this might be the right choice, it is just an uncommon approach. Having said that I will investigate what options are available in API Management to be able to extract the certificate thumbprint on the APIM gateway.

like image 31
Darrel Miller Avatar answered Oct 18 '22 05:10

Darrel Miller


I am contacting the engineers in APIM to figure out how this is intended to work and will get back to you. When you have APIM in front of your actual API (web app), my guess is that APIM is the proxy that will take care of all the authN / authZ for you. So requests would go to your API only when this succeeds. I am guessing that is why they probably just drop the client certificate instead of forwarding it onward. But I can definitely see why the client cert would still be useful in the web app.

like image 22
NazimL - MSFT Avatar answered Oct 18 '22 05:10

NazimL - MSFT