翻譯 - ASP.NET Core 託管和部署 - 在 Linux 上使用 Nginx 託管 ASP.NET Core 網站

翻譯自 https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-5.0

本文介紹了在 Ubuntu 16.04 服務器上設置生產環境可用的 ASP.NET Core 環境。這裏的介紹對於更新版本的 Ubuntu 可能也會工作,但是並沒有在更新版本的服務器上測試。

更過關於 ASP.NET Core 只是的 Linux 發行版,請查看 Prerequisites for .NET Core on Linux

注意:

對於 Ubuntu 14.04,建議使用 supervisord 監控 Kestrel 進程作爲解決方案。對於 Ubuntu 14.04 的介紹,可以該話題的之前版本。

本指南包含以下內容:

  • 使用反向代理服務器放置一個現存的 ASP.NET Core 應用程序
  • 設置反向代理服務器將請求轉發到 Kestrel web 服務器
  • 保證 web 應用程序在啓動時作爲守護進程(daemon)運行
  • 配置一個進程管理工具幫助 web 應用程序重新啓動

先決條件

1. 使用帶有 sudo 權限的標準用戶賬號訪問 Ubuntu 16.04 服務器

2. 在服務上安裝 .NET Core 運行時。

    a. 訪問 Download .NET Core page

    b. 選擇一個最新非預覽版的 .NET Core 版本

    c. 下載表格中 Run apps - Runtime 最新非預覽版本

    d.  選擇 Linux Package manager instructions 鏈接,按照你的版本的 Ubuntu 的說明進行操作

3. 一個現存的 ASP.NET Core 應用程序

之後的任何時候,在升級完 shared framework 後,需要重新啓動服務器託管的 ASP.NET Core 應用程序。

發佈和複製應用程序

配置應用程序爲框架獨立的部署。

如果應用程序在本地運行,並且沒有配置安全連接(HTTPS),可以安裝下面任意一種途徑解決:

  • 配置應用程序處理安全的本地連接。更多信息請查看 HTTPS configuration
  • 從文件 Properties/launchSettings.json 中的屬性 applicationUrl 中移除 https://localhost:5001(如果存在的話)

在開發環境中運行 dotnet publish 打包應用程序到一個可以運行在服務器上的目錄 (例如,bin/Release/{TARGET FRAMEWORK MONIKER}/publish,佔位符 {TARGET FRAMEWORK MONIKER} 是目標框架名稱) 中:

dotnet publish --configuration Release

如果你不想在服務器上維護 .NET Core 運行時,應用程序也可以被髮布爲自包含部署(self-contained deployment)。

使用組織工作流中的工具(例如,SCP,SFTP)複製 ASP.NET Core 應用程序到服務器。通常把 web 應用程序放到 var 目錄(例如:var/www/helloapp)。

注意

在生產部署環境中,一個持續集成的工作流完成發佈和複製資源到服務器上。

測試服務器:

1. 從命令行運行應用程序:dotnet <app_assembly>.dll

2. 在瀏覽器中,導航到 http://<serveraddress>:<port> 驗證應用程序正常運行

 配置反向代理服務器

 反向代理通常用來設置動態 web 應用程序服務。一個反向代理終結 HTTP 請求並轉發給 ASP.NET Core 應用程序。

使用反向代理服務

Kestrel 從 ASP.NET Core 服務動態內容是強大的,然而,web 服務能力並沒有像 IIS,Apache,或者 Nginx 有很多特性。一個反向代理服務器可以從 HTTP 服務器分擔一些工作,例如服務靜態內容,緩存請求,壓縮請求和 HTTPS 終結。反向代理服務器可能部署在專用機器上,也可能和 HTTP 服務器部署在同一臺機器上。

出於本指南的目的,一個單獨的 Nginx 實例被使用。它和 HTTP 服務運行在同一臺服務器上。根據需求,不同的設置會被選擇。

因爲請求都被反向代理轉發,使用 Microsoft.AspNetCore.HttpOverrides 包中的中間件 Forwarded Headers Middleware。這個中間件使用 X-Forwarded-Proto header 更新了 Request.Scheme,所以重定向 URIs和其它安全策略工作正確。

Forwarded Headers Middleware 應該在其它中間件之前運行。這個順序保證了依賴 forwarded headers 信息的中間件可以在處理過程中使用 header 的值。在 diagnostics 和 錯誤處理中間件之後運行 Forwarded Headers Middleware,查看 Forwarded Headers Middleware order

在調用其它中間件之前,在 Startup.Configure 的頂部調用 UseForwardedHeaders。配置中間件轉發 X-Forwarded-For 和 X-Forwarded-Proto headers:

using Microsoft.AspNetCore.HttpOverrides;

...

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

app.UseAuthentication();

如果沒有中間件沒有指定 ForwardedHeadersOptions,默認轉發的 headers 是 None。

