使用 Nginx 在 Linux 上托管 ASP.NET CoreHost ASP.NET Core on Linux with Nginx

本文内容

作者:Sourabh Shirhatti

本指南介绍如何在 Ubuntu 16.04 服务器上设置生产就绪 ASP.NET Core 环境。这些说明可能适用于较新版本的 Ubuntu,但尚未使用较新版本进行测试。

有关 ASP.NET Core 支持的其他 Linux 分配的信息,请参阅 Linux 上 .NET Core 的先决条件

备注

对于 Ubuntu 14.04,建议进行监控,以此作为监视 Kestrel 进程的解决方案。在 Ubuntu 14.04 上不提供 systemd 。有关 Ubuntu 14.04 的说明,请参阅本主题的以前版本

本指南:

  • 将现有 ASP.NET Core 应用置于反向代理服务器后方。
  • 设置反向代理服务器,以便将请求转发到 Kestrel Web 服务器。
  • 确保 Web 应用在启动时作为守护程序运行。
  • 配置进程管理工具以帮助重新启动 Web 应用。

先决条件Prerequisites

  • 使用具有 sudo 特权的标准用户帐户访问 Ubuntu 16.04 服务器。
  • 在服务器上安装 .NET Core 运行时。
    • 访问下载 .NET Core 页面
    • 选择最新的 .NET Core 非预览版。
    • 在“运行应用”-“运行时”下的表格中,下载最新的非预览版运行时 。
    • 选择 Linux 包管理器说明链接,然后按照 Ubuntu 版本的 Ubuntu 说明进行操作 。
  • 一个现有 ASP.NET Core 应用。
    升级共享框架后,可随时重启服务器托管的 ASP.NET Core 应用。

通过应用发布和复制Publish and copy over the app

配置应用以进行依赖框架的部署

如果应用在本地运行,且未配置为建立安全连接 (HTTPS),则采用以下任一方法:

  • 配置应用,以处理安全的本地连接。有关详细信息,请参阅 HTTPS 配置部分。
  • 从 Properties/launchSettings.json 文件中的 applicationUrl 属性中删除 https://localhost:5001(如果存在) 。

在开发环境中运行 dotnet publish,将应用打包到可在服务器上运行的目录中(例如 bin/Release/<target_framework_moniker>/publish) :

  1. dotnet publish --configuration Release

如果不希望维护服务器上的 .NET Core 运行时,还可将应用发布为独立部署

使用集成到组织工作流的工具(例如 SCP、SFTP)将 ASP.NET Core 应用复制到服务器。通常可在 var 目录(例如 var/www/helloapp)下找到 Web 应用 。

备注

在生产部署方案中,持续集成工作流会执行发布应用并将资产复制到服务器的工作。

测试应用:

  • 在命令行中运行应用:dotnet <app_assembly>.dll
  • 在浏览器中,导航到 http://<serveraddress>:<port&gt; 以确认应用在 Linux 本地正常运行。

配置反向代理服务器Configure a reverse proxy server

反向代理是为动态 Web 应用提供服务的常见设置。反向代理终止 HTTP 请求,并将其转发到 ASP.NET Core 应用。

使用反向代理服务器Use a reverse proxy server

Kestrel 非常适合从 ASP.NET Core 提供动态内容。但是,Web 服务功能不像服务器(如 IIS、Apache 或 Nginx)那样功能丰富。反向代理服务器可以卸载 HTTP 服务器的工作负载,如提供静态内容、缓存请求、压缩请求和 HTTPS 终端。反向代理服务器可能驻留在专用计算机上,也可能与 HTTP 服务器一起部署。

鉴于此指南的目的,使用 Nginx 的单个实例。它与 HTTP 服务器一起运行在同一服务器上。根据要求,可以选择不同的设置。

由于请求是通过反向代理转接的,因此使用 Microsoft.AspNetCore.HttpOverrides 包中的转接头中间件此中间件使用 X-Forwarded-Proto 标头来更新 Request.Scheme,使重定向 URI 和其他安全策略能够正常工作。

调用转接头中间件后,必须放置依赖于该架构的组件,例如身份验证、链接生成、重定向和地理位置。作为一般规则,转接头中间件应在诊断和错误处理中间件以外的其他中间件之前运行。此顺序可确保依赖于转接头信息的中间件可以使用标头值进行处理。

