使用 NGINX 提供多個網站的 SSL 加密服務 – 2020 版

使用 NGINX 為 Odoo 提供 SSL 加密服務 中的設定在 2020 年已經有點過時了,因此追加更新修正後的內容在此篇

由於安全性的問題,所有瀏覽器在 2020 年三月移除支援 TLS v1.0、TLS v1.1 協定

在 ssl labs 的測試中,有支援 TLS v1.0、TLS v1.1 協定時只能拿到 B

因此需將此部份

        ssl_protocols TLSv1.0 TLSv1.1 TLSv1.2;

修正為

        ssl_protocols TLSv1.2;

而在多個網站同時架設在一台主機上時,因此 ssl_session_cache 會出現快取無法重複設定的問題

這時候可以通過修改 nginx.conf 讓全部網站共用此 ssl_session_cache

vim /etc/nginx/nginx.conf

將這段

    include /etc/nginx/conf.d/*.conf;

修改為

    include /etc/nginx/conf.d/*.conf;

    ## SSL Cache Setting
    ssl_session_cache   shared:SSL:50m;
    ssl_session_timeout 10m;

以下是以 Magento 2 作為範例寫出來的設定檔(仍需要修改)

/etc/nginx/nginx.conf

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;
    server_tokens       off;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    ## Magento 2 fastcgi_backend
    upstream fastcgi_backend {
        server  unix:/run/php-fpm/php-fpm.sock;
    }

    ## SSL Cache Setting
    ssl_session_cache   shared:SSL:50m;
    ssl_session_timeout 10m;
}

/etc/nginx/conf.d/m2.cewolf.com.tw.conf

    server {
        listen 80;
        server_name m2.cewolf.com.tw;
        #set $MAGE_ROOT /usr/share/nginx/magento_hanyu;
        #include /usr/share/nginx/magento_hanyu/nginx.conf.sample;
        return 301 https://m2.cewolf.com.tw$request_uri;

        #location ^~ /.well-known/acme-challenge/ {
        #default_type    "text/plain";
        #root /etc/letsencrypt/;
        #}
    }

    server {
        listen [::]:443 ssl http2;
        listen 443 ssl http2;

        server_name m2.cewolf.com.tw;
        set $MAGE_ROOT /usr/share/nginx/magento_hanyu;
        include /usr/share/nginx/magento_hanyu/nginx.conf.sample;

        ssl_certificate /etc/letsencrypt/live/m2.cewolf.com.tw/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/m2.cewolf.com.tw/privkey.pem;
        ssl_protocols TLSv1.2;
        ssl_dhparam /etc/dehydrated/dhparam.pem;
        ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
        ssl_prefer_server_ciphers on;

        # Enable OSCP Stapling for Nginx web server
        # If you're using the SSL from Letsencrypt,
        # use the 'chain.pem' certificate
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8 8.8.4.4;
        ssl_trusted_certificate /etc/letsencrypt/live/m2.cewolf.com.tw/chain.pem;

        # Enable HTTP Strict-Transport-Security
        # If you have a subdomain of your site,
        # be carefull to use the 'includeSubdomains' options
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    }

 

Let’s encrypt 遇到 ‘ascii’ codec can’t decode byte 0xe5 的解法

Let’s encrpyt 更新憑證的時候,跳出以下錯誤訊息:

Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: ‘ascii’ codec can’t decode byte 0xe5 in position 2: ordinal not in range(128). Skipping.

解法

  1. $ sudo grep -r -P ‘[^\x00-\x7f]’ /etc/apache2 /etc/letsencrypt /etc/nginx
  2. 將看到的內容,進去檔案刪除
  3. 刪除這些編碼後,重新執行 certbot 就可以通過囉~

使用 certbot 申請 Let’s Encrypt 免費 SSL 憑證

由於目前 Dehydrated 無法正常使用,官方的 certbot 改的簡單許多,建議使用 certbot 來申請

目前我使用過兩種申請方式

  1. webroot 驗證:優點是不需要中斷現有的網路服務,但需要修改 NGINX 設定檔
  2. DNS 驗證:只需要修改 DNS 設定即可通過認證,但後續會需要手動續約,不推薦

安裝 certbot-nginx 主程式

yum install -y python-certbot-nginx

Method 1:webroot 驗證申請

需要先修改網址的 NGINX 設定,最前面改為下方樣式

    # http -> https
    server {
        listen 80;
        server_name  www.cewolf.com.tw;

        location ~ /\.well-known\/acme-challenge {
                root /etc/letsencrypt;
        allow all;
        }
        if ($request_uri !~ /\.well-known) {
                return 301 https://$server_name$request_uri;
        }
    }

重新載入 NGINX 設定檔

 service nginx reload

申請 SSL 憑證

certbot certonly --webroot -w /etc/letsencrypt/ -d www.cewolf.com.tw

跳出以下的內容就代表成功了

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for m2.cewolf.com.tw
Using the webroot path /etc/letsencrypt for all unmatched domains.
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/www.cewolf.com.tw/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/www.cewolf.com.tw/privkey.pem
Your cert will expire on 2020-03-20. 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

接著修改 NGINX 設定檔的 SSL 設定

vim /etc/nginx/conf.d/www.cewolf.com.tw.ssl.conf

加入這兩行

ssl_certificate /etc/letsencrypt/live/www.cewolf.com.tw/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.cewolf.com.tw/privkey.pem;

重新載入 NGINX 設定即可

systemctl reload nginx

Method 2:DNS 驗證申請

certbot -d m2.cewolf.com.tw --manual --preferred-challenges dns certonly

會跳出 DNS 驗證值

Please deploy a DNS TXT record under the name
_acme-challenge.www.cewolf.com.tw with the following value:

O3kP443ms6OP84K8NQnZv_vvZ5HAHMKMBdqqSIyxKlo

直接在 DNS Record 設定 txt 之後需確認可成功查詢

nslookup -type=TXT _acme-challenge.www.cewolf.com.tw

接著按下 Enter 就看到,就代表成功了

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/www.cewolf.com.tw/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/www.cewolf.com.tw/privkey.pem
Your cert will expire on 2020-03-20. 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

接著修改 NGINX 設定檔的 SSL 設定

vim /etc/nginx/conf.d/www.cewolf.com.tw.ssl.conf

加入這兩行

ssl_certificate /etc/letsencrypt/live/www.cewolf.com.tw/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.cewolf.com.tw/privkey.pem;

重新載入 NGINX 設定

systemctl reload nginx

記得確認 SSL 憑證失效日是否有成功延後喔!

自動展延憑證

我們可以利用 cron 這個小程式來定期自動展期,首先編輯 crontab 設定檔

vim /etc/crontab

加入這兩行(於每周六 AM 3:00 檢查第一組網域是否需要展期,AM 3:30 reload NGINX)

00 3 * * 6 root certbot -d www.cewolf.com.tw --no-redirect
10 3 * * 6 root certbot -d km.cewolf.com.tw --no-redirect
20 3 * * 6 root certbot -d www.gapl.com.tw --no-redirect
30 3 * * 6 root systemctl reload nginx.service

重新啟動 cron service

systemctl restart crond.service

完成!

確認憑證狀態

有時候會收到 letsencrypt 寄來的「憑證即將到期通知 Let’s Encrypt certificate expiration notice for domain “your_domain.com”」。這時候可以輸入下面的指令確認狀態。

sudo certbot certificates

使用 NGINX 為 Odoo 提供 SSL 加密服務

於 Google Compute Engine CentOS 7.0 設定 Dehydrated 與轉移 SSL 憑證

於 Google Compute Engine CentOS 7.0 建置 Magento 使用環境已經完成基礎環境設置,接著需要完成 SSL 認證檔的轉移與 Dehydrated 的設定

轉移 Dehydrated 與 SSL 憑證

建立需要的目錄

mkdir -p /etc/dehydrated/ && mkdir -p /var/www/dehydrated/ && mkdir -p /usr/share/nginx/html/.well-known/acme-challenge/

打包舊主機上的檔案

/bin/cp /etc/nginx/cert/dhparam.pem /etc/dehydrated/
cd /etc/
tar zcvf dehydrated.tar.gz dehydrated
mv dehydrated.tar.gz /usr/share/nginx/html/ && chown -R nginx: /usr/share/nginx/html/dehydrated.tar.gz

下載至新主機上並放置至相關位置

curl -O https://www.gapl.com.tw/dehydrated.tar.gz
tar zxvf dehydrated.tar.gz && mv dehydrated /etc/

轉移 Magento 檔案

打包舊主機上的 Magento 並轉移

cd /usr/share/nginx/ && tar zcf magento_$(date +%Y-%m-%d).tar.gz html --exclude html/var/cache --exclude html/var/session --exclude html/var/log
mv magento_$(date +%Y-%m-%d).tar.gz /usr/share/nginx/html && chown nginx: /usr/share/nginx/html/magento_$(date +%Y-%m-%d).tar.gz

下載至新主機上並放置至相關位置

curl -O https://www.gapl.com.tw/magento_$(date +%Y-%m-%d).tar.gz
tar zxvf magento_$(date +%Y-%m-%d).tar.gz && mv html /usr/share/nginx/
chown -R nginx: /usr/share/nginx/html

自動展期與 Cron 設定

編輯 crontab 設定檔

vim /etc/crontab

加入這三行(於每周六 AM 3:00 檢查是否需要展期,AM 3:05 reload NGINX)

*/5 * * * * nginx /usr/share/nginx/html/cron.sh
00 3 * * 6 root /etc/dehydrated/dehydrated -c -d www.gapl.com.tw
05 3 * * 6 root systemctl reload nginx.service

