截至目前為止,我們的 .NET Web API 都是使用開發者憑證,瀏覽器並不承認這個憑證,所以每次發 request 到我們的 API,瀏覽器都會警告我們。今天,我們就來介紹一下如何替我們的 Nginx 加上 SSL 憑證,讓我們的瀏覽器不再囉哩囉嗦。
簡單來說,就是替我們的網站申請一張良民證,讓別人信任我們的網站,然後透過加密連線(https)連到我們我們的網站。資安這塊筆者實在沒天分,無法分享更多東西,就連部落格或技術文章也沒找到自己覺得很好啃的,這裡就請邦友自己 Google 找跟自己緣分合得來的資料了。
身為開發者自己玩,當然要盡量當免費仔,SSL 憑證當然也不例外。本篇文章我們要介紹一個廣為人知的免費 SSL 憑證發行機構: Let’s Encrypt。它提供了大多數瀏覽器都承認的免費 SSL 憑證,雖然期限只有 90 天,但是可以免費更新(renew)。
因為每三個月都要手動更新很麻煩,而且有時候忘記更新也可能出問題,所以後來就有一個叫做 Certbot 的軟體來幫我們完成這些麻煩的事,只要在 Linux 上下下指令就能幫我們產生憑證、修改 Nginx 設定檔、自動更新憑證,真香!Let’s Encrypt 官網甚至直接開傳送門,讓我們連到 Certbot 官網跟著步驟做,BUT!!! 筆者分別在今年 4 月跟 8 月都試過 Certbot 官網的步驟,都會出現錯誤,Google 爬了一下文看別人說這是 VM 映像檔的 Hyper-V bug。
筆者最後也找不到方法解決個問題,而是放棄了 Certbot 官網的步驟,改從一篇部落格找到可以 work 的方法,果然高手在民間阿XD
申請 SSL 憑證的時候我們會需要一個固定的 IP 或一個域名(domain name, 簡單的理解就是網址斜線(/)前面那一串啦)。不過,由於免費的憑證發行者不提供純 IP 的憑證,如果要專為一個 IP 申請憑證,不僅要錢申請也麻煩。再加上 SSL 憑證綁定 IP,如果以後我們想把 VM 換到其他地方就又更麻煩,所以筆者還是建議到其他網路供應商看看,趁特價的時候買一個域名。
筆者自己是在 GoDaddy 上面買了一個冷門的域名,第一年好像不到 100 台幣。買完域名之後,需要設定一下 DNS(domain name server),DNS 簡單來說就是讓我們用網址查詢真正 IP 的伺服器,更多關於 DNS 的知識請參考這篇文章。設定 DNS 的介面各家廠商間不盡相同,不過大致上都只要設定這 A 紀錄跟 CNAME,其他的用廠商的預設值就好
筆者自己在 GoDaddy 上的設定如下,供各位參考
終於真正來到要裝 SSL 憑證的步驟了。首先一樣用 SSH 連到我們的 VM,然後透過 yum 安裝 Certbot。
sudo yum install certbot-nginx
與 Nginx 一樣,Certbot 是被包含在 EPEL (Extra Packages for Enterprise Linux) 這個 Package 之中。GCP 的 CentOS 內建這個 EPEL所以可以直接安裝,如果邦友是用 DigitalOcean 的 CentOS,會需要先安裝 EPEL
sudo yum install epel-release
接著,修改我們的 nginx 設定檔,把之前的 server_name 改成我們申請的域名vi sudo vi /etc/nginx/conf.d/ironman.conf
server {
listen 80;
server_name mydomain.com www.mydomain.com; #這邊改成我們申請的域名
# 其他設定不變
}
接著,輸入 Certbot 指令產生並設定憑證sudo certbot --nginx -d mydomain.com -d www.mydomain.com
這裡稍微解釋一下,這個指令的 --nginx 就是叫 Certbot 去找我們的 Nginx 設定檔,幫我們申請 SSL 憑證並自動修改 Nginx 設定。因為我們前面的 Nginx 設定檔中有兩個域名(mydomain.com 與 www.mydomain.com),所以需要輸入兩次 -d 把兩個域名都告訴 Certbot
產生憑證的過程中,certbot 會要求提供幾個資訊
有時候 certbot 會出現莫名的錯誤,重新跑一次指令可能就會過
執行完畢後會發現 Certbot 已經幫我們存好 SSL 憑證檔案,並且修改 Nginx 設定檔
server {
server_name mydomain.tw www.mydomain.tw; # 我們的域名
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;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/goattl.tw/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/goattl.tw/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.mydomain.tw) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = mydomain.tw) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name mydomain.tw www.mydomain.tw;
return 404; # managed by Certbot
}
如果這個時候輸入 https://外部IP/api/User 會發現瀏覽器仍然不信任我們的憑證,點開憑證資訊看,會發現我們的 API 程式還在用 localhost 的開發者憑證。要解決這個問題,我們要修改一下我們的 API 程式,在 Startup.cs 裡使用 UseForwardedHeaders 中介軟體(middleware)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 加入這段
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// 後面不變
}
接著重新發布(publish)我們的 API 程式然後重啟 systemd service,然後再重新連到 https://申請的域名/api/User 就能順利取從我們的 API 取得資料,而且我們的網址上了鎖頭,瀏覽器不再警告了。
設定排成前請注意 GCP VM 預設是 +0 時區,編輯 crontab 排程的時候要自己推算一下時間,或者使用
sudo timedatectl set-timezone Asia/Taipei
把 VM 變成台灣的 +8 時區。
最後,我們要透過 Linux 的排成管理功能 cron
幫我們做自動更新。要編輯排成我們需要編輯自己的 crontabsudo crontab -e
輸入上面這行指令後,會自動開啟 vim 編輯排程內容,在我們的排程內容中加入類似下列的指令。59 23 * * * /usr/bin/certbot renew --quiet
crontab 的排程,前面的五個數字分別代表 分、時、日、月、DayOfWeek(禮拜幾),星號代表不限,以上面的指令為例,cron 會在每天的 23:59 幫我們執行 certbot renew --quiet
指令。後面的路徑幫 cron 找到 certbot,renew 是 certbot 指令,--quiet 表示不要顯示訊息。
飛上雲端架設 VM 的文章暫時告一段落,廷天開始我們要再回到地上,簡單的介紹 MySQL,我們就不用把資料寫死在 code 裡或者自幹讀寫功能了。咱們明天見~