社内で使用しているknowledgeのWebアプリについてSSL証明書が切れてしまっており、自動更新の設定も当時はかけていなかったため、SSL証明書の新規発行と自動更新設定を行いました。
実行環境
SSL証明書の期限が切れているか確認する
root
ユーザーへ切り替えます。
❯❯❯ sudo -i
期限が切れているのは明白なのですが、状況を確認します。 ※ドメイン名は伏せてあります
❯❯❯ certbot certificates Saving debug log to /var/log/letsencrypt/letsencrypt.log Revocation status for /etc/letsencrypt/live/xxxxx.xxxxxxx.com/cert.pem is unknown - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Found the following certs: Certificate Name: xxxxx.xxxxxxx.com Domains: xxxxx.xxxxxxx.com Expiry Date: 2019-09-17 08:10:00+00:00 (INVALID: EXPIRED) Certificate Path: /etc/letsencrypt/live/xxxxx.xxxxxxx.com/fullchain.pem Private Key Path: /etc/letsencrypt/live/xxxxx.xxxxxxx.com/privkey.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
INVALID: EXPIRED
と出ているので期限が切れています。期限が切れている場合、SSL証明書の新規発行を行う必要があるため、再度発行していきます。
SSL証明書の新規発行を行う
ファイルは退避しておきます。
❯❯❯ mkdir /etc/letsencrypt/live/_bk ❯❯❯ mv /etc/letsencrypt/live/xxxxx.xxxxxxx.com /etc/letsencrypt/live/_bk
80, 443
のport
が使われているとエラーとなるのでhttpd
を停止しておきます。
❯❯❯ systemctl stop httpd
SSL証明書の新規発行を行います。
❯❯❯ certbot-auto certonly --standalone -d xxxxx.xxxxxxx.com
httpd
を起動します。
❯❯❯ systemctl start httpd Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe" for details.
エラーとなってしまいました…ここは内容を見て探っていきます。
❯❯❯ systemctl status httpd Oct 06 13:03:31 xxxxx-instance httpd[10894]: AH00526: Syntax error on line 101 of /etc/httpd/conf.d/ssl.conf: Oct 06 13:03:31 xxxxx-instance httpd[10894]: SSLCertificateFile: file '/etc/letsencrypt/live/xxxxx.xxxxxxx.com/fullchain.pem' does not exist or is empty Oct 06 13:03:31 xxxxx-instance systemd[1]: httpd.service: main process exited, code=exited, status=1/FAILURE Oct 06 13:03:31 xxxxx-instance kill[10895]: kill: cannot find process "" Oct 06 13:03:31 xxxxx-instance systemd[1]: httpd.service: control process exited, code=exited status=1 Oct 06 13:03:31 xxxxx-instance systemd[1]: Failed to start The Apache HTTP Server.
エラーを見るとSyntax error
と言っており、更にfullchain.pem
が存在しないと言っています。中身を見ていきます。
❯❯❯ vim /etc/httpd/conf.d/ssl.conf
該当箇所を見ると以下とありました。
SSLCertificateFile /etc/letsencrypt/live/xxxxx.xxxxxxx.com/fullchain.pem
フォルダも移動したので問題ない、と思っていましたがどうやら違いました。確認してみます。
❯❯❯ ll /etc/letsencrypt/live/ total 4 drwxr-xr-x. 3 root root 38 Oct 6 12:47 _bk drwxr-xr-x. 2 root root 93 Oct 6 19:59 xxxxx.xxxxxxx.com-0001 -rw-r--r--. 1 root root 740 Oct 6 12:56 README
フォルダ名がxxxxx.xxxxxxx.com-0001
となっており、名前が拡張されていました。少し気持ち悪さは残りますが、ssl.conf
内の以下で始まる定義のファイルパスに0001
を付与し変更します。
SSLCertificateFile SSLCertificateKeyFile SSLCertificateChainFile
再度httpd
の起動を行います。
❯❯❯ systemctl start httpd && systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Sun 2019-10-06 20:28:55 UTC; 13ms ago Docs: man:httpd(8) man:apachectl(8) Process: 11556 ExecStop=/bin/kill -WINCH ${MAINPID} (code=exited, status=0/SUCCESS) Process: 11515 ExecReload=/usr/sbin/httpd $OPTIONS -k graceful (code=exited, status=0/SUCCESS) Main PID: 11579 (httpd) Status: "Processing requests..." CGroup: /system.slice/httpd.service ├─11579 /usr/sbin/httpd -DFOREGROUND ├─11580 /usr/sbin/httpd -DFOREGROUND ├─11581 /usr/sbin/httpd -DFOREGROUND ├─11582 /usr/sbin/httpd -DFOREGROUND ├─11583 /usr/sbin/httpd -DFOREGROUND └─11584 /usr/sbin/httpd -DFOREGROUND Oct 06 20:28:55 xxxxx-instance systemd[1]: Starting The Apache HTTP Server... Oct 06 20:28:55 xxxxx-instance systemd[1]: Started The Apache HTTP Server.
修正箇所は正解ですね。これでhttpd
が動作しました。
SSL証明書の自動更新を設定する
元を正せば更新を自動化していないのが問題ですね。期限が迫る度に手動で更新とか手間ですし、少し面倒な事を後回しにすると人間は大体忘れます。なので、このままSSL証明書の自動更新を行うように設定していきます。実際にSSL証明書の更新はかけずにチェックを行いたいので—dry-run
をオプションにつけてrenew
を実行します。
❯❯❯ cd /usr/local/certbot/ ❯❯❯ ./certbot-auto renew --dry-run Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com-0001.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator standalone, Installer None Renewing an existing certificate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Traceback (most recent call last): File "/opt/eff.org/certbot/venv/lib/python2.7/site-packages/certbot/renewal.py", line 64, in _reconstitute renewal_candidate = storage.RenewableCert(full_path, config) File "/opt/eff.org/certbot/venv/lib/python2.7/site-packages/certbot/storage.py", line 465, in __init__ self._check_symlinks() File "/opt/eff.org/certbot/venv/lib/python2.7/site-packages/certbot/storage.py", line 523, in _check_symlinks "expected {0} to be a symlink".format(link)) CertStorageError: expected /etc/letsencrypt/live/xxxxx.xxxxxxx.com/cert.pem to be a symlink Renewal configuration file /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf is broken. Skipping. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem (success) Additionally, the following renewal configurations were invalid: /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf (parsefail) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 renew failure(s), 1 parse failure(s)
またまたエラーです。ここでは以下のエラーが問題となっています。
CertStorageError: expected /etc/letsencrypt/live/xxx.xxx.com/cert.pem to be a symlink Renewal configuration file /etc/letsencrypt/renewal/xxx.xxx.com.conf is broken. Skipping.
シンボリックリンクについてとファイルが壊れているという内容ですね。そもそもですが、前述したhttpd
が起動しない問題と同じようにパスが違うだけと想定しますが、確認してみましょう。
どのファイル末尾に2
とprefix
が振られてしまっているのは気になりますが、live
内ではarchive
のシンボリックリンクがはられているのでこちらについては問題なさそうです。
❯❯❯ ll /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/ total 4 lrwxrwxrwx. 1 root root 53 Oct 6 19:59 cert.pem -> ../../archive/xxxxx.xxxxxxx.com-0001/cert2.pem lrwxrwxrwx. 1 root root 54 Oct 6 19:59 chain.pem -> ../../archive/xxxxx.xxxxxxx.com-0001/chain2.pem lrwxrwxrwx. 1 root root 58 Oct 6 19:59 fullchain.pem -> ../../archive/xxxxx.xxxxxxx.com-0001/fullchain2.pem lrwxrwxrwx. 1 root root 56 Oct 6 19:59 privkey.pem -> ../../archive/xxxxx.xxxxxxx.com-0001/privkey2.pem -rw-r--r--. 1 root root 692 Oct 6 12:56 README
renewal
内のファイルを確認します。
❯❯❯ vim /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf
中身を見ると存在しないパスを指定しているのが問題でした。0001
を付与していきます。
# 変更前 archive_dir = /etc/letsencrypt/archive/xxxxx.xxxxxxx.com cert = /etc/letsencrypt/live/xxxxx.xxxxxxx.com/cert.pem privkey = /etc/letsencrypt/live/xxxxx.xxxxxxx.com/privkey.pem chain = /etc/letsencrypt/live/xxxxx.xxxxxxx.com/chain.pem fullchain = /etc/letsencrypt/live/xxxxx.xxxxxxx.com/fullchain.pem # 変更後 archive_dir = /etc/letsencrypt/archive/xxxxx.xxxxxxx.com-0001 cert = /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/cert.pem privkey = /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/privkey.pem chain = /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/chain.pem fullchain = /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem
再度、更新はかけずにチェックを行うコマンドを実行します。
❯❯❯ ./certbot-auto renew --dry-run Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com-0001.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator standalone, Installer None Renewing an existing certificate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator standalone, Installer None Renewing an existing certificate - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem (success) /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
今度は成功しました。これでSSL証明書の更新についてテストが出来ました。
自動更新の設定を行う
renew
の更新作業後はhttpd
の起動を実行したいので、cron
を使用します。念の為cron
が動作しているかを確認します。
❯❯❯ systemctl status crond ● crond.service - Command Scheduler Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled) Active: active (running) since Sun 2019-10-06 18:41:14 UTC; 2h 42min ago Main PID: 462 (crond) CGroup: /system.slice/crond.service └─462 /usr/sbin/crond -n Oct 06 18:41:14 knowledge-instance systemd[1]: Started Command Scheduler. Oct 06 18:41:14 knowledge-instance crond[462]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 36% if used.) Oct 06 18:41:14 knowledge-instance crond[462]: (CRON) INFO (running with inotify support)
cron
は動作しているので以下のように記述していきます。crontab -e
で作成しようと思いましたがここは別ファイルでの管理にします。理由として間違ってキーボード隣のr
をタイプしcrontab -r
を実行すると無慈悲に全て消え失せますので要注意です。
❯❯❯ vim /etc/cron.d/letsencrypt # 毎週日曜日の23:50に実行。/var/log/letsencrypt/result-renewal.logへ書き込むようにしておく。 50 23 * * 0 root /usr/local/certbot/certbot-auto renew --post-hook "systemctl reload httpd" > /var/log/letsencrypt/result-renewal.log
cron
へ作成したファイルを反映します。
❯❯❯ crontab /etc/cron.d/letsencrypt
一度、毎分のcron
が実行されるように変更を行い、実際に動いているかを確認します。
❯❯❯ vim /etc/cron.d/letsencrypt # 毎週日曜日の23:50に実行。/var/log/letsencrypt/result-renewal.logへ書き込むようにしておく。 * * * * * root /usr/local/certbot/certbot-auto renew --post-hook "systemctl httpd reload" > /var/log/letsencrypt/result-renewal.log ❯❯❯ less /var/log/letsencrypt/result-renewal.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com-0001.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/xxxxx.xxxxxxx.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The following certs are not due for renewal yet: /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem expires on 2020-01-04 (skipped) /etc/letsencrypt/live/xxxxx.xxxxxxx.com-0001/fullchain.pem expires on 2020-01-04 (skipped) No renewals were attempted. No hooks were run. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
確認が出来たら実行したいスケジュールに戻しておきます。これでSSL証明書の期限が迫っていたら自動更新が行われるようになります。
まとめ
今回については既にSSL証明書を導入済みという前提で進めてはいますが、Let’s Encryptの期限は3か月で切れるため、自動更新をしておくと後々かなり手間が省けます。「忘れんなよ」という話しではあるのですが…そこまで使われていないシステムだとSSL証明書の更新は忘れがちです。自動化のためにある程度の手間をかけることで後で楽が出来ます。SSL証明書の自動更新に関わらず、こういった考えは他の業務を遂行する上でも有用ではないでしょうか。