代理運行在迴路地址 (127.0.0.0/8, [::]),包含標準本地地址 (127.0.01),默認是被信任的。如果其它的代理或者組織內的網絡處理網絡和 web 服務器之間的請求,可以使用 ForwardedHeadersOptions 把它們添加到 KnownProxies 或者 KnownNetworks 列表中。下面的實例在 Startup.ConfigureServices 中添加了一個 IP 地址爲 10.0.0.100 可信任的代理到 Forwarded Header Middleware KnownProxies 中。

using System.Net;

...

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

更多信息查看 Configure ASP.NET Core to work with proxy servers and load balancers

安裝 Nginx

使用 apt-get 安裝 Nginx。安裝器創建一個 systemd 初始化腳本啓動 Nginx 作爲守護進程。按照下面 Ubuntu 安裝 Nginx 說明操作:Official Debian/Ubuntu packages

注意:

如果要求可選的 Nginx 模塊,可能需要從源碼編譯 Nginx。

由於 Nginx 是第一次安裝,運行下面的命令顯式啓動:

sudo service nginx start

通過瀏覽器顯示 Nginx 默認加載頁驗證 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://localhost: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;
    }
}

如果應用程序是 SingalR 或者 Blazor Server app,查看 ASP.NET Core SignalR production hosting and scaling 和 Host and deploy ASP.NET Core Blazor Serve

如果沒有 server_name 匹配,Nginx使用默認服務。如果沒有定義默認的服務,配置文件中的第一個服務作爲默認服務。作爲最佳實踐,在配置文件中添加一個返回 444 狀態碼的默認服務。一個默認的配置示例如下:

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

前面的配置文件和默認服務,Nginx 接受端口 80 上主機頭爲 example.com 或者 *.example.com 的流量。不匹配這些主機的請求將不會被轉發到 Kestrel。Nginx 轉發匹配的請求到 Kestrel 的 http://localhost:5000。更多信息查看 How nginx processes a request。更改 Kestrel 的 IP/端口。查看 Kestrel: Endpoint configuration

注意:

沒有合適的指定 server_name 指令會暴露你的應用程序的安全弱點。子域通配符綁定(例如,*.example.com)並不會造成安全問題,如果你控制了全部的父域(而不是 *.com,這個存在隱患)。更多信息查看rfc7230 section-5.4.。

Nginx 配置建立後,運行 sudo nginx -t 驗證配置文件是否有語法錯誤。如果配置文件測試成功,可以運行 sudo nginx -s reload 強制 Nginx 使用修改後的配置。

 直接在服務器上運行應用程序:

1. 導航到應用程序目錄

2. 運行應用程序:dotnet <app_assembly.dll>,app_assembly.dll 是應用程序程序集的文件名稱。

如果應用程序在服務器上運行成功,但是通過網絡訪問失敗,可以檢查服務器的防火牆確認80端口已經打開。如果使用的是 Azure Ubuntu VM,添加一個網絡安全組(NSG)規則確保80端口入站流量。不需要使能80端口出站規則,因爲出站流量在入站規則使能的視情況會自動保證使能。

完成應用程序的測試後,Ctrl + C 關閉應用程序。

監視應用程序

服務器被設置爲轉發指向 http://<serveraddress>:80 的請求到運行在 Kestrel 地址爲 http://127.0.0.1:5000 的 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 禁用超時。TimeoutStopSed 默認值是 DefaultTimeoutStopSec 的值,存在於配置文件 (systemd-system.conf,system.conf.d,systemd-user.conf,user.conf.d)。大部分發行版的默認超時時間是 90 秒。

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

Linux 文件系統區分大小寫。Production 被設置爲 ASPNETCORE_ENVIRONMENT 會使得搜索配置文件 appsetting.Production.json,而不是appsetting.production.json。

某些值(例如, SQL 連接字符串)必須轉義才能被配置提供器去讀取環境變量。使用下面的命令生成一個在配置文件中使用的合適的轉義值:

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

環境變量名稱不支持冒號(:)分隔符。使用雙下劃線(__)代替冒號。Environment Variables configuration provider 在環境變量被讀入配置的時候會轉換雙下劃線爲冒號。在下面的示例中,連接字符串鍵值 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 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

使用反向代理配置,Kestrel 通過 systemd 管理,web 應用程序已經完全配置好,可以在本機的瀏覽器中訪問 http://localhost。也可以通過遠程主機訪問,除非是有防火牆的阻塞。檢查返回頭部,Server 頭部顯示的是 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-ledinpro.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 Data Protection stack 被多個 ASP.NET Core 中間件使用,包含認證中間件(例如,cookie 中間件)和跨站請求僞造(CSRF)保護。即使數據保護 APIs 不被用戶代碼調用,數據保護也應該創建一個持久加密的鍵值存儲配置。如果數據保護沒有配置,在內存中的鍵值在應用程序重啓的時候就會被丟棄。

