Hosting ASP.NET Core 2.0 Web Api on Azure Ubuntu Server with Nginx and Mutual SSL Authentication (PART 5)
PART 5: Mutual SSL Setup for Client Authentication and Passing Client Certificate Data to Asp.Net Core App
In this post series, I will step by step show you how to host a ASP.NET Core 2.0 Web Api which is using mutual SSL authentication on a Azure Ubuntu Server using Nginx as the reverse proxy server and Kestrel as the default application server.
In this part we will see how to setup mutual SSL for client authentication and passing client certificate data to Asp.Net Core Web Api using HTTP headers.
For information about how to deploy an Ubuntu Server in Azure check part-1 of this series.
For information about how to setup .Net Core SDK 2.0 and Nginx on Ubuntu Linux check part-2 of this series.
For information about how to configure Azure Network Security Group and Publishing Asp.Net Core Web Api check part-3 of this series.
For information about how to setup a service to manage kestrel process, creating self-signed SSL certificate and configuring Nginx as a reverse proxy server check part-4 of this series.
Mutual SSL Setup for Client Authentication
Copy the client certificate to the following folder:
/etc/ssl/ca/certs/
In my case I create a bundle certificate (Bundle.crt) by putting the intermediate and root CA certificates into one crt PEM file. This allows Nginx to authenticate all client certificates belongs to that CA chain.
Open the Nginx config file with Nano editor:
sudo nano /etc/nginx/sites-available/default
Add the following statements in the config and save/exit:
ssl_client_certificate /etc/ssl/ca/certs/Bundle.crt;ssl_verify_client on;ssl_verify_depth 2;
Verify configuration changes in Nginx:
sudo nginx -t
It should display:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Restart Nginx to implement our changes:
sudo systemctl restart nginx
Now, when you want to access your api from your client machine the browser will prompt for client certificate for this connection. Unless you provide a valid certificate you will get a HTTP 400 result.
Note: To test the mutual ssl by using “curl” command you can use something like this:
curl -v -k -key /etc/ssl/private/client.key -cert /etc/ssl/certs/client.crt https://webapi.westeurope.cloudapp.azure.com/api/values
Passing Client Certificate Data to Asp.Net Core App
Since Nginx is working as a reverse proxy server. Our web api application is not aware of the outer world. For example, the authenticated user client certificate is not accessible with the current configuration. But we can change that.
The client certificate and many other data can be transferred from Nginx to Kestrel via http headers. This configuration is done in the Nginx config.
Some embedded variables that can be used for certificate information are $ssl_client_cert; $ssl_client_s_dn; $ssl_client_i_dn; The whole can be found in Nginx documentation.
However, in the current version of Nginx (1.13.3) the supplied certificate value is not suitable for http headers. It might have been fixed in the Nginx 1.13.5 version with the $ssl_client_escaped_cert variable but I haven’t tested it yet.
To solve this problem we will use some extra modules in Nginx.
Open the Nginx config file with Nano editor:
sudo nano /etc/nginx/sites-available/default
Add the following statements in the config to the location attribute and save/exit:
set_by_lua $client_cert "return ngx.var.ssl_client_raw_cert and ngx.var.ssl_client_raw_cert:gsub('\\n',' ') or nil";proxy_set_header X-Client-Cert $client_cert;
This removes the new lines in the certificate file and converts the certificate data into a one line long string. Therefore, http header can safely carry this data to the application server.
To make it clear, I will put the whole Nginx config file which includes all configurations so far:
server {
listen 80;
rewrite ^ https://$host$request_uri permanent;
}server {
listen 443 ssl;
server_name webapi.westeurope.cloudapp.azure.com;
ssl on;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_client_certificate /etc/ssl/ca/certs/Bundle.crt;
ssl_verify_client on;
ssl_verify_depth 2;
error_log /var/log/nginx/debugnginx.log debug;location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
set_by_lua $client_cert "return ngx.var.ssl_client_raw_cert and ngx.var.ssl_client_raw_cert:gsub('\\n',' ') or nil";
proxy_set_header X-Client-Cert $client_cert;
proxy_cache_bypass $http_upgrade;
}
}
Reading Client Certificate from HTTP Headers in the Asp.Net Core 2.0 Web Api
At the moment Nginx is set up as a reverse proxy server and handling client authentication by using mutual SSL. However, this means Kestrel and our .Net Core Web Api does not have any information about the client certificates. To solve this issue, we have set up HTTP headers in the Nginx config to be sent to the application server.
To read the client certificate from HTTP headers first we have to configure .Net Core to use forwarded headers. This can be enabled in the “Configure” method of Startup class.
Open your web api project and go to Startup class in Startup.cs file. Then change the Configure method by adding “ForwardedHeadersOptions”:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var forwardedHeadersOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All,
RequireHeaderSymmetry = false,
ForwardLimit = null,
};
forwardedHeadersOptions.KnownNetworks.Clear();
forwardedHeadersOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardedHeadersOptions);if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}app.UseMvc();
}
You also need to add “ using Microsoft.AspNetCore.HttpOverrides; “ to the Startup.cs to use the ForwardedHeadersOptions.
That’s the only configuration needed. Now you can get the header values as usual like this:
Request.Headers["X-Client-Cert"]
That’s it.
This completes the series of hosting ASP.NET Core 2.0 Web Api on Azure Ubuntu Server with Nginx and Mutual SSL Authentication.
Hope it helps!
Originally published at https://www.weboideas.com on November 27, 2017.