fbpx

用 ssl-proxy Docker 集中管理 HTTPS 服務

haproxy ssl

透過 HAProxy 集中管理 Let’s Encrypt 憑證

Let’s Encrypt 提供的免費 SSL 憑證實在很省錢,麻煩的就是申請與 Renew 的過程比較痛苦,透過官方提供的 certbot 已經簡化了很多流程了。我用了幾年以後,大致上沒有什問題,只是有一個地方很討厭,就是提供服務的 HTTP Server 常常有好幾種實做,像是 Apache, Nginx, NodeJS, Java Web Container, RoR, HAProxy 等等,每次遇到一種新的 HTTP Server 就要搞一次,還要搞自動化更新 (因為我很懶,總是希望能寫個 Script 一勞永逸) 實在累累。

後來我想了一個方法,就是所有 HTTP 服務都由 HAProxy 進行代理轉發,這樣就可以集中實做 Let’s Encrypt 管理腳本。而且還可以集中所有服務的 HTTP Log 到 Log Server 中,統計與排查問題變得很方便。如何設計 Log Server 又是另一個主題了,有空再與各位分享實做細節。

HAProxy 已經算是非常穩定與高效的 Load Balance Solution,之前的文章「富人用 L4 Switch,窮人用 Linux HAProxy!」已經有介紹過這個強悍的軟體。由於我之前用了 Docker 以後就愛上了 Docker,後來所有的系統都盡可能以 Micro Service (微服務) 的概念實做,方便佈署、測試、開發與管理,遵循 DevOps 的方向前進。為了解決前面提到的問題,利用 Docker 封裝了一個可以自己更新與管理憑證的 Image 叫做 ssl-haproxy,Source Code 在 GitHubDocker Hub,安裝與使用方式如下:

準備憑證設定檔

建立設定檔專用的資料夾,等等要掛進 Docker 讓程式載入:

mkdir hle
vim ./hle/email@example.com

檔名設定為 Let’s Encrypt Account 使用的 Email,當憑證即將過期沒有被刷新時,這個 Email 就會收到提醒,所以不要亂設喔。設定檔的內容定義了想要自動管理的 Domain Name,還有這個 Service 對應 Proxy 的服務位置。設定擋內容如下:

[
    {
        "domain": ["example.com", "www.example.com"],
        "server": ["127.0.0.1:8080"]
    },
    {
        "domain": ["example2.com"],
        "server": ["192.168.0.1:80", "192.168.0.2:80"]
    }
]

上面的設定檔表示 HAProxy 啟動一台虛擬主機 Domain 同時掛載為 "example.com" 與 "www.example.com",收到連線時將封包派送到 127.0.0.1:8080 這個位置,這是 Docker 預設的 Nginx 服務,在啟動 Docker 的時候可以把靜態網頁資料掛進去。

另外設定檔還描述了另一台虛擬主機 Domain 為 "example2.com",並且自動輪替派送封包到 "192.168.0.1:80", "192.168.0.2:80" 這兩台機器,可以達到 Load Balance 與 High Availability 的效果。

安裝 docker-ce (已經安裝可以跳過)

sudo apt-get remove docker docker-engine docker.io
sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
sudo apt-get update
sudo apt-get install docker-ce

執行 ssl-proxy Container

準備好設定檔之後透過以下命令啟動 Container:

docker run -it \
   --publish 443:443 --publish 80:80 \
   --name ssl-proxy \
   --env HLE_STAGING=true \
   --env HLE_INTERVAL=10 \
   --volume hle:/etc/hle \
   --volume ssl:/etc/ssl/le-storage \
   --volume your-document-root:/var/www/html \
   samejack/ssl-proxy:latest

由於 Let’s Encrypt 有限制每個帳號每天發放憑證的數量,測試的時候設定 HLE_STAGING=true 先在 Staging 環境測試比較保險,另外 HLE_INTERVAL=10 表示每 10 秒就檢查設定檔有沒有變更,會自動重新申請憑證與 Renew,因此需要加入新的虛擬主機是不需要重啟 Docker 的,只要修改設定檔,時間到了就會自動跑。

順便介紹一下 volume 的作用,/etc/hle 就是我們剛剛的設定檔位置,/etc/ssl/le-storage 用來存放憑證檔案,/var/www/html 則是預設的 Nginx Document Root,如果只是靜態網頁可以完全用這一個 Docker Image 就可以實做 SSL HTTP 服務。

如果 HLE_INTERVAL 時間還沒到想要強制重新讀取設定檔,也可以執行以下命令立即重新載入設定:

docker exec -it ssl-proxy php /usr/share/hle/hle-renew.php

透過這樣的機制處理 Server,要注意的就是實際的 Client IP 會被 HAProxy 隱藏,Proxy 會將前端收到資訊放在 X-Forwarded-* 想對應的 Header 中,後端的 Backend Server 可以透過這些 Header 取得正確的資訊。目前支援的 Header 如下:

  • X-Forwarded-For:Remote IP 位置
  • X-Forwarded-Port:連線到 Proxy 服務的 Port
  • X-Forwarded-Host:連線到 Proxy 服務的 Host
  • X-Forwarded-Proto:使用的協定

所有 Source Code 可以在 GitHub 取得,有興趣可以看看,喜歡的話可以 Star 關注。