本指南介绍如何为 Ubuntu、Red Hat Enterprise (RHEL) 和 SUSE Linux Enterprise Server 设置生产就绪 ASP.NET Core 环境。

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

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

    如果应用在 开发环境 中本地运行,并且未被服务器配置为建立安全 HTTPS 连接,则采用以下任一方法:

  • 配置应用,以处理安全的本地连接。 有关详细信息,请参阅 HTTPS 配置 部分。

  • 将应用配置为在不安全的终结点运行:

  • 在开发环境 ( Program.cs ) 中停用 HTTPS 重定向中间件:

    if (!app.Environment.IsDevelopment())
        app.UseHttpsRedirection();
    

    有关详细信息,请参阅在 ASP.NET Core 中使用多个环境

  • Properties/launchSettings.json 文件的 applicationUrl 属性中删除 https://localhost:5001(如果存在)。

    有关按环境配置的详细信息,请参阅在 ASP.NET Core 中使用多个环境

    在开发环境中运行 dotnet publish,将应用打包到可在服务器上运行的目录(例如 bin/Release/{TARGET FRAMEWORK MONIKER}/publish,其中 {TARGET FRAMEWORK MONIKER} 占位符表示目标框架名字对象 (TFM))中:

    dotnet publish --configuration Release
    

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

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

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

    测试应用:

  • 在命令行中运行应用:dotnet <app_assembly>.dll
  • 在浏览器中,导航到 http://<serveraddress>:<port> 以确认应用在 Linux 本地正常运行。
  • 配置反向代理服务器

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

    使用反向代理服务器

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

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

    由于请求由反向代理转发,请使用 Microsoft.AspNetCore.HttpOverrides 包中的 Forwarded Headers Middleware,该中间件通过共享框架的Microsoft.AspNetCore.App元包自动包含在 ASP.NET Core 应用中。 此中间件使用 X-Forwarded-Proto 标头来更新 Request.Scheme,使重定向 URI 和其他安全策略能够正常工作。

    转接头中间件应在其他中间件之前运行。 此顺序可确保依赖于转接头信息的中间件可以使用标头值进行处理。 若要在诊断和错误处理中间件后运行转接头中间件,请参阅转接头中间件顺序

    调用其他中间件之前,先调用 UseForwardedHeaders 方法。 配置中间件以转接 X-Forwarded-ForX-Forwarded-Proto 标头:

    using Microsoft.AspNetCore.HttpOverrides;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAuthentication();
    var app = builder.Build();
    app.UseForwardedHeaders(new ForwardedHeadersOptions
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    app.UseAuthentication();
    app.MapGet("/", () => "Hello ForwardedHeadersOptions!");
    app.Run();
    

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

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

    using Microsoft.AspNetCore.HttpOverrides;
    using System.Net;
    var builder = WebApplication.CreateBuilder(args);
    // Configure forwarded headers
    builder.Services.Configure<ForwardedHeadersOptions>(options =>
        options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
    builder.Services.AddAuthentication();
    var app = builder.Build();
    app.UseForwardedHeaders(new ForwardedHeadersOptions
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    app.UseAuthentication();
    app.MapGet("/", () => "10.0.0.100");
    app.Run();
    

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

    安装 Nginx

    Ubuntu Red Hat Enterprise Linux SUSE Linux Enterprise Server
    sudo service nginx start
    

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

    配置 Nginx

    Ubuntu Red Hat Enterprise Linux SUSE Linux Enterprise Server

    若要将 Nginx 配置为反向代理以将 HTTP 请求转发到 ASP.NET Core 应用,请修改 /etc/nginx/sites-available/default 并重新创建符号链接。 创建 /etc/nginx/sites-available/default 文件后,使用以下命令创建符号链接:

    sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
    

    在文本编辑器中打开 /etc/nginx/sites-available/default,并将内容替换为以下代码片段:

    若要将 Nginx 配置为反向代理以将 HTTP 请求转发到 ASP.NET Core 应用,请修改 /etc/nginx.conf。 使用以下代码片段更新 http{} 代码块:

    若要将 Nginx 配置为反向代理以将 HTTP 请求转发到 ASP.NET Core 应用,请修改 /etc/nginx.conf。 使用以下代码片段更新 http{} 代码块:

    http {
      map $http_connection $connection_upgrade {
        "~*Upgrade" $http_connection;
        default keep-alive;
      server {
        listen        80;
        server_name   example.com *.example.com;
        location / {
            proxy_pass         http://127.0.0.1:5000/;
            proxy_http_version 1.1;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection $connection_upgrade;
            proxy_set_header   Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
    

    如果应用是 SignalR 或 Blazor Server 应用,请分别参阅 ASP.NET Core SignalR 生产托管和扩展托管和部署 ASP.NET Core 服务器端 Blazor 应用,了解详细信息。

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

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

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

    未能指定正确的 server_name 指令会公开应用的安全漏洞。 如果可控制整个父域(区别于易受攻击的 *.com),则子域通配符绑定(例如,*.example.com)不具有此安全风险。 有关详细信息,请参阅 RFC 9110:HTTP 语义(第 7.2 节:托管和授权)

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

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

  • 请导航到应用目录。
  • 运行应用:dotnet <app_assembly.dll>,其中 app_assembly.dll 是应用的程序集文件名。
  • 完成应用测试后,请在命令提示符处按 Ctrl+C (Windows) 或 +C (macOS) 关闭应用。

    增加 keepalive_requests

    可以增加 keepalive_requests 以获得更佳性能,有关详细信息,请参阅此 GitHub 问题

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

    创建服务文件

    创建服务定义文件:

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

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

    [Unit]
    Description=Example .NET Web API App running on Linux
    [Service]
    WorkingDirectory=/var/www/helloapp
    ExecStart=/usr/bin/dotnet /var/www/helloapp/helloapp.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Production
    Environment=DOTNET_NOLOGO=true
    [Install]
    WantedBy=multi-user.target
    

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

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

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

    Linux 具有区分大小写的文件系统。 将 ASPNETCORE_ENVIRONMENT 设置为 Production 时,将搜索配置文件 appsettings.Production.json,而不搜索 appsettings.production.json

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

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

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

    Environment=ConnectionStrings__DefaultConnection={Connection String}
    

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

    sudo systemctl enable kestrel-helloapp.service
    

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

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

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

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

    sudo journalctl -fu kestrel-helloapp.service
    

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

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

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

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

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

  • ASP.NET Core 中的密钥存储提供程序
  • 使用 ASP.NET Core 在 Windows 和 Azure 中实现处于 rest 的密钥加密
  • 较长的请求标头字段

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

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

    启用 AppArmor

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

    配置防火墙

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

    Ubuntu Red Hat Enterprise Linux SUSE Linux Enterprise Server

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

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

    sudo apt-get install ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

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

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

    sudo yum -y install ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

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

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

    sudo yum -y install ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

    保护 Nginx

    更改 Nginx 响应名称

    编辑 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;
    

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

    HTTPS 配置

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

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

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

  • 从配置中替换默认证书(推荐)
  • KestrelServerOptions.ConfigureHttpsDefaults
  • 配置反向代理,以便进行安全 (HTTPS) 客户端连接

    本部分中的安全配置是一种常规配置,可以作为进一步进行自定义的起点。 我们无法为第三方工具、服务器和操作系统提供支持。 如果使用本部分中的配置,请自行承担风险。 有关更多信息,请访问以下资源:

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

  • 通过采用以下“/etc/nginx/nginx.conf”文件中所示的某些做法来增强安全保护。

  • 下面的示例未将服务器配置为重定向不安全的请求。 建议使用 HTTPS 重定向中间件。 有关详细信息,请参阅在 ASP.NET Core 中强制使用 HTTPS

    对于由服务器配置(而非 HTTPS 重定向中间件)处理安全重定向的开发环境,建议使用临时重定向 (302) 替代永久性重定向 (301)。 链接缓存会导致开发环境中的行为不稳定。

  • 添加 Strict-Transport-Security (HSTS) 标头可确保由客户端发起的所有后续请求都通过 HTTPS。 有关设置 Strict-Transport-Security 标头的指南,请参阅在 ASP.NET Core 中强制使用 HTTPS

  • 如果将来将禁用 HTTPS,请使用以下方法之一:

  • 不要添加 HSTS 标头。
  • 选择短的 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;
    Ubuntu
    Red Hat Enterprise Linux
    SUSE Linux Enterprise Server
    
    http {
        include        /etc/nginx/proxy.conf;
        limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
        server_tokens  off;
        sendfile on;
        # Adjust keepalive_timeout to the lowest possible value that makes sense 
        # for your use case.
        keepalive_timeout   29;
        client_body_timeout 10; client_header_timeout 10; send_timeout 10;
        upstream helloapp{
            server 127.0.0.1:5000;
        server {
            listen                    443 ssl http2;
            listen                    [::]:443 ssl http2;
            server_name               example.com *.example.com;
            ssl_certificate           /etc/ssl/certs/testCert.crt;
            ssl_certificate_key       /etc/ssl/certs/testCert.key;
            ssl_session_timeout       1d;
            ssl_protocols             TLSv1.2 TLSv1.3;
            ssl_prefer_server_ciphers off;
            ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
            ssl_session_cache         shared:SSL:10m;
            ssl_session_tickets       off;
            ssl_stapling              off;
            add_header X-Frame-Options DENY;
            add_header X-Content-Type-Options nosniff;
            #Redirects all traffic
            location / {
                proxy_pass http://helloapp;
                limit_req  zone=one burst=10 nodelay;
    

    Blazor WebAssembly 应用需要更大的 burst 参数值才能容纳应用发出的更大量的请求。 有关详细信息,请参阅托管和部署 ASP.NET Core Blazor WebAssembly

    上面的示例禁用了在线证书状态协议 (OCSP) 装订。 如果启用,请确认证书支持该功能。 有关启用 OCSP 的详细信息和指南,请参阅模块 ngx_http_ssl_module(Nginx 文档)一文中的以下属性:

  • ssl_stapling
  • ssl_stapling_file
  • ssl_stapling_responder
  • ssl_stapling_verify
  • 保护 Nginx 免受点击劫持的侵害

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

    缓解点击劫持攻击:

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

    http{} 代码块中,添加行:add_header X-Frame-Options "SAMEORIGIN";

  • 保存该文件。

  • 重启 Nginx。

    MIME 类型探查

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

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

    http{} 代码块中,添加行:add_header X-Content-Type-Options "nosniff";

  • 保存该文件。

  • 重启 Nginx。

    其他 Nginx 建议

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

  • Linux 上 .NET Core 的先决条件
  • Nginx:二进制版本:官方 Debian/Ubuntu 包
  • ASP.NET Core 项目故障排除和调试
  • 配置 ASP.NET Core 以使用代理服务器和负载均衡器
  • NGINX:使用转接头
  • 本指南介绍如何在 Ubuntu 16.04 服务器上设置生产就绪 ASP.NET Core 环境。 这些说明可能适用于较新版本的 Ubuntu,但尚未使用较新版本进行测试。

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

    对于 Ubuntu 14.04,建议使用 supervisord 作为监视 Kestrel 进程的解决方案。 systemd 不适用于 Ubuntu 14.04。 有关 Ubuntu 14.04 的说明,请参阅本主题的以前版本

  • 将现有 ASP.NET Core 应用置于反向代理服务器后方。
  • 设置反向代理服务器,以便将请求转发到 Kestrel Web 服务器。
  • 确保 Web 应用在启动时作为守护程序运行。
  • 配置进程管理工具以帮助重新启动 Web 应用。
  • 使用具有 sudo 特权的标准用户帐户访问 Ubuntu 16.04 服务器。
  • 服务器上安装了最新的非预览版 .NET 运行时
  • 一个现有 ASP.NET Core 应用。
  • 升级共享框架后,可随时重启服务器托管的 ASP.NET Core 应用。

    通过应用发布和复制

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

    如果应用在开发环境中本地运行,并且未被服务器配置为建立安全 HTTPS 连接,则采用以下任一方法:

  • 配置应用,以处理安全的本地连接。 有关详细信息,请参阅 HTTPS 配置部分。

  • 将应用配置为在不安全的终结点运行:

  • 在开发环境 (Program.cs) 中停用 HTTPS 重定向中间件:

    if (!app.Environment.IsDevelopment())
        app.UseHttpsRedirection();
    

    有关详细信息,请参阅在 ASP.NET Core 中使用多个环境

  • Properties/launchSettings.json 文件的 applicationUrl 属性中删除 https://localhost:5001(如果存在)。

    有关按环境配置的详细信息,请参阅在 ASP.NET Core 中使用多个环境

    在开发环境中运行 dotnet publish,将应用打包到可在服务器上运行的目录中(例如 bin/Release/{TARGET FRAMEWORK MONIKER}/publish,其中 {TARGET FRAMEWORK MONIKER} 占位符表示目标框架名字对象/TFM):

    dotnet publish --configuration Release
    

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

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

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

    测试应用:

  • 在命令行中运行应用:dotnet <app_assembly>.dll
  • 在浏览器中,导航到 http://<serveraddress>:<port> 以确认应用在 Linux 本地正常运行。
  • 配置反向代理服务器

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

    使用反向代理服务器

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

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

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

    转接头中间件应在其他中间件之前运行。 此顺序可确保依赖于转接头信息的中间件可以使用标头值进行处理。 若要在诊断和错误处理中间件后运行转接头中间件,请参阅转接头中间件顺序

    调用其他中间件之前,请先在 Program.cs 的基础上调用 UseForwardedHeaders 方法。 配置中间件以转接 X-Forwarded-ForX-Forwarded-Proto 标头:

    // requires using Microsoft.AspNetCore.HttpOverrides;
    app.UseForwardedHeaders(new ForwardedHeadersOptions
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    app.UseAuthentication();
    

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

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

    using System.Net;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.Configure<ForwardedHeadersOptions>(options =>
        options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
    

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

    安装 Nginx

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

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

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

    sudo service nginx start
    

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

    配置 Nginx

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

    server {
        listen        80;
        server_name   example.com *.example.com;
        location / {
            proxy_pass         http://127.0.0.1:5000;
            proxy_http_version 1.1;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_set_header   Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
    

    如果应用是 SignalR 或 Blazor Server 应用,请分别参阅 ASP.NET Core SignalR 生产托管和扩展托管和部署 ASP.NET Core 服务器端 Blazor 应用,了解详细信息。

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

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

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

    未能指定正确的 server_name 指令会公开应用的安全漏洞。 如果可控制整个父域(区别于易受攻击的 *.com),则子域通配符绑定(例如,*.example.com)不具有此安全风险。 有关详细信息,请参阅 RFC 9110:HTTP 语义(第 7.2 节:托管和授权)

    完成配置 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 (Windows) 或 +C (macOS) 关闭应用。

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

    创建服务文件

    创建服务定义文件:

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

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

    [Unit]
    Description=Example .NET Web API App running on Ubuntu
    [Service]
    WorkingDirectory=/var/www/helloapp
    ExecStart=/usr/bin/dotnet /var/www/helloapp/helloapp.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Production
    Environment=DOTNET_NOLOGO=true
    [Install]
    WantedBy=multi-user.target
    

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

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

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

    Linux 具有区分大小写的文件系统。 将 ASPNETCORE_ENVIRONMENT 设置为 Production 时,将搜索配置文件 appsettings.Production.json,而不搜索 appsettings.production.json

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

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

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

    Environment=ConnectionStrings__DefaultConnection={Connection String}
    

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

    sudo systemctl enable kestrel-helloapp.service
    

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

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

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

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

    sudo journalctl -fu kestrel-helloapp.service
    

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

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

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

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

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

  • ASP.NET Core 中的密钥存储提供程序
  • 使用 ASP.NET Core 在 Windows 和 Azure 中实现处于 rest 的密钥加密
  • 较长的请求标头字段

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

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

    启用 AppArmor

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

    配置防火墙

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

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

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

    sudo apt-get install ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

    保护 Nginx

    更改 Nginx 响应名称

    编辑 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;
    

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

    HTTPS 配置

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

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

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

  • 从配置中替换默认证书(推荐)
  • KestrelServerOptions.ConfigureHttpsDefaults
  • 配置反向代理,以便进行安全 (HTTPS) 客户端连接

    本部分中的安全配置是一种常规配置,可以作为进一步进行自定义的起点。 我们无法为第三方工具、服务器和操作系统提供支持。 如果使用本部分中的配置,请自行承担风险。 有关更多信息,请访问以下资源:

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

  • 通过采用以下“/etc/nginx/nginx.conf”文件中所示的某些做法来增强安全保护。

  • 下面的示例未将服务器配置为重定向不安全的请求。 建议使用 HTTPS 重定向中间件。 有关详细信息,请参阅在 ASP.NET Core 中强制使用 HTTPS

    对于由服务器配置(而非 HTTPS 重定向中间件)处理安全重定向的开发环境,建议使用临时重定向 (302) 替代永久性重定向 (301)。 链接缓存会导致开发环境中的行为不稳定。

  • 添加 Strict-Transport-Security (HSTS) 标头可确保由客户端发起的所有后续请求都通过 HTTPS。 有关设置 Strict-Transport-Security 标头的指南,请参阅在 ASP.NET Core 中强制使用 HTTPS

  • 如果将来将禁用 HTTPS,请使用以下方法之一:

  • 不要添加 HSTS 标头。
  • 选择短的 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 配置文件的内容替换为以下文件。 示例包含一个配置文件中的 httpserver 部分。

    http {
        include        /etc/nginx/proxy.conf;
        limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
        server_tokens  off;
        sendfile on;
        # Adjust keepalive_timeout to the lowest possible value that makes sense 
        # for your use case.
        keepalive_timeout   29;
        client_body_timeout 10; client_header_timeout 10; send_timeout 10;
        upstream helloapp{
            server 127.0.0.1:5000;
        server {
            listen                    443 ssl http2;
            listen                    [::]:443 ssl http2;
            server_name               example.com *.example.com;
            ssl_certificate           /etc/ssl/certs/testCert.crt;
            ssl_certificate_key       /etc/ssl/certs/testCert.key;
            ssl_session_timeout       1d;
            ssl_protocols             TLSv1.2 TLSv1.3;
            ssl_prefer_server_ciphers off;
            ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
            ssl_session_cache         shared:SSL:10m;
            ssl_session_tickets       off;
            ssl_stapling              off;
            add_header X-Frame-Options DENY;
            add_header X-Content-Type-Options nosniff;
            #Redirects all traffic
            location / {
                proxy_pass http://helloapp;
                limit_req  zone=one burst=10 nodelay;
    

    Blazor WebAssembly 应用需要更大的 burst 参数值才能容纳应用发出的更大量的请求。 有关详细信息,请参阅托管和部署 ASP.NET Core Blazor WebAssembly

    上面的示例禁用了在线证书状态协议 (OCSP) 装订。 如果启用,请确认证书支持该功能。 有关启用 OCSP 的详细信息和指南,请参阅模块 ngx_http_ssl_module(Nginx 文档)一文中的以下属性:

  • ssl_stapling
  • ssl_stapling_file
  • ssl_stapling_responder
  • ssl_stapling_verify
  • 保护 Nginx 免受点击劫持的侵害

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

    缓解点击劫持攻击:

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

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

  • 保存该文件。

  • 重启 Nginx。

    MIME 类型探查

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

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

    添加行:add_header X-Content-Type-Options "nosniff";

  • 保存该文件。

  • 重启 Nginx。

    其他 Nginx 建议

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

  • Linux 上 .NET Core 的先决条件
  • Nginx:二进制版本:官方 Debian/Ubuntu 包
  • ASP.NET Core 项目故障排除和调试
  • 配置 ASP.NET Core 以使用代理服务器和负载均衡器
  • NGINX:使用转接头
  • 本指南介绍如何在 Ubuntu 16.04 服务器上设置生产就绪 ASP.NET Core 环境。 这些说明可能适用于较新版本的 Ubuntu,但尚未使用较新版本进行测试。

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

    对于 Ubuntu 14.04,建议使用 supervisord 作为监视 Kestrel 进程的解决方案。 systemd 不适用于 Ubuntu 14.04。 有关 Ubuntu 14.04 的说明,请参阅本主题的以前版本

  • 将现有 ASP.NET Core 应用置于反向代理服务器后方。
  • 设置反向代理服务器,以便将请求转发到 Kestrel Web 服务器。
  • 确保 Web 应用在启动时作为守护程序运行。
  • 配置进程管理工具以帮助重新启动 Web 应用。
  • 使用具有 sudo 特权的标准用户帐户访问 Ubuntu 16.04 服务器。
  • 服务器上安装了最新的非预览版 .NET 运行时
  • 一个现有 ASP.NET Core 应用。
  • 升级共享框架后,可随时重启服务器托管的 ASP.NET Core 应用。

    通过应用发布和复制

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

    如果应用在开发环境中本地运行,并且未被服务器配置为建立安全 HTTPS 连接,则采用以下任一方法:

  • 配置应用,以处理安全的本地连接。 有关详细信息,请参阅 HTTPS 配置部分。

  • 将应用配置为在不安全的终结点运行:

  • 在开发环境 (Program.cs) 中停用 HTTPS 重定向中间件:

    if (!app.Environment.IsDevelopment())
        app.UseHttpsRedirection();
    

    有关详细信息,请参阅在 ASP.NET Core 中使用多个环境

  • Properties/launchSettings.json 文件的 applicationUrl 属性中删除 https://localhost:5001(如果存在)。

    有关按环境配置的详细信息,请参阅在 ASP.NET Core 中使用多个环境

    在开发环境中运行 dotnet publish,将应用打包到可在服务器上运行的目录中(例如 bin/Release/{TARGET FRAMEWORK MONIKER}/publish,其中 {TARGET FRAMEWORK MONIKER} 占位符表示目标框架名字对象/TFM):

    dotnet publish --configuration Release
    

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

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

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

    测试应用:

  • 在命令行中运行应用:dotnet <app_assembly>.dll
  • 在浏览器中,导航到 http://<serveraddress>:<port> 以确认应用在 Linux 本地正常运行。
  • 配置反向代理服务器

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

    使用反向代理服务器

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

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

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

    转接头中间件应在其他中间件之前运行。 此顺序可确保依赖于转接头信息的中间件可以使用标头值进行处理。 若要在诊断和错误处理中间件后运行转接头中间件,请参阅转接头中间件顺序

    调用其他中间件之前,请先在 Startup.Configure 的基础上调用 UseForwardedHeaders 方法。 配置中间件以转接 X-Forwarded-ForX-Forwarded-Proto 标头:

    using Microsoft.AspNetCore.HttpOverrides;
    app.UseForwardedHeaders(new ForwardedHeadersOptions
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    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

    using System.Net;
    services.Configure<ForwardedHeadersOptions>(options =>
        options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
    

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

    安装 Nginx

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

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

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

    sudo service nginx start
    

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

    配置 Nginx

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

    server {
        listen        80;
        server_name   example.com *.example.com;
        location / {
            proxy_pass         http://127.0.0.1:5000;
            proxy_http_version 1.1;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_set_header   Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
    

    如果应用是 SignalR 或 Blazor Server 应用,请分别参阅 ASP.NET Core SignalR 生产托管和扩展托管和部署 ASP.NET Core 服务器端 Blazor 应用,了解详细信息。

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

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

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

    未能指定正确的 server_name 指令会公开应用的安全漏洞。 如果可控制整个父域(区别于易受攻击的 *.com),则子域通配符绑定(例如,*.example.com)不具有此安全风险。 有关详细信息,请参阅 RFC 9110:HTTP 语义(第 7.2 节:托管和授权)

    完成配置 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 (Windows) 或 +C (macOS) 关闭应用。

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

    创建服务文件

    创建服务定义文件:

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

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

    [Unit]
    Description=Example .NET Web API App running on Ubuntu
    [Service]
    WorkingDirectory=/var/www/helloapp
    ExecStart=/usr/bin/dotnet /var/www/helloapp/helloapp.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Production
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    [Install]
    WantedBy=multi-user.target
    

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

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

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

    Linux 具有区分大小写的文件系统。 将 ASPNETCORE_ENVIRONMENT 设置为 Production 时,将搜索配置文件 appsettings.Production.json,而不搜索 appsettings.production.json

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

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

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

    Environment=ConnectionStrings__DefaultConnection={Connection String}
    

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

    sudo systemctl enable kestrel-helloapp.service
    

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

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

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

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

    sudo journalctl -fu kestrel-helloapp.service
    

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

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

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

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

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

  • ASP.NET Core 中的密钥存储提供程序
  • 使用 ASP.NET Core 在 Windows 和 Azure 中实现处于 rest 的密钥加密
  • 较长的请求标头字段

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

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

    启用 AppArmor

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

    配置防火墙

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

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

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

    sudo apt-get install ufw
    sudo ufw allow 22/tcp
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

    保护 Nginx

    更改 Nginx 响应名称

    编辑 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;
    

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

    HTTPS 配置

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

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

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

  • 从配置中替换默认证书(推荐)
  • KestrelServerOptions.ConfigureHttpsDefaults
  • 配置反向代理,以便进行安全 (HTTPS) 客户端连接

    本部分中的安全配置是一种常规配置,可以作为进一步进行自定义的起点。 我们无法为第三方工具、服务器和操作系统提供支持。 如果使用本部分中的配置,请自行承担风险。 有关更多信息,请访问以下资源:

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

  • 通过采用以下“/etc/nginx/nginx.conf”文件中所示的某些做法来增强安全保护。

  • 下面的示例未将服务器配置为重定向不安全的请求。 建议使用 HTTPS 重定向中间件。 有关详细信息,请参阅在 ASP.NET Core 中强制使用 HTTPS

    对于由服务器配置(而非 HTTPS 重定向中间件)处理安全重定向的开发环境,建议使用临时重定向 (302) 替代永久性重定向 (301)。 链接缓存会导致开发环境中的行为不稳定。

  • 添加 Strict-Transport-Security (HSTS) 标头可确保由客户端发起的所有后续请求都通过 HTTPS。 有关设置 Strict-Transport-Security 标头的指南,请参阅在 ASP.NET Core 中强制使用 HTTPS

  • 如果将来将禁用 HTTPS,请使用以下方法之一:

  • 不要添加 HSTS 标头。
  • 选择短的 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 配置文件的内容替换为以下文件。 示例包含一个配置文件中的 httpserver 部分。

    http {
        include        /etc/nginx/proxy.conf;
        limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
        server_tokens  off;
        sendfile on;
        # Adjust keepalive_timeout to the lowest possible value that makes sense 
        # for your use case.
        keepalive_timeout   29;
        client_body_timeout 10; client_header_timeout 10; send_timeout 10;
        upstream helloapp{
            server 127.0.0.1:5000;
        server {
            listen                    443 ssl http2;
            listen                    [::]:443 ssl http2;
            server_name               example.com *.example.com;
            ssl_certificate           /etc/ssl/certs/testCert.crt;
            ssl_certificate_key       /etc/ssl/certs/testCert.key;
            ssl_session_timeout       1d;
            ssl_protocols             TLSv1.2 TLSv1.3;
            ssl_prefer_server_ciphers off;
            ssl_ciphers               ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
            ssl_session_cache         shared:SSL:10m;
            ssl_session_tickets       off;
            ssl_stapling              off;
            add_header X-Frame-Options DENY;
            add_header X-Content-Type-Options nosniff;
            #Redirects all traffic
            location / {
                proxy_pass http://helloapp;
                limit_req  zone=one burst=10 nodelay;
    

    Blazor WebAssembly 应用需要更大的 burst 参数值才能容纳应用发出的更大量的请求。 有关详细信息,请参阅托管和部署 ASP.NET Core Blazor WebAssembly

    上面的示例禁用了在线证书状态协议 (OCSP) 装订。 如果启用,请确认证书支持该功能。 有关启用 OCSP 的详细信息和指南,请参阅模块 ngx_http_ssl_module(Nginx 文档)一文中的以下属性:

  • ssl_stapling
  • ssl_stapling_file
  • ssl_stapling_responder
  • ssl_stapling_verify
  • 保护 Nginx 免受点击劫持的侵害

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

    缓解点击劫持攻击:

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

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

  • 保存该文件。

  • 重启 Nginx。

    MIME 类型探查

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

  • 编辑 nginx.conf 文件:

    sudo nano /etc/nginx/nginx.conf
    

    添加行:add_header X-Content-Type-Options "nosniff";

  • 保存该文件。

  • 重启 Nginx。

    其他 Nginx 建议

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

  • Linux 上 .NET Core 的先决条件
  • Nginx:二进制版本:官方 Debian/Ubuntu 包
  • ASP.NET Core 项目故障排除和调试
  • 配置 ASP.NET Core 以使用代理服务器和负载均衡器
  • NGINX:使用转接头
  •