重新啟動 cron service

systemctl restart crond.service

移除舊主機上的 cron 設定並重新啟動 cron service

 

使用 Dehydrated 申請 Let’s Encrypt 免費 SSL 憑證

用 Let’s Encrypt 為 Magento 安裝免費的 SSL 憑證 / CENTOS 7

==================== 更新至新版後,無法正常 renew,請改用 Dehydrated 來處理 ====================

 

Let’s Encrypt 是各大廠為了提升網路安全性而共同合作推出免費提供憑證的機構(CA), 以下是在 CentOS 7 將 Let’s encrypt 配置到 NGINX 的方法

先安裝 git、 EPEL repo 與 Let’s encrypt 所需套件

su
yum install -y git epel-release gcc libffi-devel python-devel openssl-devel

下載 Let’s encrypt:

cd /root
git clone https://github.com/letsencrypt/letsencrypt

系統會將 Let’s Encrypt 的最新版本下載到 /root/letsencrypt

先停用 NGINX 後用 letsencrypt-auto 取得 SSL 憑證檔

cd /root/letsencrypt
./letsencrypt-auto certonly -a standalone -d yourdomain.com

or 

cd /root/letsencrypt
./letsencrypt-auto --config /home/test/configs/[your-domain].conf certonly

mkdir /root/webroot
cd /root/letsencrypt
./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d odoo.glamping.tw
./letsencrypt-auto certonly -a webroot --webroot-path=/root/webroot -d odoo.glamping.tw