如果 key ring 存儲在內存中,當應用程序重啓的時候就會:

  • 所有基於 cooked 的認證 tokens 都會失效
  • 用戶在他們下一次請求的時候會被要求再次登錄
  • 任何使用 key ring 的數據保護不再能被解密。這可能包含 CSRF tokens 和 ASP.NET Core MVC TempData cookies

爲了配置數據保護持久化和加密 key ring,請查看:

長請求頭部區域

代理服務器默認設置根據平臺請求頭部區域限制通常是 4K 或者 8K 大小。應用程序可能要求比默認大小更長的請求頭部(例如,使用 Azure Active Directory 的應用程序)。如果更長的請求頭部要求,代理服務器的默認設置就需要調整。應用的數值根據情況而定。更多信息,請查看服務器文檔:

注意:

除非有必要,否則不要增加代理 buffers 的大小。增加這些值增大了 buffer 溢出的風險和 被惡意用戶的拒絕服務 Denial of Service(Dos) 攻擊。

保護應用程序

使能 AppArmor

Linux Security Modules(LSM) 是一個框架,自 Linux 2.6 版本依賴就是 Linux 內核的一部分。LSM 支持安全模塊的不同實現。AppArmor 實現了 Mandatory Access Control 系統的一種 LSM,它允許限制程序訪問有限的資源集合。確保 AppArmor 使能是合適的配置。

配置防火牆

關閉所有用不到的端口。Uncomplicated firewall (ufw) 通過提供了 CLI 配置防火牆爲 iptable 提供了一個前端。

警告:

如果配置不正確,防火牆將會阻止訪問整個系統。錯誤的指定 SSH 端口將會將你鎖定在系統外,如果你使用 SSH 去連接它。默認端口是 22。更多信息請查看 introduction to ufw 和 manual

安裝 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;

配置選項

使用更多要求的模塊配置服務。考慮使用 web 應用程序防火牆加固應用程序,例如 ModSecurity

HTTPS 配置

配置應用程序安全 (HTTPS) 的本地連接

dotnet run 命令使用應用程序的 Properties/launchSettings.json 文件,這個文件配置應用程序在由 applicationUrl 屬性提供的 URLs 上面監聽。例如,https://localhost;http://localhost:5000。

配置應用程序在開發中 dotnet run 命令或者在開發環境中(F5 or Ctrl + F5 在 Visual Studio Code)  中使用一個證書,可以使用以下途徑之一:

配置反向代理安全(HTTPS)的客戶端連接

  • 通過指定一個受信任的證書頒發機構的有效證書,配置服務在 443 端口上監聽 HTTPS 流量
  • 利用 /etc/nginx/nginx.conf 文件中的一些實踐描述加強安全。例子包含了選擇一個強加密和重定向HTTP上所有的流量到 HTTPS。

注意

對於開發環境,我們建議使用臨時重定向(302)而不是永久重定向(301)。鏈接緩存可能在開發環境中導致不穩定的行爲。

  • 添加一個 HTTP Strict-Transport-Security(HSTS)頭部保證客戶端隨後的所有請求都使用 HTTPS。
    更多關於 HSTS 的指南,請查看 Enforce HTTPS in ASP.NET Core
  • 如果在以後不使用 HTTPS 了,可以使用下面其中之一的方法:
    1. 不要添加 HSTS 頭部
    2. 選擇一個更短的 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 helloapp{
        server localhost:5000;
    }

    server {
        listen     80;
        return     301 https://$host$request_uri;
    }

    server {
        listen                    443 ssl;
        server_name               example.com *.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://helloapp;
            limit_req  zone=one burst=10 nodelay;
        }
    }
}

注意:

Blazor WebAssembly 應用程序要求更大的 burst 參數以適應應用程序更大數量的請求。更多信息,查看 Host and deploy ASP.NET Core Blazor WebAssembly

Secure Nginx from clickjacking

Clickjacking,也被稱爲界面不久攻擊,是一種惡意攻擊,訪客被欺騙在一個不同的頁面上點擊一個鏈接或者按鈕,而不是在當前正在訪問的頁面。使用 X-FRAME-OPTIONS 保護站點。

爲了減輕點擊劫持攻擊:

1. 編輯 nginx.conf 文件:

sudo nano /etc/nginx/nginx.conf

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

2. 保存文件

3. 重啓 Nginx

MIME-type 嗅探

這個頭部阻止大多數的瀏覽器嗅探一個離開聲明內容類型的返回,由於頭部指示瀏覽器不要覆蓋返回內容的類型。使用 nosniff 選項,如果服務說內容是 text/html,那麼瀏覽器就渲染爲 text/html。

1. 變價 nginx.conf 文件:

sudo nano /etc/nginx/nginx.conf

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

2. 保存文件

3. 重啓 Nginx

額外的 Nginx 建議

在升級完服務器上共享的框架,重新啓動服務器託管的 ASP.NET Core 應用程序。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章