Morris's blog

部落格建置過程5 - 透過Let'sEncrypt公司取得免費的SSL證書,製作HTTPS連線

2018-08-22

HTTP"S", "S" means secured

  • 每當瀏覽器連上一個網站後進行一次又一次的HTTP請求與接收回應,就是一次又一次的資料交換,這些過程的資料應該要被加密,以防止有心人士在網路傳輸過程之間攔截資料,進而修改或是竊取資料。
  • HTTPS就是HTTP通訊協定的"加密版",透過一些瀏覽器與伺服器之間的密鑰交換來加密資料與解密資料。

以前要將網站連線升級成HTTPS的話...

  • 在以前,人們覺得將網站設置HTTPS是一件花費不斐又辛苦的事,因為必須要固定每隔一段時間就要續約HTTPS的憑證,又要每隔一段時間就要更新,一般的個人網站或是一些小企業的網站大部分的維護人員都不多,實在很難有心力跟財力持續做這些事情。
  • 但是這件事就在一個偉大的組織的成立之後有所改變。

Let's Encrypt

Let's Encrypt是一個於2015年三季度推出的數位憑證認證機構,  
旨在以自動化流程消除手動建立和安裝憑證的複雜流程,  
並推廣使全球資訊網伺服器的加密連接無所不在,為安全網站提供免費的SSL/TLS憑證。...

以上取自Let's Encrypt的維基百科頁面

  • Let's Encrypt推行著HTTPS every where,讓大家上網都是加密連接的,保護你我的資料的安全,不讓邪惡的中間人能夠解讀我們傳輸中的資料。
  • 我們將會使用特定的軟體叫acme.sh,幫助人們能夠將其網站自動向Let's Encrypt申請憑證與安裝憑證在server上,並能夠有自動更新憑證的功能。
  • 像是這樣的軟體,一開始就是針對Linux環境的Server推出的,所以在我們的Digital Ocean的Linux VPS主機可以適用。

先前準備工作

  1. 本安裝過程假設使用者的OS是CentOS 7,並且已經設定好一個可以用sudo指令取得root權限的使用者。
  2. 必須要有一個自己擁有的網域名稱(就是上一篇文章在教的事情)。
  3. 這個網域名稱已經設定好A指向到你的VPS主機(也是上一篇文章在教的事情)。
  4. 最好按照先前的文章的事情都已經做過了,像是安裝好Nginx。因為我們的HTTPS機制會建構在Nginx上。 如果以上這些條件都滿足的話,那就已經準備好,可以來為VPS主機上的服務來建立HTTPS連線了。

目標:安裝萬用字元憑證

  • 有種東西叫子網域
  • 假設現在我們的domain是example.com, 部落格的網址我們可以弄成blog.example.com, 架在上面的某個app可以弄成app.example.com,讓網址一眼就能看出功能。這就是子網域。只要是屬於你的domain,你可以在前面創出無數個子網域。
  • 但這些網址我們都想要是HTTPS連線的。
  • 一般說一個子網域就要為它申請一張憑證,但是我們當我們有N個子網域時,那我們就要申請N張憑證?
  • 我們可以申請萬用字元(wildcard)憑證,來解決這個問題。
  • 等等在申請時我們會申請類似這樣子的憑證 -d example.com -d *.example.com,網域前面有一個*號。

要用什麼工具來做到自動(幾乎)申請憑證,而且還要能自動更新?

  • 前一版的文章中有介紹過certbot,它是Electronic Frontier Foundation開放出來的一個會自動跟Let's Encrypt申請SSL憑證的軟體。
  • 但是不知道為什麼我總是無法讓它能自動更新憑證,所以這次要使用另一個軟體叫acme.sh,它是一個熱情的中國開發者發起的一個專案,要來幫助使用者自動化地向Let's Encrypt申請憑證,而且可以申請萬用字元的。

確定有設定好Nginx,並且目前是在背景常駐中

  • 可以按照先前的這篇文章來做基本的設定。按照文章中來做,也會將防火牆的HTTP跟HTTPS的預設port有設定成允許存取。

再設定一下Nginx的設定檔裡的Server區塊

  • 按照上次的設定的話,我們目前自己對Nginx的設定是寫在/etc/nginx/conf.d/default.conf這個設定檔裡, 用文字編輯器打開它,

    sudo vim /etc/nginx/conf.d/default.conf

