CentOS 7 - 手把手部署 ASP.NET Core 網站

Introduction

在本篇文章中,將會:

  • 安裝 CentOS 7 並建立基本環境
  • 安裝 Nginx 並將 ASP.NET Core 包成服務自動啟動
  • 安裝 Certbot 並申請 SSL 憑證使連線允許安全的 HTTPS
  • 透過 Certbot 與 Crontab 使 SSL 證書可以自動更新

Steps of Configuring

Environment

在安裝完 CentOS 7 後,我們首先先設定一下基本環境,以及安裝一些需要用到的 Packages(e.g. SSH, xRDP, dotNET Runtime),關於這個部分可以參考之前的文章 CentOS 7 Environment Setup 以及微軟官方文件,並依照自己的需求操作。

Nginx

ASP.NET Core 內建 kestrel 伺服器讓我們可以不經修改直接搬到 CentOS 上執行,但輕巧的代價就是功能陽春,因此實務上還是需要 Reverse Proxy 來對外提供服務。目前 Linux 上有兩大 Reverse Proxy Server:

  • Apache
  • Nginx

Nginx was written with an explicit goal of outperforming the Apache web server.Out of the box, serving static files, Nginx uses dramatically less memory than Apache, and can handle roughly four times more requests per second.However, this performance boost comes at a cost of decreased flexibility, such as the ability to override systemwide access settings on a per-file basis (Apache accomplishes this with an .htaccess file, while Nginx has no such feature built in).

Wikipedia ── Nginx vs Apache

截至 2018 年 1 月,Nginx 服務或代理了全球 30.46% 的網站。而在比較後我也選擇使用 Nginx 作為我的 Reverse Proxy Server。

安裝並啟動 Nginx

1
2
3
4
5
6
7
8
# Install Nginx
$ sudo yum install nginx

# Automatically start on boot
sudo systemctl enable nginx

# Start Nginx
$ sudo systemctl start nginx

為了讓伺服器可以接受外在連線,我們將 http/https 加入至防火牆規則中:

1
2
3
$ sudo firewall-cmd --permanent --zone=public --add-service=http
$ sudo firewall-cmd --permanent --zone=public --add-service=https
$ sudo firewall-cmd --reload

接著,我們就可以先測試是否可以連線到 CentOS 主機了!

設定 Nginx 組態檔

ASP.NET Core 官方文件上詳細說明了如何設定反向代理伺服器,而做法是直接修改 /etc/nginx/nginx.conf,不過較正確且模組化的作法是為每個站台建立自己的 conf,並放在 /etc/nginx/conf.d 下,舉例而言,建立一個 holey.cc 的站台組態:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo vi /etc/nginx/conf.d/holey.cc.conf
server {
listen 80;
server_name holey.cc;
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;
}
}

在建立完畢之後可以鍵入指令檢查組態,若沒問題則 reload 組態:

1
2
3
4
5
# Check Nginx configs
$ sudo nginx -t

# Reload Nginx configs
$ sudo nginx -s reload

Reload 組態之後可以試連網站看是否正常運作,若出現 502 Bad Gateway 錯誤可能是 Security-Enhanced Linux (SELinux) 禁止了 http 連線導致,可以透過指令來解鎖:

1
$ sudo setsebool -P httpd_can_network_connect on

自動啟動 ASP.NET Core

ASP.NET Core 官方文件上說明了如何將 ASP.NET Core 程式包成服務的方式。以 holey.cc 為例,首先先在 /etc/systemd/system/ 下建立 kestrel-holey.cc.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Unit]
Description=Home Page

[Service]
# Website folder path
WorkingDirectory=/var/www/holey.cc
# ASP.NET Core dll file path
ExecStart=/usr/bin/dotnet /var/www/holey.cc/holey.cc.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
SyslogIdentifier=holey-core
User=wwwroot
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target
  • WorkingDirectory 是網站檔案存放的目錄。
  • ExecStart 第一個參數是 dotnet 執行檔案,第二個參數是網站 DLL 檔。
  • User 是執行服務的身分,需要注意的是 User 必須要有網站目錄的存取權

在設定好 service 後,就可以註冊並啟動服務:

1
2
3
4
5
6
7
8
# Automatically start on boot
sudo systemctl enable kestrel-holey.cc.service

# Start service
sudo systemctl start kestrel-holey.cc.service

# Check the status of a service
sudo systemctl status kestrel-holey.cc.service

檢視紀錄

我們可以透過下面的命令來檢視位於集中式日誌中 kestrel-holey.cc.service 的特定項目:

1
sudo journalctl -fu kestrel-holey.cc.service

若要進一步篩選,例如 --since today--until 1 hour ago 或這些項目的組合等時間選項:

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

Certbot

