在 Synology NAS 上用 Shell Script Renew Let's Encrypt

2016.05.09 Update: Synology DSM 6.0 發佈之後,就已內建整合 Let’s Encrypt 憑證的申請、更新及管理,在多個 domains, virtual hosts 也可以成功運作,更加方便。

因為 Let’s Encrypt 官方的 python script 沒辦法直接在 Synology NAS 上執行,所以之前得透過另一台 Linux 機器來取得憑證,很快的,三個月的憑證期限就要到了,而這三個月內,又出現了另一個非官方但更方便的工具 – letsencrypt.sh,它是 BASH script,只需要一些大部份機器上都有的 command line tools 就可以執行,拿到 Synology NAS 上測試,的確可以成功的取得憑證,詳述方法如下:

系統環境

  • NAS: Synology DS213+
  • DSM 5.2-5644 Update 3
  • BusyBox v1.16.1 (2015-10-28 13:22:39 CST) built-in shell (ash)

安裝 Dependency

$ sudo ipkg install libcurl
$ sudo ipkg install openssl
$ sudo ipkg install grep
$ sudo ipkg install mktemp

BusyBox 中有內建 sed, grep,但其 grep 沒辦法通過 script 的檢測,裝 ipkg 的 package 會比去改 code 容易一些。

如果還沒有安裝 ipkg 可以參考:Synology DS213+ 安裝 ipkg,可惜這些 package 都稍舊,連 openssl 都還停在 0.9.8 (目前最新的是 1.0.2f),不過執行起來是沒有遇到什麼錯誤。

取得 letsencrypt.sh 程式

$ cd /path_to_what_you_want
$ git clone https://github.com/lukas2511/letsencrypt.sh.git

如果沒有 git,可以從「套件中心」安裝官方提供的版本。

準備 ACME Challenge 的目錄

letsencrypt.sh 目錄下建立 .acme-challenges 目錄

$ cd letsencrypt.sh
$ mkdir .acme-challenges

加入 Apache 共用 Alias

修改 /etc/httpd/sites-enabled-user/httpd-vhost.conf-user,在想要一併進行 renew 的 vhost 中加入:

Alias /.well-known/acme-challenge /path_to_letsencrypt.sh/.acme-challenges
<Directory /path_to_letsencrypt.sh/.acme-challenges>
  Order allow,deny
  Allow from all
</Directory>

這個設定是因為要同時 renew 多個 domain 的憑證,必須要讓各個 domain 的 acme-challenges URL 可以 access 到 letsencrypt.sh 下共用的 challenge 目錄(預設是 letsencrypt.sh/.acme-challenges),所以採用的是 Alias 設定,可以不受各個 domain 不同的 DocumentRoot 的限制。或者,如果當初在 <Directory> 中有設 FollowSymLinks,也可以把 vhost 的 /.well-known/acme-challenge symlink 過去。

如果只有一個 domain 要申請,可以直接在該 domain 的 DocumentRoot 下建立 challenge 目錄,然後在 config.sh 中設定 WELLKNOWN 變數到該目錄。

設定 .htaccess 讓 ACME Challenge 可以進行

# For Let's Encrypt challenge
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteRule \.well-known/acme-challenge - [L]
</IfModule>

因為我的 WordPress 有設了一些 cache 用的 rewrite rules,所以要多加這條規則來避開,如果沒有這樣需求的就不必動了。

此時先第一次重新啟動 Apache 讓剛剛的 Alias 設定可以生效:

$ sudo /usr/syno/sbin/synoservicecfg --restart httpd-user

將 Domain List 加入 domains.txt

$ echo "tsai.it" > domains.txt

進行憑證註冊或 Renew 既有憑證

$ ./letsencrypt.sh -c

如果一切正常,會出現下列訊息

Processing tsai.it
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for tsai.it...
 + Responding to challenge for tsai.it...
 + Challenge is valid!
 + Requesting certificate...
 + Checking certificate...
 + Done!
 + Creating fullchain.pem...
 + Done!

如果原有憑證的 expire date 還大於 30 天,該 domain 便會被 skip 過去。

Processing tsai.it
 + Checking domain name(s) of existing cert... unchanged.
 + Checking expire date of existing cert...
 + Valid till Apr 25 13:08:00 2016 GMT (Longer than 30 days). Skipping!

在 Apache 指定各 Virtual Hosts 使用的 SSL 憑證

取得的 SSL 憑證會存放在 letsencrypt.sh/certs/${domain} 中,修改 Apache 設定 /etc/httpd/sites-enabled-user/httpd-ssl-vhost.conf-user,在每個 vhost 中加入:

SSLCertificateFile /path_to_letsencrypt.sh/certs/${domain}/cert.pem
SSLCertificateKeyFile /path_to_letsencrypt.sh/certs/${domain}/privkey.pem
SSLCertificateChainFile /path_to_letsencrypt.sh/certs/${domain}/chain.pem
SSLCACertificateFile /path_to_letsencrypt.sh/certs/${domain}/fullchain.pem

再次重新啟動 Apache

$ sudo /usr/syno/sbin/synoservicecfg --restart httpd-user

此時連線的 SSL 憑證就會是 Let’s Encrypt 簽發的了。

雖然 LE 的憑證效期只有 3 個月,不像 StartSSL 是一整年,要更頻繁的 renew,但有這類方便的 script 可以批次處理,反而更省事,所以這次把手上的 SSL 都改用 LE 了。