設置 Ubuntu

安裝 .NET Core Runtime

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb 
sudo dpkg -i packages-microsoft-prod.deb 
sudo apt update 
sudo apt install apt-transport-https 
sudo apt install dotnet-runtime-3.1

安裝 Nginx

新增套件來源,新增檔案 /etc/apt/sources.list.d/nginx.list

#/etc/apt/sources.list.d/nginx.list.
deb https://nginx.org/packages/ubuntu/ focal nginx
deb-src https://nginx.org/packages/ubuntu/ focal nginx

安裝

sudo apt update
sudo apt install nginx -y

啟動、設定開機啟動

# 啟動 nginx
sudo systemctl start nginx
# 設置 nginx 開機啟動
sudo systemctl enable nginx
# 確認 nginx 運行狀態
sudo systemctl status nginx

設置 Nginx 反向代理本機的 5000 連接埠(之後 Kestrel 的 http 服務端口)

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;
    }
}

重新載入 nginx 設定

sudo systemctl reload nginx

設定 kestrel service

將建置好的發佈檔案上傳,假設上傳到/var/www/helloapp dll檔是helloapp.dll

將使用者改成www-data

sudo chmod -R www-data:www-data /var/www/helloapp

新增一個服務檔案/etc/systemd/system/kestrel-helloapp.service,設置如下:

# /etc/systemd/system/kestrel-helloapp.service
[Unit]
Description=Example .NET Web API App running on Ubuntu

[Service]
WorkingDirectory=/var/www/helloapp
# 呼叫安裝的 .net core 環境來執行 helloapp
ExecStart=/usr/bin/dotnet /var/www/helloapp/helloapp.dll
Restart=always
# 如果 .net core 服務崩潰的話 10秒後嘗試重新啟動
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

把服務設定為開機啟動

sudo systemctl enable kestrel-helloapp.service

查看 kestrel service 的執行日誌:journalctl -fu kestrel-helloapp.service

設置 .NET Core 程式碼

// 只在 Linux 啟用 Reverse Proxy 模式 
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });
}

這裡是預設 nginx、運行 .net core 的 Kestrel 都在同一台機器上, 如果使用 127.0.0.0/8 、 [::1]以外的伺服器作為 proxy,也就是說 nginx 在其他機器上, 則需要在 ConfigureServices 裡面像這樣額外設定信任的伺服器:

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

Web api template 預設會將 http 導向至 https 協議 (也就是說會把 5000 重新導向至 5001) 因此 Nginx 如果是反向代理 5000 port, 則需要把 web api 當中的 middleware app.UseHttpsRedirection(); 移除 (或是在 nginx 直接設置反向代理 .net core 的 https port) 安裝 Nginx

如果是用 Web Api 專案搭配前端開發 SPA, 可安裝擴充套件 Microsoft.AspNetCore.SpaServices.Extensions, 並且在 middleware 最後設置路由 app.UseSpa(spa => { });。 也可以不安裝擴充套件改用以下的程式碼手動進行設定:

// 搭配 vue router history mode 重寫路徑請求
app.Use(async (ctx, next) =>
{
   await next();
   bool is404 = ctx.Response.StatusCode == 404;
   bool hasExt = Path.HasExtension(ctx.Request.Path.Value);
   if (is404 && !hasExt)
   {
       ctx.Request.Path = "/index.html";
       ctx.Response.StatusCode = 200;
       await next();
   }
});

強化安全性

設定 https

拿到憑證之後可以進行設定, Nginx 裡面憑證設定方式可以參考 ssl 設定產生器

如果要使用到自簽憑證可以用下面的方式產生:

sudo mkdir /etc/ssl/private
sudo chmod 700 /etc/ssl/private
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

接著在 Nginx.conf 檔案裡面設定:

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

設置防火牆

安裝啟用 Linux 安全性模組 (LSM) 裡面的 ufw 防火牆, 只允許 ssh、http、https(也可以選擇不要允許 http):

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

隱藏 Nginx 資訊

編輯 src/http/ngx_http_header_filter_module.c 變更 Nginx 回應名稱:

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

Reference