故事情境:我有一個主要網站 Main Web 是用 Nginx 架設,目前有以下需求需要完成:
新增 docker-compose.yml
version: "2" services: nginx-proxy: restart: always image: nginx container_name: nginx-proxy ports: - "80:80" - "443:443" volumes: - "/docker_vol/nginx-proxy/etc-nginx/conf.d:/etc/nginx/conf.d" - "/docker_vol/cert-letsencrypt:/etc/letsencrypt" - "/docker_vol/data-letsencrypt:/data/letsencrypt"
建立需要的目錄
mkdir -p /docker_vol/nginx-proxy/etc-nginx/conf.d mkdir -p /docker_vol/cert-letsencrypt mkdir -p /docker_vol/data-letsencrypt
新增 Nginx 設定檔
/docker_vol/nginx-proxy/etc-nginx/conf.d/proxy.conf
server { listen 80; server_name www.your.domain; location / { proxy_pass http://your-main-web-ip; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
http://your-main-web-ip 這裡填入主網站的 IP 位址
啟動nginx-proxy 服務 (使用 docker-compose)
第一次啟動,系統會自動新增與佈署需要的 containers
#> docker-compose up -d
佈署後檢查服務狀態
#> docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------- nginx-proxy nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
TIPs:
如果一直無法正常啟動服務,試著先將 proxy.conf 移除,然後啟動試試;如果啟動正常,再將 proxy.conf 回復後重新啟動。
Reverse Proxy 基本測試
如果 nginx-proxy 服務啟動正常,若在瀏覽器上輸入 http://reverse-proxy-ip 有顯示主網站的首頁,表示服務運作正常。
使用 Reverse Proxy 同部主機,運用 docker 技術,將 Let's Encrypt 憑證服務與 Reverse Proxy 做整合。
事前準備:
需要的步驟有二:
編輯 /docker_vol/nginx-proxy/etc-nginx/conf.d/proxy.conf
加上以下這幾行
server { listen 80; server_name www.your.domain; # Statically serve all files in .well-known, which is the location where letsencrypt stores the proof file location /.well-known/ { alias /data/letsencrypt/.well-known/; } }
TIP:
/.well-known 這個目錄必須在公眾網路下可以透過 HTTP 讀取。
重啟 nginx-proxy 服務
docker-compose stop docker-compose start
線上建立憑證
docker run -it --rm \ -v "/docker_vol/cert-letsencrypt:/etc/letsencrypt" \ -v "/docker_vol/data-letsencrypt:/data/letsencrypt" \ -v "/docker_vol/log-letsencrypt:/var/log/letsencrypt" \ deliverous/certbot \ certonly \ --webroot --webroot-path=/data/letsencrypt \ -d www.your.domain
TIPs:
- www.your.domain 請改成主要網站的實際網域名稱。如果一次要建立多個網站的憑證,最後一行改成 -d first.my.web -d second.my.web -d third.my.web
- 執行後,系統會要求輸入 email,請輸入有效的 email。
- 建立憑證時,網站必須開放 HTTP 可讀取網站目錄 /.well-known/,若憑證建立失敗,請檢查此目錄是否可正常被讀取,網頁目錄為 /data/letsencrypt。
憑證建立成功
成功的畫面輸出內容如下
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/www.your.domain/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/www.your.domain/privkey.pem
Your cert will expire on 2018-05-06. 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"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
憑證檔存放路徑
container 系統
Host 系統
TIP:
之後若在同一部主機需要新增其他主機網域的憑證,只要再執行一次上述的 docker 指令,將 -d <new-host-name> 改成新主機名稱,過程中不需要再輸入 email(第一次輸入的 email 已儲存在 /docker_vol/cert-letsencrypt/)。
Lets Encrypt 憑證每次簽署只有三個月的有效期,自動更新步驟如下:
#> docker run -it --rm \ -v "/docker_vol/cert-letsencrypt:/etc/letsencrypt" \ -v "/docker_vol/data-letsencrypt:/data/letsencrypt" \ -v "/docker_vol/log-letsencrypt:/var/log/letsencrypt" \ deliverous/certbot \ renew \ --webroot --webroot-path=/data/letsencrypt #> docker-compose kill -s HUP nginx-proxy
NOTE: 不可以加上 -d your.domain.com,只能一次更新所有網域。
Cronjob:
0 0 */15 * * docker run -it --rm -v "/docker_vol/cert-letsencrypt:/etc/letsencrypt" -v "/docker_vol/data-letsencrypt:/data/letsencrypt" deliverous/certbot renew --webroot --webroot-path=/data/letsencrypt && docker-compose kill -s HUP nginx-proxy
TIP:
- 官方建議每隔 60 天對憑證做更新,每次憑證有效期為 90 天。
- 更新指令無法指定單一 domain,必須一次對所有網域做更新。
- 如果更新失敗,檢查 log 檔 /docker_vol/log-letsencrypt/letsencrypt.log
憑證更新成功的輸出
-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/r13.osslab.tw.conf
-------------------------------------------------------------------------------
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator webroot, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for r13.osslab.tw
Using the webroot path /data/letsencrypt for all unmatched domains.
Waiting for verification...
Cleaning up challenges-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/r13.osslab.tw/fullchain.pem
-------------------------------------------------------------------------------The following certs were successfully renewed:
/etc/letsencrypt/live/r13.osslab.tw/fullchain.pem (success)
一旦上述步驟都有正確完成,就可以將 Reverse Proxy 設定成 SSL 加密模式。
編輯 /docker_vol/nginx-proxy/etc-nginx/conf.d/proxy.conf
加上以下這幾行
listen 443; #這行記得改 ... # SSL Settings ssl on; ssl_certificate /etc/letsencrypt/live/www.your.domain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.your.domain/privkey.pem; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # omit SSLv3 because of POODLE (CVE-2014-3566) ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_prefer_server_ciphers on;
重啟 nginx-proxy 服務
docker-compose stop docker-compose start
Q: 更新憑證時發生錯誤
Currently, the renew verb is capable of either renewing all installed certificates that are due to be renewed or renewing a single certificate specified by its name. If you would like to renew specific certificates by their domains, use the certonly command instead. The renew verb may provide other options for selecting certificates to renew in the future.
A: 更新時不能指定網域,必須更新所有設定的網域
#> docker run -it --rm \ -v "/docker_vol/cert-letsencrypt:/etc/letsencrypt" \ -v "/docker_vol/data-letsencrypt:/data/letsencrypt" \ -v "/docker_vol/log-letsencrypt:/var/log/letsencrypt" \ deliverous/certbot \ renew \ --webroot --webroot-path=/data/letsencrypt
NOTE:不可以加上 -d your.domain.com