SSL Reverse Proxy with Lets Encrypt

    版本為 01:50, 20 Sep 2024

    到這個版本。

    返回到 版本存檔.

    查閱目前版本

    說明

    故事情境:我有一個主要網站 Main Web 是用 Nginx 架設,目前有以下需求需要完成:

    1. 租用另一部主機並建置 Reverse Proxy 服務,目的是避免主要網站主機直接曝露在公用網路上。
      注意:基於安全理由,主網站與 Reverse Proxy 應該是在不同的主機並且是不同的 IP 位址。
    2. 網站需要使用 Let's Encrypt 憑證的 SSL 加密,所以架構是
      Main Web -----[HTTP]----- Reverse Proxy -----[HTTPS]----- Client
    3. 防火牆設定原則
      • Main Web 禁止所有外部存取,僅開放來自 Reverse Proxy 的 HTTP 連線
      • Reverse Proxy 開放外部所有連線可存取 HTTPS
    4. 使用 docker 環境建置 Reverse Proxy 系統,包括 Lets Encrypt 憑證自動更新

    Reverse Proxy with 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 有顯示主網站的首頁,表示服務運作正常。

    Lets Encrypt

    使用 Reverse Proxy 同部主機,運用 docker 技術,將 Let's Encrypt 憑證服務與 Reverse Proxy 做整合。

    事前準備:

    1. 需要一個有效的網域名稱例如 www.your.domain
    2. 上述網域在公眾 DNS 主機必須有 A/AAAAA 紀錄指向到 Reverse Proxy 主機 IP
       

    需要的步驟有二:

    1. 第一次新增憑證至主機
    2. 每3個月自動更新憑證有效期
    第一次新增憑證

    編輯 /docker_vol/nginx-proxy/etc-nginx/conf.d/proxy.conf
    加上以下這幾行

    ...
        # 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/;
        }
    

    重啟 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" \
     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/,若憑證建立失敗,請檢查此目錄是否可正常被讀取。

    憑證建立成功

    成功的畫面輸出內容如下

    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 系統

    • /etc/letsencrypt/live/www.your.domain/fullchain.pem
    • /etc/letsencrypt/live/www.your.domain/privkey.pem

    Host 系統

    • /docker_vol/cert-letsencrypt/live/www.your.domain/fullchain.pem
    • /docker_vol/cert-letsencrypt/live/www.your.domain/privkey.pem

    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" \
     deliverous/certbot \
     renew \
     --webroot --webroot-path=/data/letsencrypt
    
     #> docker-compose kill -s HUP nginx-proxy

    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
    

    設定 Reverse Proxy 使用 SSL with Letsencrypt

    一旦上述步驟都有正確完成,就可以將 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 
    
    Powered by MindTouch Core