在调用 UseAuthentication 或类似的身份验证方案中间件之前,调用 Startup.Configure 中的 UseForwardedHeaders 方法。配置中间件以转接 X-Forwarded-ForX-Forwarded-Proto 标头:

  1. // using Microsoft.AspNetCore.HttpOverrides;
  2. app.UseForwardedHeaders(new ForwardedHeadersOptions
  3. {
  4. ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
  5. });
  6. app.UseAuthentication();

如果没有为中间件指定 ForwardedHeadersOptions,则要转接的默认标头为 None

默认情况下,在环回地址 (127.0.0.0/8, [::1])(包括标准 localhost 地址 (127.0.0.1))上运行的代理受信任。如果组织内的其他受信任代理或网络处理 Internet 与 Web 服务器之间的请求,请使用 ForwardedHeadersOptions 将其添加到 KnownProxiesKnownNetworks 的列表。以下示例会将 IP 地址为 10.0.0.100 的受信任代理服务器添加到 Startup.ConfigureServices 中的转接头中间件 KnownProxies

  1. // using System.Net;
  2. services.Configure<ForwardedHeadersOptions>(options =>
  3. {
  4. options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
  5. });

有关详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

安装 NginxInstall Nginx

使用 apt-get 安装 Nginx。安装程序将创建一个 systemd init 脚本,该脚本运行 Nginx,作为系统启动时的守护程序 。按照以下网站上的 Ubuntu 安装说明操作:Nginx:官方 Debian/Ubuntu 包

备注

如果需要可选 Nginx 模块,则可能需要从源代码生成 Nginx。

因为是首次安装 Nginx,通过运行以下命令显式启动:

  1. sudo service nginx start

确认浏览器显示 Nginx 的默认登陆页。可在 http://<server_IP_address>/index.nginx-debian.html 访问登陆页面。

配置 NginxConfigure Nginx

若要将 Nginx 配置为反向代理以将请求转接到 ASP.NET Core 应用,请修改 /etc/nginx/sites-available/default 。在文本编辑器中打开它,并将内容替换为以下内容:

  1. server {
  2. listen 80;
  3. server_name example.com *.example.com;
  4. location / {
  5. proxy_pass http://localhost:5000;
  6. proxy_http_version 1.1;
  7. proxy_set_header Upgrade $http_upgrade;
  8. proxy_set_header Connection keep-alive;
  9. proxy_set_header Host $host;
  10. proxy_cache_bypass $http_upgrade;
  11. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  12. proxy_set_header X-Forwarded-Proto $scheme;
  13. }
  14. }

如果要用是依赖于 SignalR WebSocket 的 Blazor Server 应用,请参阅 托管和部署 ASP.NET Core Blazor Server 了解如何设置 Connection 标头。

当没有匹配的 server_name 时,Nginx 使用默认服务器。如果没有定义默认服务器,则配置文件中的第一台服务器是默认服务器。作为最佳做法,添加指定默认服务器,它会在配置文件中返回状态代码 444。默认的服务器配置示例是:

  1. server {
  2. listen 80 default_server;
  3. # listen [::]:80 default_server deferred;
  4. return 444;
  5. }

使用上述配置文件和默认服务器,Nginx 接受主机标头 example.com*.example.com 端口 80 上的公共流量。与这些主机不匹配的请求不会转接到 Kestrel。Nginx 将匹配的请求转接到 http://localhost:5000 中的 Kestrel。有关详细信息,请参阅 nginx 如何处理请求若要更改 Kestrel 的 IP/端口,请参阅 Kestrel:终结点配置

警告

未能指定正确的 server_name 指令会公开应用的安全漏洞。如果可控制整个父域(区别于易受攻击的 .com),则子域通配符绑定(例如,.example.com)不具有此安全风险。有关详细信息,请参阅 rfc7230 第 5.4 条

完成配置 Nginx 后,运行 sudo nginx -t 来验证配置文件的语法。如果配置文件测试成功,可以通过运行 sudo nginx -s reload 强制 Nginx 选取更改。

