macOS ServerにLet’s Encryptの証明書を使い、GUIを使わずに自動更新する方法

はじめに

macOS Serverが提供する各種サービスについては、Serverの証明書を管理する仕組みに基づいて、各種サービスに適用されるようになっていて大変便利である。自己署名証明書であれば簡単に作成できる。ただ、外部の証明機関が発行する証明書は自身で取得して登録しなければならず、GUIでの手順は散見されるが、CUIでの手順はほとんど知られていないため、自動化する知見が共有されていない。管理に手間がかかり、うっかり更新を忘れて失効することもしばしばである。

ここでは、無償で証明書を取得することができ、CUIのクライアントのみで取得・更新が可能なLet’s Encryptの証明書を利用し、自動でServerに登録・更新する方法の一例を紹介する。

TL;DR

  • Let’s Encryptのユーザーフォーラム Mac OSX (Server): import LE certificate?でwikuさんを中心に議論された内容が全て
  • Cyril PicardさんがGitHubGistに公開しているcertbot4osxをもらってきて、cronなりlaunchdで定期実行すればOK

方針

  • 証明書は、certbotで取得・更新する
  • certbotは、Homebrewで取得する
  • certbotは、Webrootプラグインで認証する
    • Webサイトを有効にし、.well-known/acme-challengeディレクトリを公開する
  • certbot4osxを利用して、証明書をServerに反映する
  • certbot4osxを、launchdで定期実行する

certbot4osxでやっていること

  • certbotで証明書を新規取得・更新する
  • /etc/letsencrypt/live/*のそれぞれの証明書が、前回から更新されているかフィンガープリントを比較し、更新されていれば、その証明書について以下の処理を行う
    1. openssl pkcs12で秘密鍵、SSLサーバ証明書、中間CA証明書を、PKCS#12形式の単一ファイル.p12に結合する
    2. security importでシステムキーチェーンに.p12ファイルを取り込む
    3. certupdate replace -c 古い証明書 -C 新しい証明書で証明書の更新を通知する
    4. security delete-certificateで古い証明書をシステムキーチェーンから削除する

手順

Homebrewでcertbotをインストールする

Homebrewをまだ入れてなければ

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

しかる後に

brew install certbot

certbotのWebrootプラグインのために、認証用の一時ファイルを書き込むディレクトリを用意する

mkdir -p /Library/Server/Web/Data/Sites/Default/.well-known/acme-challenge

Webサイトを動かしておく

外部からアクセス出来るようにしておく。

certbot4osxをとってくる

certbot4osxからコピーしてきてファイルに保存するか、

curl -O https://gist.githubusercontent.com/cyrilpic/4504527de3a7b08ed84e/raw/8e3e317e63bd5a9203cfba8c049c8cfeac5d33f5/certbot4osx

という感じでダウンロードしてくる。

/usr/local/binとか適当なところに保存しておく。

最初の証明書の取得 兼 試運転

sudo /bin/bash /usr/local/bin/certbot4osx new example.com /Library/Server/Web/Data/Sites/Default

初回のみ、証明書期限切れの通知用メールアドレスの入力や、利用規約への合意などが必要。

Congratulations! と出て、ここに保存した、期限はいついつ、メールはここに送る、など重要な結果報告がされるので、承知しておく。

失敗した場合はその理由が表示されるので対処する。

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your cert
   will expire on 2018-05-03. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot-auto again. To
   non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you lose your account credentials, you can recover through
   e-mails sent to xxxx@example.com.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - 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

Server.appの証明書の画面を開き、期待通り登録されているかどうか確かめる。

Launchd用の.plistを作成

一つの例。

  • Labelが識別名になる。ファイル名も一致させる
    • jp.noellabo.certbot4osx.plist
  • 実行するコマンドと引数を指定
    • 証明書を更新する指定 renew
    • ドメイン名(実際のドメインに変えること) example.com
    • Webrootプラグイン用のディレクトリ /Library/Server/Web/Data/Sites/Default
  • 定期実行する時間の指定の仕方は色々あるので調べるべし
    • ここでは、4時と16時に実行される
  • 標準出力とエラー出力を、それぞれログにとる
  • 実行時にpathが効かないのでworkaround(実行時の環境変数指定)
    • 適切な対応方法不明。誰か教えて
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>jp.noellabo.certbot4osx</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/usr/local/bin/certbot4osx</string>
        <string>renew</string>
        <string>example.com</string>
        <string>/Library/Server/Web/Data/Sites/Default</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>4</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
    <key>StandardOutPath</key>
    <string>/var/log/certbot4osx.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/certbot4osx.err</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:/Applications/Server.app/Contents/ServerRoot/usr/sbin/</string>
    </dict>
</dict>
</plist>

所定の場所に入れ、登録、試運転

所定の場所に入れる。
ここに入れておくとデーモンとして実行される。
再起動すればロードされるが、明示的にロードすれば再起動しなくてOK。

sudo mv jp.noellabo.certbot4osx.plist /Library/LaunchDaemons/
sudo launchctl load /Library/LaunchDaemons/jp.noellabo.certbot4osx.plist

指定時間を待たずに、その場で実行してみる。

sudo launchctl start jp.noellabo.certbot4osx

実行ログ、エラーログを確認する。
何も出力されていなければ、実行に失敗しているので対処する。

sudo tail /var/log/certbot4osx.log
sudo tail /var/log/certbot4osx.err

シェアする

  • このエントリーをはてなブックマークに追加

フォローする