2017-01-07

在 docker nginx 下使用 docker let's encrypt

2018-03-02 更新 建議用輕量級的 acme.sh 取代 certbot

注意

certbot 工具說明

certbot 是 let’s encrypt 幫忙獲取 ssl 證書的命令行工具。

這工具主要有兩種模式

此模式下,certbot 會把自己當成 web server。自動處理完所有證書相關的事情。然後把檔案丟到 /etc/letsencrypt 下面。運作原理是 certbot 發起請求 -> let’s encrypt 網站對你的 domain 發起請求驗證你擁有此網站 -> 取得證書。

缺點明顯。因為要佔用端口,所以 nginx 必須停下,讓 certbot 弄完證書之後才能啟動。

此模式下,certbot 是利用原本的 web server 做驗證。原理是 certbot 在 nginx 訪問的下寫入特殊的路徑檔案 -> let’s encrypt 訪問該路徑檔來驗證 -> 取得證書。

此方式雖然需要對 web server 的設定做些改動,但是不用每次刷新證書都要停機。

docker nginx 配置

docker run \
--restart=always -d \
--name nginx \
-p 80:80 \
-p 443:443 \
-v /data/log/nginx:/var/log/nginx \
-v /data/app/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /data/app/nginx/conf.d:/etc/nginx/conf.d \
-v /data/app/nginx/cert:/etc/nginx/cert \
-v /data/app/nginx/html:/usr/share/nginx/html \
nginx:1-alpine
docker exec nginx -v
# nginx version: nginx/1.11.6

nginx 配置

nginx.conf

...
http {
    ...
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    server {
        listen      80 default_server;
        listen [::]:80;
        server_name example.com www.example.com;

        # 給 let's encrypt 的 webroot 模式驗證的
        location ^~ /.well-known/acme-challenge/ {
            default_type "text/plain";
            root     /usr/share/nginx/html;
        }
        location = /.well-known/acme-challenge/ {
           return 404;
        }

        # 消滅 http ,全部都是 https
        return 301 https://$server_name$request_uri;
    }
    # 沒拿到證書前要先註解 https server 部份
    server {
        listen      443 ssl http2;
        listen [::]:443 ssl http2;
        ssl_certificate         /etc/nginx/cert/live/example.com/fullchain.pem;
        ssl_certificate_key     /etc/nginx/cert/live/example.com/privkey.pem;
        ssl_trusted_certificate /etc/nginx/cert/live/example.com/chain.pem;
        server_name example.com;
        location / {
            proxy_pass http://127.0.0.1:3000;
        }
    }
    server {
        listen      443 ssl http2;
        listen [::]:443 ssl http2;
        ssl_certificate     /etc/nginx/cert/live/www.example.com/fullchain.pem;
        ssl_certificate_key /etc/nginx/cert/live/www.example.com/privkey.pem;
        server_name www.example.com;
        location / {
            proxy_pass http://127.0.0.1:3000;
        }
    }
}

docker certbot 使用

FROM alpine:3.4
RUN apk add --update bash certbot
VOLUME ["/etc/letsencrypt"]
docker build -t my:certbot .
#!/bin/bash
if test -z "$1";then
LIST=(
'example.com'
'www.example.com'
)
else
LIST=(
$1
)
fi

FAILED_LIST=()
WWW_ROOT=/usr/share/nginx/html

for domain in ${LIST[@]};do
docker run \
--rm \
-v /data/app/nginx/cert:/etc/letsencrypt \
-v /data/log/letsencrypt:/var/log/letsencrypt \
-v /data/app/nginx/html:${WWW_ROOT} \
my:certbot \
certbot certonly --verbose --noninteractive --quiet --agree-tos \
--webroot -w ${WWW_ROOT} \
--email="[email protected]" \
-d "$domain"
CODE=$?
if [ $CODE -ne 0 ];then
    FAILED_LIST+=($domain)
fi
done

# output failed domains
if [ ${#FAILED_LIST[@]} -ne 0 ];then
    echo 'failed domain:'
    for (( i=0; i<${#FAILED_LIST[@]}; i++ ));
    do
        echo ${FAILED_LIST[$i]}
    done
fi

自動更新

0 3 * * 1 /data/renew_cert.sh >> /data/log/renew_cert.log 2>> /data/log/renew_cert.error.log
0 0 * * 2 docker exec nginx -s reload

檢查 https 運作是否正常

其他問題

我使用 let’s encrypt 證書有的站可以開有的不行。所以確定跟證書沒關係。

微信的白名單機制,我也不是很懂。

這跟瀏覽器沒有支援 SNI (Server Name Indication) 有關,多 domain 的情況下,只能選擇使用共用(泛解析)證書。不然只能把不同 domain 放到不同 ip 下。

嗯,請升級 java 到 8 版本,或換 lib。請看這篇