在 Linux 上使用 Apache 裝載 ASP.NET Core

作者:Shayne Boyer

使用本指南,瞭解如何在CentOS 7上將Apache設定為反向 proxy 伺服器,以將 HTTP 流量重新導向至在伺服器上執行的 ASP.NET Core web 應用程式 Kestrelmod_proxy 延伸模組和相關的模組會建立伺服器的反向 Proxy。

必要條件

  • 執行 CentOS 7 的伺服器搭配具有 sudo 權限的標準使用者帳戶。
  • 在伺服器上安裝 .NET Core 執行階段。
    1. 流覽 [下載 .Net Core] 頁面
    2. 選取最新的非預覽 .NET Core 版本。
    3. 在 [ 執行應用程式-運行 時間] 下的表格中下載最新的非預覽執行時間。
    4. 選取 [Linux 套件管理員指示 ] 連結,並遵循 CentOS 指示。
  • 現有的 ASP.NET Core 應用程式。

在升級共用架構之後的任何時間點,重新開機伺服器所裝載的 ASP.NET Core 應用程式。

跨應用程式發佈與複製

架構相依部署設定應用程式。

如果應用程式在本機執行且未設定為進行安全連線 (HTTPS),請採用下列任一方法:

  • 設定應用程式以處理安全的本機連線。 如需詳細資訊,請參閱 HTTPS 組態一節。
  • Properties/launchSettings.json 檔案中的 applicationUrl 屬性移除 https://localhost:5001 (如果有的話)。

從開發環境執行 dotnet publish 將應用程式封裝到可在伺服器上執行的目錄 (例如,bin/Release/<target_framework_moniker>/publish):

dotnet publish --configuration Release

如果您不想在伺服器上維護 .NET Core 執行階段,應用程式也可以發佈為獨立式部署

使用整合至組織工作流程的工具 (SCP、SFTP 等等) 將 ASP.NET Core 應用程式複製到伺服器。 Web 應用程式通常可在 var 目錄下找到 (例如 var/www/helloapp)。

注意

在生產環境部署案例中,持續整合工作流程會執行發佈應用程式並將資產複製到伺服器的工作。

設定 Proxy 伺服器

反向 Proxy 是為動態 Web 應用程式提供服務的常見設定。 反向 Proxy 會終止 HTTP 要求,並將它轉送至 ASP.NET 應用程式。

Proxy 伺服器會將用戶端要求轉送至另一部伺服器,而不是自行履行要求。 反向 Proxy 會轉送至固定目的地,通常代表任意的用戶端。 在本指南中,Apache 設定為在 Kestrel 服務 ASP.NET Core 應用程式的同一部伺服器上執行的反向 proxy。

因為反向 proxy 會轉送要求,所以請使用來自AspNetCore. >microsoft.aspnetcore.HTTPoverrides封裝的轉送標頭中介軟體。 此中介軟體會使用 X-Forwarded-Proto 標頭來更新 Request.Scheme,以便讓重新導向 URI 及其他安全性原則正確運作。

任何依賴配置的元件,例如驗證、連結產生、重新導向和地理位置,都必須在叫用轉送的標頭中介軟體後放置。

轉送的標頭中介軟體應該在其他中介軟體之前執行。 這種排序可確保依賴轉送標頭資訊的中介軟體可以耗用用於處理的標頭值。 若要在診斷和錯誤處理中介軟體之後執行轉送的標頭中介軟體,請參閱 轉送標頭中介軟體順序

UseForwardedHeadersStartup.Configure 呼叫其他中介軟體之前,先在頂端叫用方法。 請設定中介軟體來轉送 X-Forwarded-ForX-Forwarded-Proto 標頭:

// using Microsoft.AspNetCore.HttpOverrides;

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

app.UseAuthentication();

如果未將任何 ForwardedHeadersOptions 指定給中介軟體,則要轉送的預設標頭會是 None

