ご無沙汰しております。freemakeのウェブ担当です。しばらく旅に出ておりました🙇
さて、2月21日に弊社ウェブサイトにて一時的な証明書の有効期限切れが発生しておりました。
関係各位にはご迷惑をおかけしましたこと、深くお詫び申し上げます……それと共に、新人が建ててくれたこの新しいウェブサーバーでなぜ証明書の有効期限切れが発生したのかについて、トラブルシューティングをしましたのでご参考までに書き記させていただきます。
起きたこと
以前より freemake のサイトでは、Let’sEncrypt を使ったフリーSSL証明書を採用しています。数年前の黎明期にも私が記事を書いたりしましたが、あの頃よりも様々な面で洗練され、採用例も増えて市民権を得ていますね。
現在では certbot コマンドひとつで証明書の取得、更新、インストールまで行ってくれて非常に使い勝手もよいです。相変わらず有効期限は短く3か月のままですが、 certbot が優秀なために弊害はないようなものでしょう。
Linux には指定した時間や日時で処理を自動で行ってくれる cron と呼ばれるサービスがあります。certbot を毎日決まったタイミングで繰り返し処理させれば、必要に応じて証明書を更新し続けてくれて常にセキュアな環境を保ってくれる……はずでした。
先に言っておきますが、これは彼が悪いというわけではありません。ある程度以上に Linux という仕組みに慣れていない人間にはトラップと言ってもよい事象が起きていたためです。
新人くんはなにをしたのか?
証明書が更新されていないということは、まず疑うべきはcronの状態です。
cron に処理を登録するためには、よく crontab -e
というコマンドを使います。そして、その内容を確認するためには crontab -l
です。
[ec2-user@ip-XXX-XX-X-XXX ~]$ sudo crontab -l
39 1,13 * * * root certbot renew --no-self-upgrade
……なるほど。では、cron の実行ログはどうでしょうか。
[ec2-user@ip-XXX-XX-X-XXX ~]$ sudo cat /var/log/cron | grep certbot
Feb 19 13:39:01 ip-XXX-XX-X-XXX CROND[13559]: (root) CMD (root certbot renew --no-self-upgrade)
Feb 20 01:39:01 ip-XXX-XX-X-XXX CROND[821]: (root) CMD (root certbot renew --no-self-upgrade)
Feb 20 13:39:01 ip-XXX-XX-X-XXX CROND[21084]: (root) CMD (root certbot renew --no-self-upgrade)
Feb 21 01:39:01 ip-XXX-XX-X-XXX CROND[7735]: (root) CMD (root certbot renew --no-self-upgrade)
……なるほどなるほど。
本来実行すべきだったコマンド certbot renew --no-self-upgrade
ではなく、 root certbot renew --no-self-upgrade
というコマンドが実行されてしまい、そして失敗し続けていたのですね。 サーバに root というコマンドなど存在しません。
どうしてこうなったのか? その理由は crontab -e
の記入内容が間違えていたからに他なりません。エラーログからもわかる通り、rootという実行時ユーザーに関する記述が不要なのです。正しくはこうあるべきでした。
[ec2-user@ip-XXX-XX-X-XXX ~]$ sudo crontab -l
39 1,13 * * * certbot renew --no-self-upgrade
これで設定は正しくなったはずです。念のため、crontab が実際に参照しているファイルを確認してみましょう。
/var/spool/cron 以下にあります。
[ec2-user@ip-XXX-XX-X-XXX ~]$ sudo cat /var/spool/cron/root
39 1,13 * * * root certbot renew --no-self-upgrade
はい、きちんと反映されていますね。いやしかし、この流れを見るだけでも非常にややこしい……
なにがややこしいって、 /var/spool/cron
の記述は crontab -e
と違って実行時ユーザーを含む記述になっているのですから。
ややこしいcrontab
実は crontab には複数の設定方法があり、実行時ユーザーを指定する書き方と指定しない書き方がそれぞれ必要となる場面があります。
先ほど確認した /var/spool 以下のファイルを直に書き換える方法は推奨されませんので除外するとしても、です。
まず実行時ユーザーを指定しない方法は、この記事でも何度も触れた crontab -e
です。
この場合、cron に登録されたジョブの実行者は crontab コマンドを実行したユーザー(または uオプションで指定されたユーザー)となります。実行者が最初から決まっているために、書式に実行者を書き入れる必要がありません。
もう少し正確に言うと、内部的には /var/spool/cron 以下のファイルに実行時ユーザーの情報を追加した上で転記されるため、 crontab コマンドでは実行者について考える必要がないのです。
その一方で、実は crontab コマンドを使用せずに /etc/crontab
を編集してジョブを追記する方法があります。この場合、サービスとしてのcrontabを実行しているのはシステムですが、個別のジョブは /etc/crontab に記載されているユーザーが実行したことになります。
初期状態の /etc/crontab ファイルは次のような内容になっています。
[ec2-user@ip-XXX-XX-X-XXX ~]$ sudo cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
見ての通り、こちらの用例には user-name
が必須として記載されています。つまり、実行時ユーザーを書き入れる必要があります。
そう、新人君がハマってしまった罠とは、 /etc/crontab
を直に編集する際に使用すべき書式を、 crontab -e
で使用してしまったたことだったのです。
……以上、『Linuxあるある』なトラブルシューティングはいかがだったでしょうか。
失敗も大事な経験ですし、弊社ではそのために新人君にはあえて社内のシステムで研鑽を……っていやちょっとまってください。
いえ、ここで話は終わるはずだったのです。本来ならば。
少し調べたところ、この問題にはもう1つの闇が関わっていました。
コトの真相
新人君は別の案件を進行中ですので、ここまでの調査と復旧作業は私が行いました。調査中、ずっと気になっていたことがあります。
『このジョブは、なぜこの時間に実行するようにしたのだろう――?』
『毎日の1時39分および13時39分に更新チェックを行う』という設定、なにかしらの根拠がないとパッと浮かぶものでもありません。おそらくどこかのサイトの記述か、マニュアルなどを読んで参考にして設定したに違いありません。
ちょっと検索をかけて調べてみます。すると……
見つけました。AWS のマニュアルにそのものズバリの説明があります。どうやら新人君はちゃんと自分で調べて、信頼性の高い情報を取捨選択していたようです。素晴らしい。
いや、でも、なぜこのようなことに……んー?
あー、これは紛らわしいー!!
英語からの翻訳の都合かなにかかも知れませんが、少なくとも日本語では 1.に AまたはBと書かれていたらAとBとどっちでも変わらないと思って 2.以降の手順も進めてしまうでしょう。
確かにある程度のLinux経験値がある人なら「おや?」と気づくべきかもしれません。しかし、このマニュアルが必要な人の大半にそれは酷というものでしょう。もちろん彼――新人君にも。
今回の事案、新人君……君は全く悪くない。
まぁ、新人君が完成させたものをきっと大丈夫と思ってチェックが甘かった私も深く反省すべしとは思うのですが……
……と言うか、Let’sEncrypt証明書の期限切れ警告メールって、確かサーバーの状況と関係なく管理者に届きましたよね?
だとすると、そのメールを受け取ってたのって……
社長「あ、そういえば来てたけど、新しいサーバーは証明書購入してると勘違い&古いサーバーの分だと思ってスルーしてましたわー。ゴメンチョ。テヘペロ♪」
社長ォー!!