307 lines
12 KiB
Markdown
307 lines
12 KiB
Markdown
---
|
||
title: Host ASP.NET Core on Linux with nginx
|
||
description: Describes how to setup nginx as a reverse proxy on Ubuntu 16.04 to forward HTTP traffic to an ASP.NET Core web app running on Kestrel.
|
||
author: rick-anderson
|
||
ms.author: riande
|
||
manager: wpickett
|
||
ms.custom: mvc
|
||
ms.date: 08/21/2017
|
||
ms.topic: article
|
||
ms.technology: aspnet
|
||
ms.prod: asp.net-core
|
||
uid: host-and-deploy/linux-nginx
|
||
---
|
||
# Host ASP.NET Core on Linux with nginx
|
||
|
||
By [Sourabh Shirhatti](https://twitter.com/sshirhatti)
|
||
|
||
This guide explains setting up a production-ready ASP.NET Core environment on an Ubuntu 16.04 Server.
|
||
|
||
**Note:** For Ubuntu 14.04, *supervisord* is recommended as a solution for monitoring the Kestrel process. *systemd* isn't available on Ubuntu 14.04. [See previous version of this document](https://github.com/aspnet/Docs/blob/e9c1419175c4dd7e152df3746ba1df5935aaafd5/aspnetcore/publishing/linuxproduction.md)
|
||
|
||
This guide:
|
||
|
||
* Places an existing ASP.NET Core app behind a reverse proxy server.
|
||
* Sets up the reverse proxy server to forward requests to the Kestrel web server.
|
||
* Ensures the web app runs on startup as a daemon.
|
||
* Configures a process management tool to help restart the web app.
|
||
|
||
## Prerequisites
|
||
|
||
1. Access to an Ubuntu 16.04 Server with a standard user account with sudo privilege
|
||
1. An existing ASP.NET Core app
|
||
|
||
## Copy over the app
|
||
|
||
Run `dotnet publish` from the dev environment to package an app into a self-contained directory that can run on the server.
|
||
|
||
Copy the ASP.NET Core app to the server using whatever tool integrates into the organization's workflow (for example, SCP, FTP). Test the app, for example:
|
||
|
||
* From the command line, run `dotnet <app_assembly>.dll`.
|
||
* In a browser, navigate to `http://<serveraddress>:<port>` to verify the app works on Linux.
|
||
|
||
## Configure a reverse proxy server
|
||
|
||
A reverse proxy is a common setup for serving dynamic web apps. A reverse proxy terminates the HTTP request and forwards it to the ASP.NET Core app.
|
||
|
||
### Why use a reverse proxy server?
|
||
|
||
Kestrel is great for serving dynamic content from ASP.NET Core; however, the web serving parts aren’t as feature rich as servers like IIS, Apache, or nginx. A reverse proxy server can offload work like serving static content, caching requests, compressing requests, and SSL termination from the HTTP server. A reverse proxy server may reside on a dedicated machine or may be deployed alongside an HTTP server.
|
||
|
||
For the purposes of this guide, a single instance of nginx is used. It runs on the same server, alongside the HTTP server. Based on requirements, a different setup may be choosen.
|
||
|
||
Because requests are forwarded by reverse proxy, use the `ForwardedHeaders` middleware from the `Microsoft.AspNetCore.HttpOverrides` package. This middleware updates `Request.Scheme`, using the `X-Forwarded-Proto` header, so that redirect URIs and other security policies work correctly.
|
||
|
||
When setting up a reverse proxy server, the authentication middleware needs `UseForwardedHeaders` to run first. This ordering ensures that the authentication middleware can consume the affected values and generate correct redirect URIs.
|
||
|
||
# [ASP.NET Core 2.x](#tab/aspnetcore2x)
|
||
|
||
Invoke the `UseForwardedHeaders` method (in the `Configure` method of *Startup.cs*) before calling `UseAuthentication` or similar authentication scheme middleware:
|
||
|
||
```csharp
|
||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||
{
|
||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||
});
|
||
|
||
app.UseAuthentication();
|
||
```
|
||
|
||
# [ASP.NET Core 1.x](#tab/aspnetcore1x)
|
||
|
||
Invoke the `UseForwardedHeaders` method (in the `Configure` method of *Startup.cs*) before calling `UseIdentity` and `UseFacebookAuthentication` or similar authentication scheme middleware:
|
||
|
||
```csharp
|
||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||
{
|
||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||
});
|
||
|
||
app.UseIdentity();
|
||
app.UseFacebookAuthentication(new FacebookOptions()
|
||
{
|
||
AppId = Configuration["Authentication:Facebook:AppId"],
|
||
AppSecret = Configuration["Authentication:Facebook:AppSecret"]
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### Install nginx
|
||
|
||
```bash
|
||
sudo apt-get install nginx
|
||
```
|
||
|
||
> [!NOTE]
|
||
> If optional nginx modules will be installed, building nginx from source might be required.
|
||
|
||
Use `apt-get` to install nginx. The installer creates a System V init script that runs nginx as daemon on system startup. Since nginx was installed for the first time, explicitly start it by running:
|
||
|
||
```bash
|
||
sudo service nginx start
|
||
```
|
||
|
||
Verify a browser displays the default landing page for nginx.
|
||
|
||
### Configure nginx
|
||
|
||
To configure nginx as a reverse proxy to forward requests to our ASP.NET Core app, modify `/etc/nginx/sites-available/default`. Open it in a text editor, and replace the contents with the following:
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
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 $http_host;
|
||
proxy_cache_bypass $http_upgrade;
|
||
}
|
||
}
|
||
```
|
||
|
||
This nginx configuration file forwards incoming public traffic from port `80` to port `5000`.
|
||
|
||
Once the nginx configuration is established, run `sudo nginx -t` to verify the syntax of the configuration files. If the configuration file test is successful, force nginx to pick up the changes by running `sudo nginx -s reload`.
|
||
|
||
## Monitoring the app
|
||
|
||
The server is setup to forward requests made to `http://<serveraddress>:80` on to the ASP.NET Core app running on Kestrel at `http://127.0.0.1:5000`. However, nginx is not set up to manage the Kestrel process. *systemd* can be used to create a service file to start and monitor the underlying web app. *systemd* is an init system that provides many powerful features for starting, stopping, and managing processes.
|
||
|
||
### Create the service file
|
||
|
||
Create the service definition file:
|
||
|
||
```bash
|
||
sudo nano /etc/systemd/system/kestrel-hellomvc.service
|
||
```
|
||
|
||
The following is an example service file for the app:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Example .NET Web API App running on Ubuntu
|
||
|
||
[Service]
|
||
WorkingDirectory=/var/aspnetcore/hellomvc
|
||
ExecStart=/usr/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
|
||
Restart=always
|
||
RestartSec=10 # Restart service after 10 seconds if dotnet service crashes
|
||
SyslogIdentifier=dotnet-example
|
||
User=www-data
|
||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
**Note:** If the user *www-data* is not used by the configuration, the user defined here must be created first and given proper ownership for files.
|
||
**Note:** Linux has a case-sensitive file system. Setting ASPNETCORE_ENVIRONMENT to "Production" results in searching for the configuration file *appsettings.Production.json*, not *appsettings.production.json*.
|
||
|
||
Save the file and enable the service.
|
||
|
||
```bash
|
||
systemctl enable kestrel-hellomvc.service
|
||
```
|
||
|
||
Start the service and verify that it is running.
|
||
|
||
```
|
||
systemctl start kestrel-hellomvc.service
|
||
systemctl status kestrel-hellomvc.service
|
||
|
||
● kestrel-hellomvc.service - Example .NET Web API App running on Ubuntu
|
||
Loaded: loaded (/etc/systemd/system/kestrel-hellomvc.service; enabled)
|
||
Active: active (running) since Thu 2016-10-18 04:09:35 NZDT; 35s ago
|
||
Main PID: 9021 (dotnet)
|
||
CGroup: /system.slice/kestrel-hellomvc.service
|
||
└─9021 /usr/local/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
|
||
```
|
||
|
||
With the reverse proxy configured and Kestrel managed through systemd, the web app is fully configured and can be accessed from a browser on the local machine at `http://localhost`. It is also accessible from a remote machine, barring any firewall that might be blocking. Inspecting the response headers, the `Server` header shows the ASP.NET Core app being served by Kestrel.
|
||
|
||
```text
|
||
HTTP/1.1 200 OK
|
||
Date: Tue, 11 Oct 2016 16:22:23 GMT
|
||
Server: Kestrel
|
||
Keep-Alive: timeout=5, max=98
|
||
Connection: Keep-Alive
|
||
Transfer-Encoding: chunked
|
||
```
|
||
|
||
### Viewing logs
|
||
|
||
Since the web app using Kestrel is managed using `systemd`, all events and processes are logged to a centralized journal. However, this journal includes all entries for all services and processes managed by `systemd`. To view the `kestrel-hellomvc.service`-specific items, use the following command:
|
||
|
||
```bash
|
||
sudo journalctl -fu kestrel-hellomvc.service
|
||
```
|
||
|
||
For further filtering, time options such as `--since today`, `--until 1 hour ago` or a combination of these can reduce the amount of entries returned.
|
||
|
||
```bash
|
||
sudo journalctl -fu kestrel-hellomvc.service --since "2016-10-18" --until "2016-10-18 04:00"
|
||
```
|
||
|
||
## Securing the app
|
||
|
||
### Enable AppArmor
|
||
|
||
Linux Security Modules (LSM) is a framework that is part of the Linux kernel since Linux 2.6. LSM supports different implementations of security modules. [AppArmor](https://wiki.ubuntu.com/AppArmor) is a LSM that implements a Mandatory Access Control system which allows confining the program to a limited set of resources. Ensure AppArmor is enabled and properly configured.
|
||
|
||
### Configuring the firewall
|
||
|
||
Close off all external ports that are not in use. Uncomplicated firewall (ufw) provides a front end for `iptables` by providing a command line interface for configuring the firewall. Verify that `ufw` is configured to allow traffic on any ports needed.
|
||
|
||
```bash
|
||
sudo apt-get install ufw
|
||
sudo ufw enable
|
||
|
||
sudo ufw allow 80/tcp
|
||
sudo ufw allow 443/tcp
|
||
```
|
||
|
||
### Securing nginx
|
||
|
||
The default distribution of nginx doesn't enable SSL. To enable additional security features, build from source.
|
||
|
||
#### Download the source and install the build dependencies
|
||
|
||
```bash
|
||
# Install the build dependencies
|
||
sudo apt-get update
|
||
sudo apt-get install build-essential zlib1g-dev libpcre3-dev libssl-dev libxslt1-dev libxml2-dev libgd2-xpm-dev libgeoip-dev libgoogle-perftools-dev libperl-dev
|
||
|
||
# Download nginx 1.10.0 or latest
|
||
wget http://www.nginx.org/download/nginx-1.10.0.tar.gz
|
||
tar zxf nginx-1.10.0.tar.gz
|
||
```
|
||
|
||
#### Change the nginx response name
|
||
|
||
Edit *src/http/ngx_http_header_filter_module.c*:
|
||
|
||
```c
|
||
static char ngx_http_server_string[] = "Server: Web Server" CRLF;
|
||
static char ngx_http_server_full_string[] = "Server: Web Server" CRLF;
|
||
```
|
||
|
||
#### Configure the options and build
|
||
|
||
The PCRE library is required for regular expressions. Regular expressions are used in the location directive for the ngx_http_rewrite_module. The http_ssl_module adds HTTPS protocol support.
|
||
|
||
Consider using a web app firewall like *ModSecurity* to harden the app.
|
||
|
||
```bash
|
||
./configure
|
||
--with-pcre=../pcre-8.38
|
||
--with-zlib=../zlib-1.2.8
|
||
--with-http_ssl_module
|
||
--with-stream
|
||
--with-mail=dynamic
|
||
```
|
||
|
||
#### Configure SSL
|
||
|
||
* Configure the server to listen to HTTPS traffic on port `443` by specifying a valid certificate issued by a trusted Certificate Authority (CA).
|
||
|
||
* Harden the security by employing some of the practices depicted in the following */etc/nginx/nginx.conf* file. Examples include choosing a stronger cipher and redirecting all traffic over HTTP to HTTPS.
|
||
|
||
* Adding an `HTTP Strict-Transport-Security` (HSTS) header ensures all subsequent requests made by the client are over HTTPS only.
|
||
|
||
* Do not add the Strict-Transport-Security header or chose an appropriate `max-age` if SSL will be disabled in the future.
|
||
|
||
Add the */etc/nginx/proxy.conf* configuration file:
|
||
|
||
[!code-nginx[Main](linux-nginx/proxy.conf)]
|
||
|
||
Edit the */etc/nginx/nginx.conf* configuration file. The example contains both `http` and `server` sections in one configuration file.
|
||
|
||
[!code-nginx[Main](linux-nginx/nginx.conf?highlight=2)]
|
||
|
||
#### Secure nginx from clickjacking
|
||
Clickjacking is a malicious technique to collect an infected user's clicks. Clickjacking tricks the victim (visitor) into clicking on an infected site. Use X-FRAME-OPTIONS to secure the site.
|
||
|
||
Edit the *nginx.conf* file:
|
||
|
||
```bash
|
||
sudo nano /etc/nginx/nginx.conf
|
||
```
|
||
|
||
Add the line `add_header X-Frame-Options "SAMEORIGIN";` and save the file, then restart nginx.
|
||
|
||
#### MIME-type sniffing
|
||
|
||
This header prevents most browsers from MIME-sniffing a response away from the declared content type, as the header instructs the browser not to override the response content type. With the `nosniff` option, if the server says the content is "text/html", the browser renders it as "text/html".
|
||
|
||
Edit the *nginx.conf* file:
|
||
|
||
```bash
|
||
sudo nano /etc/nginx/nginx.conf
|
||
```
|
||
|
||
Add the line `add_header X-Content-Type-Options "nosniff";` and save the file, then restart nginx.
|