找到server區塊,在server_name屬性裡填入上一篇文章中申請好的Domain,在這裡我們先假設我們申請的domain叫example.com,打上domain本身與前面加上*號的,空白隔開。

 server{

    listen       80;
    server_name  example.com *.example.com;  #增加的部分

   ### .....以下省略
 }

改完後存檔離開。

:x

再用這個指令檢查設定檔是否都正確無誤

sudo nginx -t

再讓Nginx重新讀取設定檔

sudo systemctl reload nginx

安裝acme.sh

curl  https://get.acme.sh | sh

再重新登入一下,讓新安裝的acme.sh生效

logout

到Digital Ocean的管理後台,生成一組api key準備給acme.sh使用

  • 為什麼我們要去DG申請這個APIKey? 因為在上一篇文章中,我們domain的cname, A指向那些是設定在DG裡的,如果你設定在GoDaddy底下,就要生成那裡的ApiKey, 以此類推。
  • 申請萬用字元憑證時,會有一個動作,就是憑證單位會要你新增兩筆key叫_acme-challengetxt類型的DNS Record,到你的網域底下,以讓它確定這個網域真的是你這個申請人的。
  • acme.sh需要你提供給它的有讀寫你的網域的DNS Record的apiKey以幫你做這件事情。
  • 進入到DG後台後,按下左下角的API apiKey
  • 之後會到類似下圖的頁面,找到personal acess token區域,按下generate acess token accessToken
  • 輸入一個名稱,記住下面的讀寫權限都要打勾 newAccessToken
  • 之後會產生出一組文數字,這就是apiKey,趕快複製起來,注意不要多反白到空格,或少複製到某個字...

回到VPS的terminal上,執行acme.sh來取得憑證

  • 在terminal上執行

    export DO_API_KEY="....那個APIKey"

    這個動作是把一個叫DO_API_KEY的變數曝露到目前的ssh session底下,等等acme.sh在執行的時候,就會抓取並拿去使用,如果是別間DNS廠商的話,這個export的變數名稱就不相同,其他DNS業者的設定方式 可參考acme.sh的文件

  • 接著申請憑證

    acme.sh --issue --dns dns_dgon -d example.com -d *.example.com

    注意到第二個-d後面是*.example.com 因為我們要申請的是萬用字元的憑證

  • 執行完後會看到這樣的畫面

    [Web May 8 22:36:45 CST 2019] Your cert is in /home/yourusername/.acme.sh/yourdomain.com/yourdomain.com.cer
    [Web May 8 22:36:45 CST 2019] Your cert key is in /home/yourusername/.acme.sh/yourdomain.com/yourdomain.com.key
    [Web May 8 22:36:45 CST 2019] The intermediate CA cert is in /home/yourusername/.acme.sh/yourdomain.com/ca.cer
    [Web May 8 22:36:45 CST 2019] And the full chain certs is there: /home/yourusername/.acme.sh/yourdomain.com/fullchain.cer

    acme.sh 告訴你它把申請好的東西放到哪裡了,它在~/底下創了一個.acme.sh資料夾,然後再用你的domain名稱在那底下再創了一個資料夾,放進了這四個檔案,
    由上往下依序是

    1. 你的網域的憑證
    2. 你的網域的私鑰
    3. 中繼憑證
    4. fullChain

準備將上面的那些東西跟Nginx結合在一起

首先我們要先用acme.sh--installcert指令

acme.sh  --installcert  -d  <domain>.com   \
        --key-file   /etc/nginx/ssl/<domain>.key \
        --fullchain-file /etc/nginx/ssl/fullchain.cer \
        #--reloadcmd  "service nginx force-reload"

-d, --key-file, --fullchain-file後面要換成自己要放的地方,<domain>請換成自己的domain,上面的指令是放在Nginx預設的安裝目錄底下,
這個動作非常重要,一定要用這個指令來執行,不可以自己複製,因為在執行下去的過程中,acme.sh會建立起之後能夠自動讓它自動幫你更新憑證的機制。

