使用 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 憑證

CentOS 更新 OpenSSL 到最新版 1.1.0

CentOS YUM 套件庫中的 OpenSSL 版本只到 1.0.1e

但 NGINX 有些功能需要升級到 1.0.2+ 才能支援

依照下方流程即可順利更新

下載最新版 openSSL

$ wget https://www.openssl.org/source/openssl-1.1.0-latest.tar.gz
$ tar zxvf openssl-1.1.0-latest.tar.gz -C /usr/src/
$ cd /usr/src/openssl-1.1.0*

編譯及安裝

$ ./config -Wl,--enable-new-dtags,-rpath,'$(LIBRPATH)' => 如果沒下後方的參數,Let's Encrypt 會報錯
$ make
$ make install

如果之前有手動更新過 openssl 需手動移除此檔案

$ rm -f /usr/bin/openssl && rm -f /usr/lib64/libssl.so.1.1 && rm -f /usr/lib64/libcrypto.so.1.1

重新製作 symbolic link

$ ln -s /usr/local/bin/openssl /usr/bin/openssl
$ ln -s /usr/local/lib64/libssl.so.1.1 /usr/lib64/libssl.so.1.1
$ ln -s /usr/local/lib64/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1

確認版本

$ openssl version
OpenSSL 1.1.0f 25 May 2017
Let’s Encrypt 需增加
cd ~/.local/share/letsencrypt/bin/
./pip uninstall cryptography pyopenssl -y
./pip install --upgrade pip
rm -rf ~/.cache/
./pip install cryptography pyopenssl

參考資料:

https://stackoverflow.com/questions/42111198/undefined-symbol-openssl-sk-num/43622117

CentOS 更新 OpenSSL 到最新版 1.0.2j

CentOS YUM 套件庫中的 OpenSSL 版本只到 1.0.1e

但 NGINX 有些功能需要升級到 1.0.2 才能支援

依照下方流程即可順利更新

$ wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz
$ tar zxvf openssl-1.0.2-latest.tar.gz -C /usr/local/
$ cd /usr/local/openssl-1.0.2*
$ ./config
$ make depend
$ make
$ make test
$ make install
$ mv /usr/bin/openssl /usr/bin/openssl_1.0.1e
$ ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
$ openssl version
OpenSSL 1.0.2h  3 May 2016

用 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