由於 HTTPS 確保了伺服器與使用者雙方間的通訊安全,HTTPS 已成為許多平台與瀏覽器的最低要求。舉例而言,AppStore 便規定了所有連線都必須是 HTTPS 否則不可上架,或是如 Firefox、Chrome 等瀏覽器會將 HTTP 標示為不安全或未受信任。因此,將網站掛上 HTTPS 是勢在必行的。

HTTP/HTTPS

而現在能夠在網路上申請的免費憑證中,當屬 Let’s Encrypt 較為出名,而 Certbot 便是將整個申請流程化繁為簡並自動化的 Package。

Certbot is an easy-to-use client that fetches a certificate from Let’s Encrypt—an open certificate authority launched by the EFF, Mozilla, and others—and deploys it to a web server.

Cretbot ── Introduction

Install Certbot

1
$ sudo yum install certbot

Modify Nginx config

若是有用過 Let’s Encrypt 申請憑證,對於驗證 your.domain/.well-known/acme-challenge 應該會有印象。為了讓我們的網站可以被驗證,我們必須先對 Nginx config 做一些調整:

1
2
3
4
5
6
$ sudo vi /etc/nginx/conf.d/holey.cc.conf
# Append lines in your server block
location ^~ /.well-known/acme-challenge {
alias /var/www/.well-known/acme-challenge/;
default_type "text/plain";
}

這樣我們讓原本 holey.cc/.well-known/acme-challenge 的要求會對應實際目錄的 /var/www/holey.cc/.well-known/acme-challenge 轉到 /var/www/.well-known/acme-challenge 去。

Get certificate from Let’s Encrypt

在調整完組態後,我們便可以開始申請 SSL 憑證:

1
$ sudo certbot certonly --webroot -w /my/website/folder -d mydomain.name -m my.email@address
  • -w:(必填)網站目錄
  • -d:(必填)要取得憑證的網域(子網域)名稱
  • -m:(選填)當憑證快過期時可以收到通知的電子信箱

在驗證完畢且成功後會出現 Congratulations,而 Private Key 以及 LE Chain 存放路徑也會顯示在訊息內。

需要注意的是,Let’s Encrypt 有申請速率限制,對一般使用者較有影響的是:

  • 每周 50 個註冊域名證書
    假設 holey.cc 為主域名,則每周最多可以申請 50 個 *.holey.cc 的證書。
  • 每周五份的重複證書限制
    若證書包含忽略大小寫與排序而完全相同的主機名,則一周申請限制是五次。

Diffie-Hellman Key Exchange

Diffie-Hellman Key Exchange (D-H) 是著名的密鑰交換協定,目的在於確保通訊雙方可以安全地交換密鑰。需要注意的是它不是演算法,所以不提供加密功能,僅是保護密鑰交換的過程。

Diffie-Hellman Key Exchange

所以為了保護密鑰,我們透過 openssl 建立 2048 長度的 dhparam.pem 檔案並存放至 /etc/ssl/certs/

1
2
# Remember to modify your dhparam.pem path
$ sudo openssl dhparam -out /dhparam/file/path/dhparam.pem 2048

由於在進行 Diffie-Hellman Key Exchange 時,若長度太短則安全性低下,但若太長也可能會影響使用者體驗。因此建議使用 2048 或 4096 的長度會較好。而在產生的過程中會耗用大量的 CPU 資源,因此可視主機性能來決定產生長度。

在建立 dhparam.pem 之後,開啟網站組態並新增:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ sudo vi /etc/nginx/conf.d/holey.cc/conf
# Append lines in your server block
listen 443 ssl http2;
listen [::]:443 ssl http2;
# Remember to modify your fullchain.pem path
ssl_certificate /full/chain/file/path/fullchain.pem;
# Remember to modify your privkey.pem path
ssl_certificate_key /private/key/file/path/privkey.pem;
ssl_protocols TLSv1 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;
ssl_stapling_verify on;
# Remember to modify your dhparam.pem path
ssl_dhparam /dhparam/file/path/dhparam.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

之後重新檢查組態與 reload 讓設定生效即可:

1
2
$ sudo nginx -t
$ sudo nginx -s reload

Automatically renew certificate

由於 Let’s Encrypt 所提供的證書有效期限只有 90 天,因此在過期前就必須重新申請一次。不過我們可以透過Crontab 與 Certbot 來達成自動更新證書:

1
2
3
$ sudo crontab -e
# Append this line
0 3 5 * * /usr/bin/certbot renew --quiet --renew-hook "/bin/systemctl reload nginx"

關於 Crontab 可以參考 Wikipedia 的說明,在本篇的範例是設定為每月五號三點時執行更新證書的動作:

1
2
3
4
5
6
7
8
9
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * command to execute

References