讓 HAProxy 也可以 Let's Encrypt
越來越多的服務要求使用 https
, 在網路上混的 HAProxy
當然也要聲援一下.
HAProxy
自從 1.5
版後就支援 SSL
. 但每次到期要手動申請 renew
也是一項不人性的作業.
不出意外, 只要有這種長時間後才要重複作業的工作, 一定會有 莫非
的出現.
還好關於申請 ssl 證書有人推出了自動化服務, 特別它是免費的.
Let’s Encrypt
在 2015 年底正式提供這個服務. 所以我們的目標便是將兩者整合在一起, 期快樂無比.
Let’s Encrypt 首頁大大寫著
Let’s Encrypt is a free, automated, and open Certificate Authority.
不知你最先注意到的是 free
, open
還是 automated
.
free 跟 open 應該是這個網路網路時代的顯學. 但很多人沒注意到的是 automated(自動化)
才是幕後功臣.
然而畢竟是自動化, 總不能要自己還要複製貼上的輸入資料吧. 所以, 要讓你的 Let’s Encrypt
申請自動化,
首先你的 Web Server 要支援
Automatic Certificate Management Environment (ACME 目前還是草稿階段).
而現在要讓 HAProxy
支援 ACME
, 但(臣妾)辦不到(目前). 或許你可以利用崁入式語言
Lua
為他寫一個支援 ACME
的程式. 使用
haproxy -vv|grep Lua
檢查你的 HAproxy
是否支援 Lua
.
不想如此麻煩, 就必須另外使用工具.
Let’s Encrypt
推薦的首選工具是 certbot
.
現在我們要借用 certbot
來達成 HAProxy
支援 ACME
的功能.
首先下載 certbot
, 你可以簡單的使用 sudo apt-get install certbot
下載安裝現有的封裝版本.
或註冊 certbot
的個人套件庫取得最新的版本.
$ sudo apt-get update
$ sudo apt-get -y install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get -y install certbot
理所當然的你應該有一個合法的註冊網域和正在執行的 HAProxy
伺服器.
要求新授權證書
因為是要求新授權, 理所當然認為你現在沒有證書, 也就沒有辦法使用 https
,
所以 Let’s Encrypt
是透過 port:80
與 Client 溝通.
我們可以將 HAProxy
設定成將透過 port:80
進來且路徑是
/.well-known/acme-challenge/
的 request
傳給 certbot
.
並選擇一個空的 port number
給 certbot
使用. 這裡使用的是 port:8888
.
frontend http
bind *:80
acl acme path_beg /.well-known/acme-challenge/
use_backend acme-backend if acme
backend acme-backend
server certbot 127.0.0.1:16523
改好 HAProxy
的設定檔後, 重新啟動 HAProxy
.
sudo certbot certonly --standalone -d your.domain.name \ --non-interactive --agree-tos \ --email yourAdmin@your.domain.name \ --http-01-port=16523
這裡的 --http-01-port=16523
要跟
HAProxy
設定檔中 server certbot 127.0.0.1:16523
的 port
相同.
成功會出現
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your.domain.name/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your.domain.name/privkey.pem Your cert will expire on yyyy-mm-dd. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew"
之類的說明文字. 如果有出現錯誤, 請再依說明處理.
IMPORTANT NOTES: - The following errors were reported by the server:
將產生的兩個檔案 /etc/letsencrypt/live/your.domain.name/fullchain.pem
和
/etc/letsencrypt/live/your.domain.name/privkey.pem
合併成一個檔案.
要注意合併的先後順序.
$ sudo mkdir -p /etc/ssl/
$ sudo cat /etc/letsencrypt/live/your.domain.name/fullchain.pem \
/etc/letsencrypt/live/your.domain.name/privkey.pem \
| sudo tee /etc/ssl/your.domain.name/your.domain.name.pem
將該檔案設給 HAProxy
的 ssl crt
.
因為現有有了 ssl 證書, 所以我們可以將 HAProxy
監聽 port 443
的設定加上.
同時現在也可以對 Let’s Encrypt
要求 renew
了. 當然因為是 renew
所以
ACME
是透過 443 port
來要求的.
frontend https
bind *:443 ssl crt /etc/ssl/your.domain.name/your.domain.name.pem
acl acme path_beg /.well-known/acme-challenge/
use_backend acme-backend if acme
改好 HAProxy
的設定檔後, 重新啟動 HAProxy
.
要求更新授權證書
certbot
要求更新證書是很簡單的. 只要執行 certbot renew
,
其他參數都會延續之前要求新授權證書時的設定.
但因為我們現在是透過 HAProxy
來轉給 certbot
, 所以跟之前監聽 port 443
一樣,
必須把 443 port
下, 路徑是 /.well-known/acme-challenge/
轉給 certbot
處理.
Let’s Encrypt
發的證書, 有效是 90
天, 而 certbot
並不是系統常駐程式,
而是做完工作就結束了. 所以我們必須在到期前, 再執行一次 certbot renew
, 取得新證書,
並讓 HAProxy
載入新證書, 才不會過期.
$ sudo /usr/bin/certbot renew
--force-renewal \ // (1)
--tls-sni-01-port=16523 \ //(2)
--renew-hook <renew 後要執行 service haproxy reload> // (3)
-
--force-renewal
要求強迫更新證書, 提前強迫更新證書可以避免有證書斷層. -
--tls-sni-01-port=<port>
指示certbot
在HAProxy
導過來的 port 工作. -
--renew-hook
指示當renew
成功後要執行的script
. 也就是合併檔案, 重新啟動HAProxy
.
同樣的將產生的兩個檔案合併, 然後重新啟動 HAProxy
.
最後將上述指令設定給 crontab
來定時執行, 即可.
例如, 在每個月的1號凌晨1點執行 renew.sh
.
*m h dom mon dow command
0 1 1 * * renew.sh
crontab renew
成功後要執行的 script
.
domain="api.accunix.net" fullchain="/etc/letsencrypt/live/${domain}/fullchain.pem" privkey="/etc/letsencrypt/live/${domain}/privkey.pem" target="/etc/ssl/${domain}/${domain}.pem" if sudo [ -f ${fullchain} ] && sudo [ -f ${privkey} ]; then dir="/etc/ssl/${domain}" if sudo [ ! -d ${dir} ]; then sudo mkdir -p ${dir} fi sudo cat ${fullchain} ${privkey} | sudo tee ${target} sudo service haproxy reload fi
crontab
要執行的 script
.
#!/bin/bash cwd=$(cd `dirname $0`; pwd -P) port="16523" hook="${cwd}/hook.sh" sudo /usr/bin/certbot renew --force-renewal --tls-sni-01-port=${port} --renew-hook ${hook}