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

本文内容

作者:Shayne Boyer

使用本指南,了解如何在 CentOS 7 上将 Apache 设置为反向代理服务器,以将 HTTP 流量重定向到在 Kestrel 服务器上运行的 ASP.NET Core Web 应用。mod_proxy extension 和相关模块可创建服务器的反向代理。

先决条件Prerequisites

  • 运行 CentOS 7 的服务器,使用具有 sudo 特权的标准用户帐户。
  • 在服务器上安装 .NET Core 运行时。
    • 访问下载 .NET Core 页面
    • 选择最新的 .NET Core 非预览版。
    • 在“运行应用”-“运行时”下的表格中,下载最新的非预览版运行时 。
    • 选择 Linux 包管理器说明链接,然后按照 CentOS 说明进行操作 。
  • 一个现有 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 应用 。

备注

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

配置代理服务器Configure a proxy server

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

代理服务器将客户端请求转发到另一个服务器,而不是自身实现这些请求。反向代理转发到固定的目标,通常代表任意客户端。在本指南中,Apache 被配置为反向代理,在 Kestrel 为 ASP.NET Core 应用提供服务的同一服务器上运行。

由于请求是通过反向代理转接的,因此使用 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 以使用代理服务器和负载均衡器

安装 ApacheInstall Apache

将 CentOS 包更新为其最新稳定版本:

  1. sudo yum update -y

使用单个 yum 命令在 CentOS 上安装 Apache Web 服务器:

  1. sudo yum -y install httpd mod_ssl

运行该命令后的示例输出:

  1. Downloading packages:
  2. httpd-2.4.6-40.el7.centos.4.x86_64.rpm | 2.7 MB 00:00:01
  3. Running transaction check
  4. Running transaction test
  5. Transaction test succeeded
  6. Running transaction
  7. Installing : httpd-2.4.6-40.el7.centos.4.x86_64 1/1
  8. Verifying : httpd-2.4.6-40.el7.centos.4.x86_64 1/1
  9. Installed:
  10. httpd.x86_64 0:2.4.6-40.el7.centos.4
  11. Complete!

备注

在此示例中,输出反映了 httpd.86_64,因为 CentOS 7 版本是 64 位的。若要验证 Apache 的安装位置,请从命令提示符运行 whereis httpd

配置 ApacheConfigure Apache

Apache 的配置文件位于 /etc/httpd/conf.d/ 目录内。除了 /etc/httpd/conf.modules.d/ 中的模块配置文件外(其中包含加载模块所需的任何配置文件),将对任何带 .conf 扩展名的文件按字母顺序进行处理。

为应用创建名为 helloapp.conf 的配置文件:

  1. <VirtualHost *:*>
  2. RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
  3. </VirtualHost>
  4. <VirtualHost *:80>
  5. ProxyPreserveHost On
  6. ProxyPass / http://127.0.0.1:5000/
  7. ProxyPassReverse / http://127.0.0.1:5000/
  8. ServerName www.example.com
  9. ServerAlias *.example.com
  10. ErrorLog ${APACHE_LOG_DIR}helloapp-error.log
  11. CustomLog ${APACHE_LOG_DIR}helloapp-access.log common
  12. </VirtualHost>

VirtualHost 块可以在服务器上的一个或多个文件中多次出现。在前面的配置文件中,Apache 接受端口 80 上的公共流量。正在向域 www.example.com 提供服务,*.example.com 别名解析为同一网站。有关详细信息,请参阅基于名称的虚拟主机支持请求会通过代理从根位置转到 127.0.0.1 处的服务器的端口 5000。对于双向通信,需要 ProxyPassProxyPassReverse若要更改 Kestrel 的 IP/端口,请参阅 Kestrel:终结点配置

警告

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

可以使用 ErrorLogCustomLog 指令配置每个 VirtualHost 的日志记录。ErrorLog 是服务器记录错误的位置,CustomLog 则设置文件名和日志文件的格式。在这种情况下,这是记录请求信息的位置。每个请求将各占一行。

保存文件,并测试配置。如果一切正常,响应应为 Syntax [OK]

  1. sudo service httpd configtest

重新启动 Apache:

  1. sudo systemctl restart httpd
  2. sudo systemctl enable httpd

监视应用Monitor the app

Apache 现在已设置为将对 http://localhost:80 发起的请求转发到运行在 http://127.0.0.1:5000 处的 Kestrel 上的 ASP.NET Core 应用。但是,未将 Apache 设置为管理 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 CentOS 7
  3. [Service]
  4. WorkingDirectory=/var/www/helloapp
  5. ExecStart=/usr/local/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=apache
  12. Environment=ASPNETCORE_ENVIRONMENT=Production
  13. [Install]
  14. WantedBy=multi-user.target

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

使用 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

必须转义某些值(例如,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 CentOS 7
  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 进行访问。检查响应标头,服务器 标头表示 ASP.NET Core 应用由 Kestrel 提供服务:

  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 来查看前一小时的条目。有关详细信息,请参阅 journalctl 手册页

  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

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

保护应用Secure the app

配置防火墙Configure firewall

Firewalld 是管理防火墙的动态守护程序,支持网络区域。仍可以使用 iptable 管理端口和数据包筛选。默认情况下应安装 Firewalldyum 可用于安装包或验证是否已安装。

  1. sudo yum install firewalld -y

使用 firewalld 仅打开应用所需的端口。在此示例中,使用的是端口 80 和 443。以下命令将端口 80 和 443 永久设置为打开:

  1. sudo firewall-cmd --add-port=80/tcp --permanent
  2. sudo firewall-cmd --add-port=443/tcp --permanent

重新加载防火墙设置。检查默认区域中可用的服务和端口。通过检查 firewall-cmd -h 获取可用选项。

  1. sudo firewall-cmd --reload
  2. sudo firewall-cmd --list-all
  1. public (default, active)
  2. interfaces: eth0
  3. sources:
  4. services: dhcpv6-client
  5. ports: 443/tcp 80/tcp
  6. masquerade: no
  7. forward-ports:
  8. icmp-blocks:
  9. rich rules:

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) 客户端连接

