Cyber Coding Course

師其意,不泥其跡

攻略docker版Let's Encrypt憑證申請

前言

本文主要分享,我如何採用Docker的方式進行Let's Encrypt憑證申請,
Let's Encrypt有相當多種類的ACME Client,
我將使用官方推廌Certbot(ACME Client)做說明。
並且使用docker的方式來執行ACME Client。
這樣做對筆者來說,有兩個好處:

一、我不需要在主機端(host),使用最高權限(root),跑certbot程式,也不需安裝相關執行Client的Python環境。

二、貼上指令,自動下載Client並執行,然後取得憑證,當您理解docker指令及certbot參數後,這將會是非常簡易的一件事情。

Dockerfile

再開始前,我們先來看看Certbot所提供的Dockerfile設定。
理解Dockerfile的設定,有助於我們了解等下要執行指令的目地,所以我們來稍微看看吧。
Dockerhub的位置,請參看這裡:https://hub.docker.com/r/certbot/certbot
FROM python:2-alpine3.7

ENTRYPOINT [ "certbot" ]
EXPOSE 80 443
VOLUME /etc/letsencrypt /var/lib/letsencrypt
WORKDIR /opt/certbot

COPY CHANGELOG.md README.rst setup.py src/
COPY acme src/acme
COPY certbot src/certbot

RUN apk add --no-cache --virtual .certbot-deps \
        libffi \
        libssl1.0 \
        openssl \
        ca-certificates \
        binutils
RUN apk add --no-cache --virtual .build-deps \
        gcc \
        linux-headers \
        openssl-dev \
        musl-dev \
        libffi-dev \
    && pip install --no-cache-dir \
        --editable /opt/certbot/src/acme \
        --editable /opt/certbot/src \
    && apk del .build-deps
可以看見,上方的ENTRYPOINT就是certbot指令,我們可以由host代入參數端執行container內指令。

如何用certbot取得憑證

我將他分為四種運用情境,並不是四個步驟哦。
情境二,只需一行指令,就可以產生憑證的。
請留意docker命令中,certbot/certbot之後所代入的參數,
不同的情境,使用了不同的參數哦。

情境一、manual手動設定
代表當網頁伺服器,是遠端的主機,我們想在自己的電腦產生憑證給該主機用時。
因為要掛載目錄到container中,讓產生的憑證可以寫入我們的主機。
請先建立資料夾。
mkdir -p /etc/letsencrypt
再用下方的指令產生憑證(請自己換成您自己的郵件哦)
docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -ti certbot/certbot certonly --manual --email devin@ccc.tc --agree-tos
過程中,會詢問我們,要使用那個域名,並且會要求我們將challenage的資訊,放到遠端網頁伺服器上。
這裡提供我的做法,直接ssh到該主機,拷貝certbot要求的相關資訊,用echo標準輸出,透過大於,重導成檔案即可。
 
ssh root@我的主機
cd /var/www/html/.well-known/acme-challenge/
echo LW_70m1q1QWIAxktnR8rU3QK4znLP9iyvp1Uf3mBsU4._ZtWqdsZpgLv_TS7hHMCm0zcL8HXhJrGePNrNSSi23Y> LW_70m1q1QWIAxktnR8rU3QK4znLP9iyvp1Uf3mBsU4
完成後,再壓Enter,如果他能經由網址連到該檔驗證通過,就會將憑證發出,放至於/etc/letsencrypt下的相關目錄中。


情境二、certonly只產生憑證
代表了,透過運行中的網頁伺服器來產生憑證,簡單說我們要執行指令的地方,
已經有一台活生生的網頁伺服器運作中了。