在回送位址 () 上執行的 proxy 127.0.0.0/8, [::1] (包括標準 localhost 位址 (127.0.0.1) )預設為受信任。 如果組織內有其他受信任的 Proxy 或網路處理網際網路與網頁伺服器之間的要求,請使用 ForwardedHeadersOptions,將其新增至 KnownProxiesKnownNetworks 清單。 下列範例會將 IP 位址 10.0.0.100 的受信任 Proxy 伺服器新增至 Startup.ConfigureServices 中「轉送的標頭中介軟體」的 KnownProxies

// using System.Net;

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
});

如需詳細資訊,請參閱設定 ASP.NET Core 以與 Proxy 伺服器和負載平衡器搭配運作

安裝 Apache

將 CentOS 套件更新至其最新穩定版本:

sudo yum update -y

使用單一 yum 命令在 CentOS 上安裝 Apache 網頁伺服器:

sudo yum -y install httpd mod_ssl

執行命令之後的範例輸出:

Downloading packages:
httpd-2.4.6-40.el7.centos.4.x86_64.rpm               | 2.7 MB  00:00:01
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Installing : httpd-2.4.6-40.el7.centos.4.x86_64      1/1 
Verifying  : httpd-2.4.6-40.el7.centos.4.x86_64      1/1 

Installed:
httpd.x86_64 0:2.4.6-40.el7.centos.4

Complete!

注意

在此範例中,輸出會反映 httpd.86_64,因為 CentOS 第 7 版是 64 位元。 若要確認 Apache 的安裝位置,請從命令提示字元執行 whereis httpd

設定 Apache

Apache 的組態檔是位於 /etc/httpd/conf.d/ 目錄內。 除了 /etc/httpd/conf.modules.d/ (包含載入模組所需的所有設定檔) 中的模組設定檔之外,任何副檔名為 .conf 的檔案也會依字母順序處理。

為應用程式建立名為 helloapp.conf 的設定檔:

<VirtualHost *:*>
    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>

<VirtualHost *:80>
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:5000/
    ProxyPassReverse / http://127.0.0.1:5000/
    ServerName www.example.com
    ServerAlias *.example.com
    ErrorLog ${APACHE_LOG_DIR}helloapp-error.log
    CustomLog ${APACHE_LOG_DIR}helloapp-access.log common
</VirtualHost>

VirtualHost 區塊可以在伺服器上的一或多個檔案中出現多次。 在上述設定檔中,Apache 會在連接埠 80 接受公用流量。 所服務的網域是 www.example.com,而 *.example.com 別名則會解析成同一個網站。 如需詳細資訊,請參閱以 名稱為基礎的虛擬主機支援。 要求會在根目錄透過 Proxy 傳送至位於 127.0.0.1 之伺服器的連接埠 5000。 如需進行雙向通訊,則必須要有 ProxyPassProxyPassReverse。 若要變更 Kestrel 的 IP/埠,請參閱 Kestrel :端點設定。

VirtualHost 區塊可以在伺服器上的一或多個檔案中出現多次。 在上述設定檔中,Apache 會在連接埠 80 接受公用流量。 所服務的網域是 www.example.com,而 *.example.com 別名則會解析成同一個網站。 如需詳細資訊,請參閱以 名稱為基礎的虛擬主機支援。 要求會在根目錄透過 Proxy 傳送至位於 127.0.0.1 之伺服器的連接埠 5000。 如需進行雙向通訊,則必須要有 ProxyPassProxyPassReverse。 若要變更 Kestrel 的 IP/埠,請參閱 Kestrel :端點設定。

警告

如果無法在 VirtualHost 區塊中指定適當的 ServerName 指示詞,就會讓應用程式暴露在安全性弱點的風險下。 若您擁有整個父網域 (相對於易受攻擊的 *.com) 的控制權,子網域萬用字元繫結 (例如 *.example.com) 就沒有此安全性風險。 如需詳細資訊,請參閱 rfc7230 章節-5.4

您可以使用 ErrorLogCustomLog 指示詞來依 VirtualHost 設定記錄功能。 ErrorLog 是伺服器記錄錯誤的位置,而 CustomLog 則會設定記錄檔的檔案名稱和格式。 在此案例中,這就是記錄要求資訊的位置。 每個要求都會有一行。

請儲存檔案並測試設定。 如果所有項目都通過,回應應該是 Syntax [OK]

sudo service httpd configtest

