Recently, when looking at how to configure authentication using external login providers (e.g. Google, Facebook) with ASP.NET Core I noticed that https is now a requirement for some of them.
Another thing I noticed was how hard it was to find resources about enabling HTTPS on ASP.NET Core.
This blog post is about the process of creating a local ASP.NET Core website running with HTTPS with no errors in Chrome (showing the green padlock in the address bar) for your local development time. And then how you can use Nginx or IIS when you are ready to expose it to the world.
In order to use HTTPS we need a digital certificate. If you need to generate your own to use at development time I recommend Using OpenSSL to Create Certificates. It not only explains how you can create your own certificate authority and certificates, but also how you can configure your browser (Chrome or Firefox) to trust the certificate authority.
Also, if you want to understand what’s the role of certificates in HTTPS, then have a look at Brief(ish) explanation of how https works.
From now on I’m assuming that you have generated your certificate authority, certificate and corresponding private key and you have a .pfx (Personal Information Exchange) file.
Create a new project
I’ll be describing the process using Visual Studio Code and Yeoman.
If you are not familiar with yeoman, it’s a command line tool that allows you to create new projects from a list of project templates. The yeoman generator (that’s the name of the package for yeoman that contains the templates) we are going to use is generator-aspnet. To install yeoman you first need npm and then you can follow the instructions for installing yeoman and the ASP.NET Core templates here.
The reason for using Visual Studio Code and yeoman is that this process works on Windows, Linux and macOS.
To use yeoman with the ASP.NET Core templates run the following command:
$ yo aspnet
_-----_ ╭──────────────────────────╮
| | │ Welcome to the │
|--(o)--| │ marvellous ASP.NET Core │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? What type of application do you want to create? (Use arrow keys)
❯ Empty Web Application
Empty Web Application (F#)
Console Application
Console Application (F#)
Web Application
Web Application Basic [without Membership and Authorization]
Web Application Basic [without Membership and Authorization] (F#)
Select Empty Web Application. A simple web project will be created that just has page with the text “Hello World”.
If you are running Windows and have the full version of Visual Studio you can just do File -> New Project, select .Net Core, ASP.NET Core Web Application and then select Empty.
Configuring HTTPS for Kestrel
Kestrel is the web server that is used by default with ASP.NET Core.
To add HTTPS support to Kestrel add the Microsoft.AspNetCore.Server.Kestrel.Https
package as a dependency.
If you are using project.json you can do this by editing project.json’s “dependencies” section and adding:
"Microsoft.AspNetCore.Server.Kestrel.Https": "1.1.0"
The version number might be different for you, however it should be the same as the one used in the Microsoft.AspNetCore.Server.Kestrel
package.
If you are using .csproj
you can add the dependency by running:
dotnet add package Microsoft.AspNetCore.Server.Kestrel.Https
Certificate Configuration
To configure Kestrel to use our .pfx file (certificate and private key) we need to edit Program.cs
where the WebHostBuilder
is being created and make a few changes:
var host = new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel(options => {
options.UseHttps("localhost.pfx", "password");
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls("https://*:4430")
.UseStartup<Startup>()
.Build();
The changes are:
.UseKestrel(options => {
options.UseHttps("localhost.pfx", "password");
})
Which is where we specify what is the .pfx file that we want to use and its password. UseHttps
is an extension method, if you get an error saying it does not exist it’s because you didn’t add the Microsoft.AspNetCore.Server.Kestrel.Https
nuget package, which is where it lives.
Also:
.UseUrls("https://*:4430")
Which defines where Kestrel will be listening for incoming connections. Alternatively you could use https://localhost:4430
instead of *
. With *
however, you can use any domain name, for example if you have this web application in myDomain.com
it would still work, whereas with localhost
, only requests to localhost
would be served.
In case you are wondering why I did not use the default HTTPS port, 443, that’s because you need special permissions on Linux for ports below 1024. In Windows this would not be a problem. However, because doing this with Kestrel is only recommended when you are at development time, it’s not a big problem.
If you are trying this in Linux, macOS or using the command line in Windows you can call dotnet run
, open Chrome and go to https://localhost:43000
and see a green padlock icon.
If you are using full Visual Studio in Windows the way to do this is to specify that you want to run the web application directly and not through IIS Express:
Also select the browser you want to try it on. If you installed your test root certificate authority’s certificate in a specific browser, use that browser, for example Chrome (if you are confused by this statement I recommend that you read Brief(ish) explanation of how https works and Using OpenSSL to Create Certificates):
Finally, edit the project properties(right click and select properties):
And change the launch url:
Using ASP.NET Core with a reverse proxy
Although it is certainly possible to serve a web application using ASP.NET Core using only Kestrel, it is not recommended. This is because Kestrel is not a fully featured web server and is still lacking some security features.
Even though it is not recommended, you might just want to show something you’ve done to someone and skip the part of installing and configuring a web server. With services like DigitalOcean or Linode you can create a VPS in very little time and just have your ASP.NET Core available through there only having to install .net core.
However, if you want something more serious (and you probably do since you are here because of enabling HTTPS) you should use a full featured web server, like Nginx or IIS (in Windows), as a reverse proxy to your ASP.NET Core application.
A reverse proxy server is a web server that accepts requests and sends them to another web server which actually creates the responses for those requests. The responses are sent back to the proxy server who forwards them to the clients who issued the corresponding requests.
With Nginx acting as a reverse proxy it would look something like this:
Browser <----> Nginx <----> Kestrel
The good thing about this setup is that we can take advantage of Nginx (or IIS) being a full web server and enable HTTPS on it:
Browser <---- HTTPS ---> Nginx <---- HTTP ----> Kestrel
So we don’t even need to configure Kestrel with HTTPS, just Nginx.
Configuring HTTPS with Nginx
First you have to install Nginx. I’m using Ubuntu 16.04 for this, however you can even install it on Windows.
If you are running Ubuntu open a terminal and type
$ sudo apt-get install nginx
Next thing we need to do is to edit Nginx’s configuration. But before we do that it is useful to know what is the location of the configuration files and how they become enabled. The folder where the configuration files are usually placed is /etc/nginx/sites-available/
(this location might be different if you are using a different Linux distribution).
The configuration file that is present when you install Nginx is named default
.
For a Nginx configuration to become enabled it must be located in the folder /etc/nginx/sites-enabled
. So after creating a new configuration file (in sites-available
) a symbolic link is created in sites-enabled
that points to the new file.
Imagine we had just created a new configuration file named my-site-https
in sites-available
. After that you would go to sites-enabled
folder and run the following command to create a symbolic link:
$ ln -s /etc/nginx/sites-available/my-site-htts
To make Nginx read this new configuration run the command:
$ sudo nginx -s reload
Now that we know how to create and enable Nginx configurations let’s create the configuration file for setting up Nginx as a reverse proxy to an ASP.NET Core application that is running at http://localhost:5000
.
First create a new file in /etc/nginx/sites-available
named, for example, aspnetcore
, with the following contents:
server {
listen 443 ssl;
ssl_certificate PATH_TO_CERTIFICATE/CERTIFICATE_NAME.pem;
ssl_certificate_key PATH_TO_PRIVATE_KEY/PRIVATE_KEY.pem;
location / {
proxy_pass http://localhost:5000;
}
}
Here we are configuring Nginx to listen
to port 443 using SSL (443 is the default for HTTPS). Next we are specifying where the certificate (ssl_certificate
) and private key (ssl_certificate_key
) are located and finally we are instructing Nginx to forward all requests (location /
will match all requests) to http://localhost:5000
.
Finally, create the symbolic link to this file in sites-enabled
(you can delete the link to the default
file, this won’t delete the original file since it’s a symbolic link).
/etc/nginx/sites-enabled$ sudo ln -s /etc/nginx/sites-available/aspnetcore
To make Nginx load this configuration:
$ sudo nginx -s reload
If you have your ASP.NET Core application running at http://localhost:5000
you should now be able to open https://localhost
and see it being served over HTTPS.
There’s a common issue that might happen when you reload the Nginx configuration, which is you get prompted for a password. Specifically for private key’s password. This might not be practical for you, so if you want to remove the password from the private key you can run the following command:
$ openssl rsa -in privateKeyWithPassword.pem -out privateKeyWithoutPassword.pem
Another useful thing you can do with Nginx is to have all requests to HTTP be redirected to HTTPS. If you want to enable this add this extra configuration to you aspnetcore
Nginx configuration file:
server {
listen 80;
return 301 https://$host$request_uri;
}
Configuring Supervisor
One thing we must concern ourselves with is that we need to keep our ASP.NET Core web application running.
There’s a tool called supervisor that allows us to configure that an application should be initiated at startup time, and if it crashes it should be brought back up
First thing we need to do is install it. I’m using Ubuntu 16.04 for this, and in this distribution installing it is very simple:
$ sudo apt-get install supervisor
Next thing we need to do is to create a supervisor configuration file for our web application. Let’s call it aspnetcore.conf
and add it to /etc/supervisor/conf.d
(you need to do this using sudo):
Place this inside the configuration file:
[program:aspnetcore]
command=/usr/bin/dotnet PATH_TO_YOUR_PUBLISHED_PROJECT/YOURWEBAPP.dll
directory=PATH_TO_YOUR_PUBLISHED_PROJECT
autostart=true
autorestart=true
stdout_logfile=/var/log/aspnetcore.out.log
stderr_logfile=/var/log/aspnetcore.err.log
environment=ASPNETCORE_ENVIRONMENT="Production"
The first line defines the name (aspnetcore
) by which we can refer to the web application in supervisor.
The line with command
defines the command to run. In this case it’s dotnet
followed by your web application’s dll. This is equivalent to running dotnet run
in the root of your project.
The next line (directory=
) sets the working directory as the one where your project is located.
autostart=true
means that the application will be started at startup time.
autorestart=true
means that the application will be restarted if it crashes or even if it is stopped (for example using kill
). If you want your application to only be restarted in case of a crash change it to autorestart=unexpected
.
The next two lines define to which files the standard and error output are written to, respectively.
Finally environment
allows us to set environment variables, in this case we are setting ASPNETCORE_ENVIRONMENT=Production
.
To enable this new configuration file run the following commands:
$ sudo supervisorctl reread
$ sudo supervisorctl update
You can check the status of the processes running under supervisor by running
$ sudo supervisorctl
It should display something similar to:
aspnetcore RUNNING pid 18817, uptime 0:05:29
supervisor>
There are more several commands you can use to manage the processes under supervisor, type help
for a list of the available commands. To quit, just type quit
.
Configuring HTTPS with IIS
The first step in enabling HTTPS while using IIS is to install our certificate and private key (using a .pfx file that contains both).
Open IIS Manager and select Server Certificates
Click import certificate:
Select the .pfx file and enter the corresponding password, leave the store as Personal:
Before we continue it is important to mention that you need to have the ASP.NET Core Module installed. It is very likely that you already do since it is installed for you when you install the .Net Core SDK.
However, in case it is not (you can verify it in IIS Manager by opening Modules and check if AspNetCoreModule is listed there), you can find installation instructions here and a direct link for the downloading the ASP.NET Core Server Hosting Bundle here.
What this module does is to start your ASP.NET Core website and keep it running (restarting it if it crashes). This module is also responsible for forwarding the HTTP requests to your web app and then forward the responses back to the client.
An important thing to know about the AspNetCoreModule is that it is configured by the web.config
file in the root of your project. You need to run dotnet publish
on your project to get web.config
setup properly in your publish folder.
The next step is to create a new Application Pool and select No Managed Code as the .NET CLR Version. Normally the websites run inside a IIS worker process, but in .NET Core they run as a separate process, so there’s no need for a .Net Runtime in the app pool:
Finally, let’s create a new website inside IIS for our ASP.NET Core application. We need to select the app pool we have just created, select as the Physical Path the path to the published website, select HTTPS as the binding and our certificate in the SSL certificate dropdown:
You should now be able to use https to browse to your ASP.NET Core app.