要直接在服务器上运行应用:

  • 请导航到应用目录。
  • 运行应用:dotnet <app_assembly.dll>,其中 app_assembly.dll 是应用的程序集文件名。
    如果应用在服务器上运行,但无法通过 Internet 响应,请检查服务器的防火墙,并确认端口 80 已打开。如果使用 Azure Ubuntu VM,请添加启用入站端口 80 流量的网络安全组 (NSG) 规则。不需要启用出站端口 80 规则,因为启用入站规则后会自动许可出站流量。

测试应用完成后,请在命令提示符处按 Ctrl+C 关闭应用。

监视应用Monitor the app

服务器设置为将对 http://<serveraddress>:80 发起的请求转接到在 http://127.0.0.1:5000 中的 Kestrel 上运行的 ASP.NET Core 应用。但是,未将 Nginx 设置为管理 Kestrel 进程。systemd 可用于创建服务文件以启动和监视基础 Web 应用。systemd 是一个 init 系统,可以提供用于启动、停止和管理进程的许多强大的功能。

创建服务文件Create the service file

创建服务定义文件:

  1. sudo nano /etc/systemd/system/kestrel-helloapp.service

以下是应用的一个示例服务文件:

  1. [Unit]
  2. Description=Example .NET Web API App running on Ubuntu
  3. [Service]
  4. WorkingDirectory=/var/www/helloapp
  5. ExecStart=/usr/bin/dotnet /var/www/helloapp/helloapp.dll
  6. Restart=always
  7. # Restart service after 10 seconds if the dotnet service crashes:
  8. RestartSec=10
  9. KillSignal=SIGINT
  10. SyslogIdentifier=dotnet-example
  11. User=www-data
  12. Environment=ASPNETCORE_ENVIRONMENT=Production
  13. Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
  14. [Install]
  15. WantedBy=multi-user.target

在前面的示例中,管理服务的用户由 User 选项指定。用户 (www-data) 必须存在并且拥有正确应用文件的所有权。

使用 TimeoutStopSec 配置在收到初始中断信号后等待应用程序关闭的持续时间。如果应用程序在此时间段内未关闭,则将发出 SIGKILL 以终止该应用程序。提供作为无单位秒数的值(例如,150)、时间跨度值(例如,2min 30s)或 infinity 以禁用超时。TimeoutStopSec 默认为管理器配置文件(systemd-system.confsystem.conf.dsystemd-user.confuser.conf.d)中 DefaultTimeoutStopSec 的值。大多数分发版的默认超时时间为 90 秒。

  1. # The default value is 90 seconds for most distributions.
  2. TimeoutStopSec=90

Linux 具有区分大小写的文件系统。将 ASPNETCORE_ENVIRONMENT 设置为“生产”会导致搜索配置文件 appsettings.Production.json ,而不是 appsettings.production.json 。

必须转义某些值(例如,SQL 连接字符串)以供配置提供程序读取环境变量。使用以下命令生成适当的转义值以供在配置文件中使用:

  1. systemd-escape "<value-to-escape>"

环境变量名不支持冒号 (:) 分隔符。使用双下划线 () 代替冒号。环境变量读入配置时,环境变量配置提供程序将双下划线转换为冒号。以下示例中,连接字符串密钥 ConnectionStrings:DefaultConnectionConnectionStringsDefaultConnection 形式设置到服务定义文件中:

  1. Environment=ConnectionStrings__DefaultConnection={Connection String}

保存该文件并启用该服务。

  1. sudo systemctl enable kestrel-helloapp.service

启用该服务,并确认它正在运行。

  1. sudo systemctl start kestrel-helloapp.service
  2. sudo systemctl status kestrel-helloapp.service
  3. kestrel-helloapp.service - Example .NET Web API App running on Ubuntu
  4. Loaded: loaded (/etc/systemd/system/kestrel-helloapp.service; enabled)
  5. Active: active (running) since Thu 2016-10-18 04:09:35 NZDT; 35s ago
  6. Main PID: 9021 (dotnet)
  7. CGroup: /system.slice/kestrel-helloapp.service
  8. └─9021 /usr/local/bin/dotnet /var/www/helloapp/helloapp.dll