執行指令前,請先確認主機端有這兩個目錄。
mkdir -p /etc/letsencrypt
mkdir -p /var/www/html/.well-known/acme-challenge
(這裡假定您運作中的主機的網站根目錄是在/var/www/html下,另外,請自行換成產生憑證的域名,用www.ccc.tc是過不了的
如果您的目錄不是/var/www/html請自行調整掛載的位置。
docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -v /var/www/html:/html -ti certbot/certbot certonly --email devin@ccc.tc --agree-tos --webroot -w /html -d www.ccc.tc
上方指令,使用了-v的參數分別掛載了host端的兩個資料夾。
/etc/letsencrypt  (憑證與let's encrypt設定)
/var/www/html (網站的根目錄)
另外多出了--webroot -w /html 這是什麼意思呢?
這裡是我定義了網站的根目錄是/html (容器內)。不過我們透過了docker的-v /var/www/html:/html參數,
將host端的真正的跟目錄/var/www/html掛載進了容器的/html中了。

所以certbot可將challenge的檔寫到我們的網站目錄下。
/var/www/html/.well-known/acme-challenge
也就是說他會來連結我們主機上的由certbot隨機生成檔案及內容。
http://www.ccc.tc/.well-know/acme-challenge/0wCvy2q6URD-AwFTJQGUkz5sug6d3ptGp6QQGzqe1Eg

通過後,certbot便會將憑證及設定存入host端的/etc/letsencrypt中(-v /etc/letsencrypt:/etc/letsencrypt)。
因此,同樣的docker掛載方式,將certbot的entrypoint後,變更成renew,在憑證快到其時,就能自動更新囉。
0 0 * * * docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -v /var/www/html:/html -ti certbot/certbot renew
試執行renew,可以看見,顯示憑證未過期:
root@ccc:~# docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -v /var/www/html:/html -ti certbot/certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/www.cc.tc.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/www.ccc.tc/fullchain.pem expires on 2019-04-12 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

情境三、standalone獨立伺服器
當certbot多了--standalone參數時,代表了,他自己會建立一台challenges的http伺服器,因此,您應該確認您的防火牆是有開啟port 80的,
下方的例子中-d後面所代入的域名的DNS正解,需能解析出您電腦所使用的外網IP。
docker run --rm  -v /etc/letsencrypt:/etc/letsencrypt  -p 80:80 -ti certbot/certbot certonly --standalone --email devin@ccc.tc --agree-tos --preferred-challenges http -d test1.ccc.tc
這個例字中,由certbot幫我們建立一台webserver接收challenge並產生憑證。
很明顯的,可以看到,這裡透過-p將在主機端開啟port 80並mapping到容器中的port 80。
另外當然要掛載容器內的/etc/letsencrypt,用來保留產生的憑證,
由於docker參數中,我加了--rm,當執容器執行完必後,是會自動被移除的,
所以我們要透過-v掛載/etc/letsencrypt來保留設定及獲取憑證。

所以,不需要有網頁伺服器,docker run時,certbot就會獨立生成一台網頁伺服器,作業結束後該主機就被停止。
然後,我們的/etc/letsencrypt上,就有該憑證了。

關於-v左方的/etc/letsencrypt目錄是可以自訂的。
如果我是在自己的家目錄中建了letsencrypt資料夾,就可以設定如下的方式掛載 -v ${HOME}/letsencrypt:/etc/letsencrypt。
${HOME}指的就是我們的家目錄,您可以用MacOS或Linux系統上echo ${HOME}看看。
希望大家能思考及了解參數背後的意義,進而依自己的環境需求調整參數,而不是只調整域名及郵件,就貼上。

情境四、wildcard憑證申請
如果我們要生成任意的子域名憑證(*.ccc.tc),就可以用下方的指令。
docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -ti certbot/certbot certonly --agree-tos --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory -d "*.ccc.tc"
wildcard的憑證需採用dns進行challenge,所以您必需要能調整DNS Server,加入他要求的txt記錄進行驗證。
 

最後

如果您能成功的用docker的方式產生憑證。其實參數都是一樣的,
在無docker的環境,你也可以直接安裝certbot檔,直接使用certbot指令來產生憑證。
把下方整段的docker指令
docker run --rm -v /etc/letsencrypt:/etc/letsencrypt -v /var/www/html:/html -ti certbot/certbot
想成執行certbot指令即可
/usr/bin/certbot


網頁伺服器的憑證設定,不在本篇的討論範圍,但是這裡給大家一點小提示,在第二種的certonly的設定中,
因為產生憑證主機跟網頁主機是同一台上,我們可以直接將憑證設定寫到網頁伺服器的設定中,以Apache為例:
SSLCertificateFile    /etc/letsencrypt/live/www.ccc.tc/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.ccc.tc/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/www.ccc.tc/chain.pem
加上排程每天檢測certbot renew及如下指令來重啟主機。
service httpd graceful
如此一來,就可以神不知鬼不覺的自動更新憑證啦。
如果您的主機是nginx,可以用
nginx -s reload
如果是在container(容器)內的主機,我相信這招應該通殺,您或許可以試試 :)
https://www.ccc.tc/notes/50

看完本文,您是否發覺憑證申請,簡單到爆炸呢,自動化也不難,不是嗎?



 

作者: Devin Yang