接著打開Nginx的設定檔!

  • 預設的設定檔是在 /etc/nginx/conf.d/default.conf
  • 所以就:

    sudo vim /etc/nginx/conf.d/default.conf
  • 找到http 80``server block結束的地方,再加上另一組httpsserver設定
 } #  ... 80的server block的結束大括號
server{

    server_name  example.com www.example.com;
    listen 443 ssl http2;                          #http2 可以順便寫上去 
    ssl_certificate /etc/nginx/ssl/fullchain.cer;  #就是上面的  --fullchain-file後面的值
    ssl_certificate_key /etc/nginx/ssl/<domain>.key; #就是上面的  --key-file後面的值  
    # 下面這四行是更加強ssl機制的設定
    ssl_session_cache shared:le_nginx_SSL:1m;
    ssl_session_timeout 1440m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    
    # 可能還會有一些額外的location的設定
}  #這個server區塊的結束大括號
  • 寫完後,ESC然後:x存檔並離開,先檢查設定檔格式是否正確:

    nginx -t

再讓Nginx重新讀取設定

sudo systemctl force-reload nginx

這邊用的是force-reload,因為只用reload有可能會讓Nginx不會重新讀取憑證設定

更新 迪菲-赫爾曼(Diffie-Hellman) 參數

  • 如果你現在使用SSLLabs這個網站,輸入你的網址,它會去測試你的網站的HTTPS連線的安全性強度,你會發現你只能拿到B等級的分數,這是為什麼?

原因就是你的迪菲-赫爾曼(Diffie-Hellman) 參數的強度太弱了。

迪菲-赫爾曼密鑰交換(英語:Diffie–Hellman key exchange,縮寫為D-H) 是一種安全協定。
它可以讓雙方在完全沒有對方任何預先資訊的條件下通過不安全信道建立起一個金鑰。
這個金鑰可以在後續的通訊中作為對稱金鑰來加密通訊內容......

以上內容取自 維基百科 - 迪菲-赫爾曼密鑰交換

  • 這個參數的強度會影響到Server跟client初次建立連線時密鑰交換時的安全性,我們可以重新在我們的VPS上計算一個強度更強的參數,存在一個dhparam.pem檔裡,然後在我們的Nginx的設定檔的Server區塊裡使用它。

  • 透過openssl這個OS自帶的程式來產生這個檔案

    sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

    2048這個數字代表著計算的複雜程度,數字越大強度越強,算出來的時間也要花得越久。

  • 計算完之後,再來就是要在Nginx的設定檔套用它。打開我們的Nginx設定檔

    sudo vim /etc/nginx/conf.d/default.conf

    你會發現Certbot早就在你的設定檔裡加上了許多東西,找到 server區塊,找到ssl_dhparam這個參數,把值換成我們剛剛用openssl算完的那個檔案:

    .....
    server{
    ### .....其他部分省略
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    }

    存檔並離開後,再用這個指令檢查設定檔是否都正確無誤

    sudo nginx -t

再讓Nginx重新讀取設定檔

sudo systemctl reload nginx

你的網站現在更安全多了,再透過SSLLabs檢查一次,現在應該至少可以得到A等級。

設定憑證自動更新

Let's Encrypt的證書雖然是免費的,但是只有90天的有效期限,我們再來就是要設定每當證書失效時,我們可以自動重新跟Let's Encrypt申請證書。acme.sh在申請證書的同時,也同時幫我們設定好自動更新證書的排程了。

  • unix like的作業系統裡的排程管理 - cron

    $ crontab -l
    0 0 * * * "/home/ubuntu/.acme.sh"/acme.sh --cron --home "/home/ubuntu/.acme.sh" > /dev/null

手動更新一下證書,看看流程是否一切正確

acme.sh --cron -f

應該會重新申請了一次證書,並在最後自動重啟Nginx,而且不需要我們手動輸入密碼。

就這樣,我們的網站的HTTPS的設定就配置好了

要知道的地方是:

  • 在這個過程中,我們使用了acme.shdns api來幫我們處理了申請憑證的瑣碎動作,。
  • 我們網站的HTTPS是跟Nginx互相搭配的。
  • acme.sh主要幫我們做了兩件事:

    1. 使用我們提供給它的api-key,連接我們的dns holder,幫我們加上兩筆_acme.challengetxt紀錄。
    2. 幫我們向Let's enctrypt申請證書。。

more reference

-- end of file--

response