Let’s encrypt 會將憑證檔案放到 /etc/letsencrypt/live/.

接下來生成 DH Parameter

mkdir /etc/nginx/cert
openssl dhparam 2048 -out /etc/nginx/cert/dhparam.pem
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

接著設定 NGINX 的 SSL 設定,加入以下參數

    ssl_certificate      /etc/letsencrypt/live/odoo.glamping.tw/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/odoo.glamping.tw/privkey.pem;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 180m;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4;
    add_header Strict-Transport-Security "max-age=31536000 always;

接下來重新啟動 NGINX

service nginx reload

去測試網站跑分,這樣子的設定應該可以直接拿到最高評分 A+ !

最後,由於 Lets Encrypt 憑證有效期限只有 90 天,建議每 60 天自動續約

先建立 renew script:auto-renew.sh

/root/letsencrypt/letsencrypt-auto certonly --webroot --renew-by-default --agree-tos -m cewolf@cewolf.com -w /root/webroot -d gapl.com.tw -d www.gapl.com.tw -d glamp.tw
nginx -s reload

接著設定 CRON 於每兩個月的 20號 凌晨 3 點續約

0 3 20 2,4,6,8,10,12 * root /root/auto-renew.sh

Reload crond

systemctl reload crond.service

大功告成!

目前 nginx reload 並不會換上新的憑證

再找到解決方案前先這樣子跑

systemctl stop crond.service
/root/letsencrypt/letsencrypt-auto renew
/bin/systemctl start nginx.service

 

測試網站:

Qualys SSL Labs SSL Server Test

DigiCert® SSL Installation Diagnostics Tool

SSL Checker

參考資料:

LinuxRHEL / CentOS 7 安裝 Let’s encrypt RHEL / CentOS 7 安裝 Let’s encrypt

Let’s Encrypt 的 SSL 憑證安裝

Configure Magento with SSL

Optimizing HTTPS on Nginx

Guide to Deploying Diffie-Hellman for TLS

用 nginx 建置一個 A+ 等級的 https 網頁伺服器

SSL延遲有多大?

How to Validate a Let’s Encrypt Certificate on a Site Already Active on CloudFlare