在Linux上用Nginx部署ASP.NET Core

翻译Host ASP.NET Core on Linux with Nginx

在Ubuntu 16.04服务器上配置一个production-ready的ASP.NET Core环境。

  • 把一个已经存在的ASP.NET Core应用放在一个反向代理服务器后面。

  • 配置一个反向代理服务器来发送请求到Kestrel网络服务器。

  • 确保网络应用在系统启动时作为Daemon运行

  • 配置一个进程管理工具来帮助重启网络应用

先决条件

  • 拥有一个sudo权限的标准用户可以访问Ubunut 16.04服务器

  • 一个已经存在的ASP.NET Core应用

复制应用

从开发环境运行dotnet publish来把一个应用打包到一个self-contained目录里,这样它可以在服务器上运行。

用一些工具(SCP, FTP)把ASP.NET Core应用拷贝到服务器。测试应用,例如:

  • 从命令行运行donet <app_assembly>.dll

  • 在浏览器中,导航到http://<serveraddress>:<port>来确认应用在Linux上工作。

配置一个反向代理服务器

反向代理是一个通用的设置,用来服务动态网络应用。反向代理终止HTTP请求并且把它发送到ASP.NET Core应用中。

为什么使用反向代理服务器?

Kestrel是一个ASP.NET Core应用,可以很好的服务动态内容的。然而,网络服务功能不如IIS, Apache或者Nginx丰富。反向代理服务器可以减轻HTTP服务器的工作,比如服务静态内容,缓存请求,压缩请求和SSL termination。反向代理服务器可能在一个专用的机器,也可能被部署在HTTP服务器上。

这篇文章,Nginx和HTTP服务运行在一台服务器上。

因为请求被反向代理发送,从Microsoft.AspNetCore.HttpOverrides包来使用Forwarded Headers中间件。这个中间件更新Request.Scheme, 使用X-Forwarded-Proto头,以便重定向链接和其他的安全策略能正确的工作。

当使用任何认证的中间件时,Forwarded Headers中间件必须首先运行。 这个顺序确保认证中间件可以销毁头的值并且生成正确的重定向链接。

在调用UseAuthentication或者类似的认证中间件前,在Startup.Configure中调用UseForwardedHeaders方法。配置中间件发送X-Forwarded-For和X-Forwarded-Proto头。

//ASP.NET Core 2.x
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

app.UseAuthentication();

如果ForwardedHeadersOptions中没有被指定的中间件,发送的默认的头是空。

安装Nginx

sudo apt-get install nginx

如果需要有选择的安装Nginx模块,可能必须需要从源码去build。

使用apt-get来安装Nginx。安装程序会创建一个系统的初始化的脚本,用来在系统启动时,Nginx可以作为Daemon运行起来。因为Nginx是第一次被安装,需要明确的启动它:

sudo service nginx start

确认浏览器显示Nginx默认的着陆页。

配置Nginx

为了配置Nginx作为反向代理来发送请求给你的ASP.NET Core应用,修改/etc/nginx/sites-available/default。用文本编辑器打开它,把它的内容替换为下面的内容:

server {
    listen        80;
    server_name   example.com *.example.com;
    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;
    }
}

当没有server_name匹配,Nginx会使用default server。如果没有定义default server,在配置文件里的第一个server就是默认服务器。最佳实践是,在你的配置文件里添加一个指定的默认服务器,返回状态码为444。默认服务器配置例子:

server {
    listen   80 default_server;
    # listen [::]:80 default_server deferred;
    return   444;
}

用了前面的配置文件和默认服务器,Nginx接受80端口的Host头是example.com或者*.example.com的流量。不匹配这些hosts的请求不会被发送到Kestrel。Nginx发送匹配的请求给http://localhost:5000Nginx是如何处理请求的

一旦Nginx配置建好了,运行sudo nginx -t来确认配置文件的语法。如果配置文件语法测试成功了,运行sudo nginx -s reload来让Nginx加载这些改变。

监控应用

这个服务器可以把http://<serveraddress>:80的请求发送到运行在http://127.0.0.1:5000的ASP.NET Core应用。然而,Nginx没有管理Kestrel进程。可以使用systemd来创建一个服务文件,启动并且监控后台运行的网络应用。Systemd是一个初始化系统,提供了很多有用的功能用来启动,停止和管理进程。

创建服务文件

创建一个定义服务的文件:

sudo vi /etc/systemd/system/kestrel-hellomvc.service

下面是一个应用的服务文件的例子:

[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

注意: 如果配置文件里没有使用www-data用户,那么这里定义的用户必须先被创建并且给予合适的权限。Linux是一个大小写敏感的文件系统。把ASPNETCORE_ENVIRONMENT设置为"Production",应用会寻找appsettings.Production.json配置文件,而不是appsettings.production.json。

保存文件并且激活服务

systemctl enable kestrel-hellomvc.service

启动服务并且确认它正在运行。

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

反向代理配置好了并且Kestrel通过systemd管理,这个网络应用就配置完了并且可以从本机的浏览器http://localhost来访问。禁用任何可能阻塞的防火墙配置,它也可以被远程的机器访问。分析响应头,这个服务头显示ASP.NET Core应用被Kestrel服务。

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

查看日志

因为Kestrel网络应用使用systemd管理,所有的事件和进程被记录到一个中心日志。然而,这个日志包含了所有的被systemd管理的服务和进程。为了看kestrel-hellomvc.service-specific的日志,使用下面的命令:

sudo journalctl -fu kestrel-hellomvc.service

为了进一步过滤,时间选项--since today--until 1 hour ago或者这些的组合都可以减少返回的内容。

sudo journalctl -fu kestrel-hellomvc.service --since "2016-10-18" --until "2016-10-18 04:00"

应用安全

激活AppArmor

Linux Security Modules (LSM)是一个框架,从Linux 2.6以后成文Linux内核的一部分。LSM支持不同的安全模块的实现。AppArmor 是一个实现了管制程序访问有限资源的系统。确保AppArmor被激活并且正确的配置。

配置防火墙

关闭所有的没有被使用的多余的端口。Uncomplicated firewall (ufw)提供了一个iptables的前端,通过提供命令行接口来配置防火墙。确保ufw的配置允许需要的端口被打开。

sudo apt-get install ufw
sudo ufw enable

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Nginx的安全

默认的Nginx没有激活SSL。为了激活额外的安全功能,可以从源码build。

下载源码并且安装build的依赖

# 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
改变Nginx响应名字
Edit src/http/ngx_http_header_filter_module.c:

static char ngx_http_server_string[] = "Server: Web Server" CRLF;
static char ngx_http_server_full_string[] = "Server: Web Server" CRLF;
配置可选项和build

PCRE库对正则表达式是必须的。正则表达式在location指令中给ngx_http_rewrite_module使用。http_ssl_module添加了HTTPS协议的支持。

可以考虑使用一个网络应用防火墙(ModSecurity)来加强应用的安全。

./configure
--with-pcre=../pcre-8.38
--with-zlib=../zlib-1.2.8
--with-http_ssl_module
--with-stream
--with-mail=dynamic
配置SSL
  • 通过指定一个有效的,由一个可信任证书权威签发的证书,配置服务器来监听在443端口的HTTPS流量。

  • 在/etc/nginx/nginx.conf文件中添加配置,比如选择一个更强的cipher并且把HTTP全部重定向到HTTPS。

  • 添加一个HTTP Strict-Transport-Security (HSTS)头来确保所有的由客户端产生的请求只能通过HTTPS。

  • 如果将来要禁用SSL,就不添加Strict-Transport-Security头或者选择一个合适的max-age

添加/etc/nginx/proxy.conf配置文件:

proxy_redirect          off;
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;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;

编辑/etc/nginx/nginx.conf配置文件。例子包含了http和server部分:

http {
    include    /etc/nginx/proxy.conf;
    limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
    server_tokens off;

    sendfile on;
    keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case.
    client_body_timeout 10; client_header_timeout 10; send_timeout 10;

    upstream hellomvc {
        server localhost:5000;
    }

    server {
        listen *:80;
        add_header Strict-Transport-Security max-age=15768000;
        return 301 https://$host$request_uri;
    }

    server {
        listen *:443    ssl;
        server_name     example.com;
        ssl_certificate /etc/ssl/certs/testCert.crt;
        ssl_certificate_key /etc/ssl/certs/testCert.key;
        ssl_protocols 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; #ensure your cert is capable
        ssl_stapling_verify on; #ensure your cert is capable

        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        #Redirects all traffic
        location / {
            proxy_pass  http://hellomvc;
            limit_req   zone=one burst=10 nodelay;
        }
    }
}
点击劫持

Clickjacking是一个恶意的技术,用来收集受感染的用户的点击。Clickjacking欺骗受害者点进受感染的网站。使用X-FRAME-OPTIONS来使网站安全。

编辑nginx.conf文件:

sudo vi /etc/nginx/nginx.conf

添加add_header X-Frame-Options "SAMEORIGIN"这行并且保存文件,然后重启Nginx。

MIME-type嗅探

这个头阻止了大部分浏览器从MIME-sniffing一个远离声明类型的响应,因为这个头指明浏览器不会覆盖响应内容类型。用了nosniff选项,如果这个服务器说内容是"text/html", 浏览器会把内容作为"text/html"发送。

编辑nginx.conf文件:

sudo vi /etc/nginx/nginx.conf

添加add_header X-Content-Type-Options "nosniff"这一行并且保存文件,然后重启Nginx。


Liunx ASP.NET Core Nginx

这篇文章: 有用 无用
创建 2018-04-25 02:35:44 / 更新 2018-04-25 02:54:13