重新啟動 Apache:

sudo systemctl restart httpd
sudo systemctl enable httpd

監視應用程式

Apache 現在已設定為將要求轉送至在 http://localhost:80 上執行的 ASP.NET Core 應用程式 Kestrel http://127.0.0.1:5000 。 不過,未設定 Apache 來管理 Kestrel 進程。 請使用 systemd 並建立服務檔案,以啟動並監視基礎 Web 應用程式。 systemd 是一種 init 系統,提供許多強大的功能來啟動、停止及管理進程。

建立服務檔

建立服務定義檔:

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

應用程式的範例服務檔:

[Unit]
Description=Example .NET Web API App running on CentOS 7

[Service]
WorkingDirectory=/var/www/helloapp
ExecStart=/usr/local/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=apache
Environment=ASPNETCORE_ENVIRONMENT=Production 

[Install]
WantedBy=multi-user.target

在上述範例中,管理服務的使用者是由選項所指定 User 。 使用者 (apache) 必須存在,且擁有應用程式檔案的適當擁有權。

使用 TimeoutStopSec 可設定應用程式收到初始中斷訊號之後等待關閉的時間。 如果應用程式在此期間後未關閉,則會發出 SIGKILL 來終止應用程式。 提供不具單位的秒值 (例如 150)、時間範圍值 (例如 2min 30s) 或 infinity (表示停用逾時)。 TimeoutStopSec 在管理員設定檔 (systemd-system.confsystem.conf.dsystemd-user.confuser.conf.d) 的預設值為 DefaultTimeoutStopSec。 大多數發行版本的預設逾時為 90 秒。

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

有些值 (例如 SQL 連接字串) 必須以逸出方式處理,設定提供者才能讀取環境變數。 請使用下列命令來產生要在設定檔中使用並已適當逸出的值:

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

環境變數名稱不支援冒號 (:) 分隔符號。 請使用雙底線 (__) 來取代冒號。 環境變數組態提供者會在將環境變數讀入組態時,將雙底線轉換為冒號。 在下列範例中,連接字串索引鍵 ConnectionStrings:DefaultConnection 會設定為服務定義檔中的 ConnectionStrings__DefaultConnection

環境變數名稱不支援冒號 (:) 分隔符號。 請使用雙底線 (__) 來取代冒號。 環境變數組態提供者會在將環境變數讀入組態時,將雙底線轉換為冒號。 在下列範例中,連接字串索引鍵 ConnectionStrings:DefaultConnection 會設定為服務定義檔中的 ConnectionStrings__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 CentOS 7
    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 設定並管理反向 proxy Kestrel 之後,web 應用程式就會完整設定,並可在本機電腦上的瀏覽器中存取 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

檢視記錄

由於使用的 web 應用程式 Kestrel 是使用 systemd 來管理,因此事件和進程會記錄到集中式日誌中。 不過,此日誌包含 systemd 所管理全部服務和處理序的項目。 若要檢視 kestrel-helloapp.service 的特定項目,請使用下列命令:

sudo journalctl -fu kestrel-helloapp.service

如需進行時間篩選,請搭配命令指定時間選項。 例如,使用 --since today 來針對今天進行篩選,或使用 --until 1 hour ago 來查看前一個小時的項目。 如需詳細資訊,請參閱 journalctl 的手冊頁面 (英文)。

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 並非由使用者程式碼呼叫,仍應設定資料保護,以建立持續密碼編譯金鑰存放區。 如不設定資料保護,金鑰會保留在記憶體中,並於應用程式重新啟動時捨棄。

如果 Keyring 儲存在記憶體中,則當應用程式重新啟動時:

  • 所有 cookie 的驗證權杖都會失效。
  • 當使用者提出下一個要求時,需要再次登入。
  • 所有以 Keyring 保護的資料都無法再解密。 這可能包括CSRF 權杖ASP.NET Core MVC TempData cookie s

若要設定資料保護來保存及加密金鑰環,請參閱:

保護應用程式

設定防火牆

Firewalld 是一個可管理防火牆並支援網路區域的動態精靈。 您仍然可以使用 iptables 來管理連接埠和封包篩選。 預設應該安裝 Firewalld。 您可以使用 yum 來安裝套件或確認是否已安裝套件。