在配置了反向代理并通过 systemd 管理 Kestrel 后,Web 应用现已完全配置,并能在本地计算机上的浏览器中从 http://localhost 进行访问。也可以从远程计算机进行访问,同时限制可能进行阻止的任何防火墙。检查响应标头,Server 标头显示由 Kestrel 所提供的 ASP.NET Core 应用。

  1. HTTP/1.1 200 OK
  2. Date: Tue, 11 Oct 2016 16:22:23 GMT
  3. Server: Kestrel
  4. Keep-Alive: timeout=5, max=98
  5. Connection: Keep-Alive
  6. Transfer-Encoding: chunked

查看日志View logs

使用 Kestrel 的 Web 应用是通过 systemd 进行管理的,因此所有事件和进程都被记录到集中日志。但是,此日志包含由 systemd 管理的所有服务和进程的全部条目。若要查看特定于 kestrel-helloapp.service 的项,请使用以下命令:

  1. sudo journalctl -fu kestrel-helloapp.service

有关进一步筛选,使用时间选项(如 —since today—until 1 hour ago)或这些选项的组合可以减少返回的条目数。

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

数据保护Data protection

ASP.NET Core 数据保护堆栈由多个 ASP.NET Core 中间件(包括 cookie 中间件等身份验证中间件)和跨站点请求伪造 (CSRF) 保护使用。即使用户代码不调用数据保护 API,也应该配置数据保护,以创建持久的加密密钥存储如果不配置数据保护,则密钥存储在内存中。重启应用时,密钥会被丢弃。

如果密钥环存储于内存中,则在应用重启时:

  • 所有基于 cookie 的身份验证令牌都无效。
  • 用户需要在下一次请求时再次登录。
  • 无法再解密使用密钥环保护的任何数据。这可能包括 CSRF 令牌ASP.NET Core MVC TempData cookie

若要配置数据保护以持久保存并加密密钥环,请参阅:

较长的请求标头字段Long request header fields

代理服务器默认设置通常将请求标头字段限制为 4 K 或 8 K,具体取决于平台。某些应用可能需要超过默认值的字段(例如,使用 Azure Active Directory 的应用)。如果需要更长的字段,则代理服务器的默认设置需要进行调整。要应用的值具体取决于方案。有关详细信息,请参见服务器文档。

警告

除非必要,否则不要提高代理缓冲区的默认值。提高这些值将增加缓冲区溢出的风险和恶意用户的拒绝服务 (DoS) 攻击风险。

保护应用Secure the app

启用 AppArmorEnable AppArmor

Linux 安全模块 (LSM) 是一个框架,它是自 Linux 2.6 后的 Linux kernel 的一部分。LSM 支持安全模块的不同实现。AppArmor 是实现强制访问控制系统的 LSM,它允许将程序限制在一组有限的资源内。确保已启用并成功配置 AppArmor。

配置防火墙Configure the firewall

关闭所有未使用的外部端口。通过为配置防火墙提供 CLI,不复杂的防火墙 (ufw) 为 iptables 提供了前端。

警告

如果未正确配置,防火墙将阻止对整个系统的访问。在使用 SSH 进行连接时,未能指定正确的 SSH 端口最终会将你关在系统之外。默认端口为 22。有关详细信息,请参阅 ufw 简介手册

安装 ufw,并将其配置为允许所需任何端口上的流量。

  1. sudo apt-get install ufw
  2. sudo ufw allow 22/tcp
  3. sudo ufw allow 80/tcp
  4. sudo ufw allow 443/tcp
  5. sudo ufw enable

保护 NginxSecure Nginx

更改 Nginx 响应名称Change the Nginx response name

编辑 src/http/ngx_http_header_filter_module.c :

  1. static char ngx_http_server_string[] = "Server: Web Server" CRLF;
  2. static char ngx_http_server_full_string[] = "Server: Web Server" CRLF;

配置选项Configure options

用其他必需模块配置服务器。请考虑使用 ModSecurity 等 Web 应用防火墙来加强对应用的保护。

HTTPS 配置HTTPS configuration

配置应用,以进行安全的 (HTTPS) 本地连接