若要为 Apache 配置 HTTPS,请使用 mod_ssl 模块。安装了 httpd 模块时,也会安装了 mod_ssl 模块。如果未安装,请使用 yum 将其添加到配置。

  1. sudo yum install mod_ssl

若要强制使用 HTTPS,请安装 mod_rewrite 模块以启用 URL 重写:

  1. sudo yum install mod_rewrite

修改 helloapp.conf 文件以启用 URL 重写和端口 443 上的安全通信:

  1. <VirtualHost *:*>
  2. RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
  3. </VirtualHost>
  4. <VirtualHost *:80>
  5. RewriteEngine On
  6. RewriteCond %{HTTPS} !=on
  7. RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
  8. </VirtualHost>
  9. <VirtualHost *:443>
  10. ProxyPreserveHost On
  11. ProxyPass / http://127.0.0.1:5000/
  12. ProxyPassReverse / http://127.0.0.1:5000/
  13. ErrorLog /var/log/httpd/helloapp-error.log
  14. CustomLog /var/log/httpd/helloapp-access.log common
  15. SSLEngine on
  16. SSLProtocol all -SSLv2
  17. SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
  18. SSLCertificateFile /etc/pki/tls/certs/localhost.crt
  19. SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
  20. </VirtualHost>

备注

此示例中使用了本地生成的证书。SSLCertificateFile 应为域名的主证书文件。SSLCertificateKeyFile 应为创建 CSR 时生成的密钥文件。SSLCertificateChainFile 应为证书颁发机构提供的中间证书文件(如有)。

保存文件,并测试配置:

  1. sudo service httpd configtest

重新启动 Apache:

  1. sudo systemctl restart httpd

其他 Apache 建议Additional Apache suggestions

通过共享框架更新重启应用Restart apps with shared framework updates

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

其他标头Additional headers

为了防止恶意攻击,应对一些标头进行修改或添加一些标头。确保已安装 mod_headers 模块:

  1. sudo yum install mod_headers

保护 Apache 免受点击劫持攻击Secure Apache from clickjacking attacks

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

缓解点击劫持攻击:

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

添加行 Header append X-FRAME-OPTIONS "SAMEORIGIN"

  • 保存该文件。

  • 重启 Apache。

MIME 类型探查MIME-type sniffing

X-Content-Type-Options 标头阻止 Internet Explorer 进行 MIME 探查(从文件内容中确定文件的 Content-Type)。如果服务器通过设置 nosniff 选项将 Content-Type 标头设置为 text/html,则不管文件内容为何,Internet Explorer 都会将内容呈现为 text/html

编辑 httpd.conf 文件:

  1. sudo nano /etc/httpd/conf/httpd.conf

添加行 Header set X-Content-Type-Options "nosniff"保存该文件。重启 Apache。

负载平衡Load Balancing

此示例演示如何在同一实例计算机上的 CentOS 7 和 Kestrel 上设置和配置 Apache。为了不出现单一故障点;使用 mod_proxy_balancer 并修改 VirtualHost 可实现在 Apache 代理服务器后方管理 Web 应用的多个实例。

  1. sudo yum install mod_proxy_balancer

在下面所示的配置文件中,helloapp 的其他实例设置为在端口 5001 上运行。“代理” 部分设置了具有两个成员的均衡器配置,以便对 byrequests 进行负载均衡。

  1. <VirtualHost *:*>
  2. RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
  3. </VirtualHost>
  4. <VirtualHost *:80>
  5. RewriteEngine On
  6. RewriteCond %{HTTPS} !=on
  7. RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
  8. </VirtualHost>
  9. <VirtualHost *:443>
  10. ProxyPass / balancer://mycluster/
  11. ProxyPassReverse / http://127.0.0.1:5000/
  12. ProxyPassReverse / http://127.0.0.1:5001/
  13. <Proxy balancer://mycluster>
  14. BalancerMember http://127.0.0.1:5000
  15. BalancerMember http://127.0.0.1:5001
  16. ProxySet lbmethod=byrequests
  17. </Proxy>
  18. <Location />
  19. SetHandler balancer
  20. </Location>
  21. ErrorLog /var/log/httpd/helloapp-error.log
  22. CustomLog /var/log/httpd/helloapp-access.log common
  23. SSLEngine on
  24. SSLProtocol all -SSLv2
  25. SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
  26. SSLCertificateFile /etc/pki/tls/certs/localhost.crt
  27. SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
  28. </VirtualHost>

速率限制Rate Limits

使用 httpd 模块中包含的 mod_ratelimit ,客户端的带宽可以限制为:

  1. sudo nano /etc/httpd/conf.d/ratelimit.conf

示例文件将根位置下的带宽限制为 600 KB/秒:

  1. <IfModule mod_ratelimit.c>
  2. <Location />
  3. SetOutputFilter RATE_LIMIT
  4. SetEnv rate-limit 600
  5. </Location>
  6. </IfModule>

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

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

警告

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

其他资源Additional resources