sudo yum install firewalld -y

請使用 firewalld 來僅開啟應用程式所需的連接埠。 在此情況下,會使用埠80和443。 下列命令會將連接埠 80 和 443 永久設定為開啟:

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

重新載入防火牆設定。 檢查預設區域中可用的服務和連接埠。 您可以檢查 firewall-cmd -h 來取得選項。

sudo firewall-cmd --reload
sudo firewall-cmd --list-all
public (default, active)
interfaces: eth0
sources: 
services: dhcpv6-client
ports: 443/tcp 80/tcp
masquerade: no
forward-ports: 
icmp-blocks: 
rich rules: 

HTTPS 設定

設定應用程式以進行安全的本機連線 (HTTPS)

dotnet run 命令使用應用程式的 Properties/launchSettings.json 檔案,其設定應用程式在 applicationUrl 屬性所提供的 URL 上接聽 (例如 https://localhost:5001;http://localhost:5000)。

使用下列其中一種方法,設定應用程式將憑證用在針對 dotnet run 命令的開發,或用在開發環境 (F5,若在 Visual Studio Code 中則為 Ctrl+F5):

設定反向 Prooxy 以進行安全的用戶端連線 (HTTPS)

警告

本節中的安全性設定是一般設定,可作為進一步自訂的起點。 我們無法提供協力廠商工具、伺服器和作業系統的支援。 使用本節中的設定,以您自己的風險。 如需詳細資訊,請存取下列資源:

為了設定適用於 HTTPS 的 Apache,會使用 mod_ssl 模組。 安裝 httpd 模組時,已一併安裝 mod_ssl 模組。 如果未安裝該模組,請使用 yum 將它新增到設定中。

sudo yum install mod_ssl

若要強制執行 HTTPS,請安裝 mod_rewrite 模組來啟用 URL 重寫:

sudo yum install mod_rewrite

修改 helloapp 檔案,以在埠443上啟用安全通訊。

下列範例不會將伺服器設定為重新導向不安全的要求。 建議使用 HTTPS 重新導向中介軟體。 如需詳細資訊,請參閱在 ASP.NET Core 中強制使用 HTTPS

注意

針對伺服器設定處理安全重新導向而非 HTTPS 重新導向中介軟體的開發環境,我們建議使用暫時性重新導向 (302) ,而不是永久重新導向 (301) 。 連結快取可能會在開發環境中造成不穩定的行為。

新增 Strict-Transport-Security (HSTS) 標頭可確保用戶端提出的所有後續要求都是透過 HTTPS。 如需設定 Strict-Transport-Security 標頭的指引,請參閱 在 ASP.NET Core 中強制使用 HTTPS

<VirtualHost *:*>
    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>

<VirtualHost *:443>
    Protocols             h2 http/1.1
    ProxyPreserveHost     On
    ProxyPass             / http://127.0.0.1:5000/
    ProxyPassReverse      / http://127.0.0.1:5000/
    ErrorLog              /var/log/httpd/helloapp-error.log
    CustomLog             /var/log/httpd/helloapp-access.log common
    SSLEngine             on
    SSLProtocol           all -SSLv3 -TLSv1 -TLSv1.1
    SSLHonorCipherOrder   off
    SSLCompression        off
    SSLSessionTickets     on
    SSLUseStapling        off
    SSLCertificateFile    /etc/pki/tls/certs/localhost.crt
    SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
    SSLCipherSuite        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
</VirtualHost>

注意

此範例使用本機產生的憑證。 SSLCertificateFile 應該是網域名稱的主要憑證檔案。 SSLCertificateKeyFile 應該是建立 CSR 時產生的金鑰檔。 SSLCertificateChainFile 應該是憑證授權單位所提供的中繼憑證檔案 (如果有的話)。

需要有 Apache HTTP 伺服器版本2.4.43 或更新版本,才能使用 OpenSSL 1.1.1 來操作 TLS 1.3 web 伺服器。

注意

上述範例會停用 (OCSP) 裝訂的線上憑證狀態通訊協定。 如需啟用 OCSP 的詳細資訊和指引,請參閱 (Apache 檔) 的 Ocsp 裝訂

儲存檔案並測試設定:

sudo service httpd configtest

重新啟動 Apache:

sudo systemctl restart httpd

Apache 的其他建議

使用共用架構更新重新開機應用程式

在伺服器上升級共用架構之後,請重新開機伺服器所裝載的 ASP.NET Core 應用程式。

其他標頭

為了保護免于惡意攻擊,有幾個標頭應該修改或新增。 請確認已安裝 mod_headers 模組:

sudo yum install mod_headers

保護 Apache 免於點閱綁架攻擊

點閱綁架(也稱為「UI 偽裝攻擊」) 是一種惡意攻擊,會誘騙網站訪客點選與其目前所瀏覽頁面不同的頁面上連結或按鈕。 請使用 X-FRAME-OPTIONS 來保護網站安全。

減輕點擊劫持攻擊:

  1. 編輯 httpd.conf 檔案:

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

    新增 Header append X-FRAME-OPTIONS "SAMEORIGIN" 行。

  2. 儲存檔案。

  3. 重新啟動 Apache。

MIME 類型探查

X-Content-Type-Options 標頭可防止 Internet Explorer 執行「MIME 探查」 (從檔案的內容判斷檔案的 Content-Type)。 如果伺服器將 Content-Type 標頭設定為 text/html 並搭配設定 nosniff 選項,則不論檔案內容為何,Internet Explorer 都會將該內容轉譯為 text/html

編輯 httpd.conf 檔案:

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

新增 Header set X-Content-Type-Options "nosniff" 行。 儲存檔案。 重新啟動 Apache。

負載平衡

此範例說明如何在 CentOS 7 和 Kestrel 相同的實例電腦上設定和設定 Apache。 不會有單一失敗點;使用 mod_proxy_balancer 和修改 VirtualHost ,可讓您管理 Apache proxy 伺服器後方的多個 web 應用程式實例。

sudo yum install mod_proxy_balancer

在以下所示的設定檔中,已將一個額外的 helloapp 執行個體設定在連接埠 5001 上執行。 Proxy 區段中設定了平衡器設定,其中有兩個成員為 byrequests 進行負載平衡。

<VirtualHost *:*>
    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>

<VirtualHost *:80>
    RewriteEngine On
    RewriteCond %{HTTPS} !=on
    RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
</VirtualHost>

<VirtualHost *:443>
    ProxyPass / balancer://mycluster/ 

    ProxyPassReverse / http://127.0.0.1:5000/
    ProxyPassReverse / http://127.0.0.1:5001/

    <Proxy balancer://mycluster>
        BalancerMember http://127.0.0.1:5000
        BalancerMember http://127.0.0.1:5001 
        ProxySet lbmethod=byrequests
    </Proxy>

    <Location />
        SetHandler balancer
    </Location>
    ErrorLog /var/log/httpd/helloapp-error.log
    CustomLog /var/log/httpd/helloapp-access.log common
    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
    SSLCertificateFile /etc/pki/tls/certs/localhost.crt
    SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
</VirtualHost>

速率限制

使用 mod_ratelimit (包含在 httpd 模組中) 時,可以限制用戶端的頻寬:

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

此範例檔案將根目錄位置下的頻寬限制為 600 KB/秒:

<IfModule mod_ratelimit.c>
    <Location />
        SetOutputFilter RATE_LIMIT
        SetEnv rate-limit 600
    </Location>
</IfModule>

要求標頭欄位太長

Proxy 伺服器預設設定通常會將要求標頭欄位限制為8190個位元組。 應用程式可能需要比預設 (更長的欄位,例如使用Azure Active Directory) 的應用程式。 如果需要較長的欄位,proxy 伺服器的 LimitRequestFieldSize 指示詞需要調整。 要套用的值取決於案例。 如需詳細資訊,請參閱您的伺服器文件。

警告

除非必要,否則請勿增加 LimitRequestFieldSize 的預設值。 增加此值會提高緩衝區溢位及惡意使用者發動拒絕服務 (DoS) 攻擊的風險。

其他資源