dotnet run 命令使用应用的 Properties/launchSettings.json 文件,该文件将应用配置为侦听 applicationUrl 属性(例如 https://localhost:5001;http://localhost:5000)提供的 URL 。

使用以下方法之一配置应用,使其在开发过程中将证书用于 dotnet run 命令或开发环境(Visual Studio Code 中的 F5 或 Ctrl+F5):

配置反向代理,以便进行安全 (HTTPS) 客户端连接

  • 通过指定由受信任的证书颁发机构 (CA) 颁发的有效证书来配置服务器,以侦听端口 443 上的 HTTPS 流量。

  • 通过采用以下“/etc/nginx/nginx.conf” 文件中所示的某些做法来增强安全保护。示例包括选择更强的密码并将通过 HTTP 的所有流量重定向到 HTTPS。

  • 添加 HTTP Strict-Transport-Security (HSTS) 标头可确保由客户端发起的所有后续请求都通过 HTTPS。

  • 如果以后会禁用 HTTPS,请勿添加 HSTS 头或选择相应的 max-age

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

  1. proxy_redirect off;
  2. proxy_set_header Host $host;
  3. proxy_set_header X-Real-IP $remote_addr;
  4. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  5. proxy_set_header X-Forwarded-Proto $scheme;
  6. client_max_body_size 10m;
  7. client_body_buffer_size 128k;
  8. proxy_connect_timeout 90;
  9. proxy_send_timeout 90;
  10. proxy_read_timeout 90;
  11. proxy_buffers 32 4k;

编辑 /etc/nginx/nginx.conf 配置文件。示例包含一个配置文件中的 httpserver 部分。

  1. http {
  2. include /etc/nginx/proxy.conf;
  3. limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
  4. server_tokens off;
  5. sendfile on;
  6. keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case.
  7. client_body_timeout 10; client_header_timeout 10; send_timeout 10;
  8. upstream hellomvc{
  9. server localhost:5000;
  10. }
  11. server {
  12. listen *:80;
  13. add_header Strict-Transport-Security max-age=15768000;
  14. return 301 https://$host$request_uri;
  15. }
  16. server {
  17. listen *:443 ssl;
  18. server_name example.com;
  19. ssl_certificate /etc/ssl/certs/testCert.crt;
  20. ssl_certificate_key /etc/ssl/certs/testCert.key;
  21. ssl_protocols TLSv1.1 TLSv1.2;
  22. ssl_prefer_server_ciphers on;
  23. ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
  24. ssl_ecdh_curve secp384r1;
  25. ssl_session_cache shared:SSL:10m;
  26. ssl_session_tickets off;
  27. ssl_stapling on; #ensure your cert is capable
  28. ssl_stapling_verify on; #ensure your cert is capable
  29. add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
  30. add_header X-Frame-Options DENY;
  31. add_header X-Content-Type-Options nosniff;
  32. #Redirects all traffic
  33. location / {
  34. proxy_pass http://hellomvc;
  35. limit_req zone=one burst=10 nodelay;
  36. }
  37. }
  38. }

保护 Nginx 免受点击劫持的侵害Secure Nginx from clickjacking

点击劫持(也称为 UI 伪装攻击)是一种恶意攻击,其中网站访问者会上当受骗,从而导致在与当前要访问的页面不同的页面上单击链接或按钮。使用 X-FRAME-OPTIONS 可保护网站。

缓解点击劫持攻击:

  • 编辑 nginx.conf 文件:
  1. sudo nano /etc/nginx/nginx.conf

添加行 add_header X-Frame-Options "SAMEORIGIN";

  • 保存该文件。

  • 重启 Nginx。

MIME 类型探查MIME-type sniffing

此标头可阻止大部分浏览器通过 MIME 方式探查来自已声明内容类型的响应,因为标头会指示浏览器不要替代响应内容类型。使用 nosniff 选项后,如果服务器认为内容是“文本/html”,则浏览器将其显示为“文本/html”。

编辑 nginx.conf 文件:

  1. sudo nano /etc/nginx/nginx.conf

添加行 add_header X-Content-Type-Options "nosniff"; 并保存文件,然后重新启动 Nginx。

其他 Nginx 建议Additional Nginx suggestions

在服务器上升级共享框架后,重启服务器托管的 ASP.NET Core 应用。

其他资源Additional resources