こんにちは。エンジニアの nobushi です。
サーバーアプリケーションをデプロイする場合、絶対必要なのが SSL 証明書ですよね。しかしながらこの SSL 証明書、有料だったり更新が必要だったり何かと面倒です。
そんなエンジニアの強力な味方となってくれるのが Let’s Encrypt です。 開発中に一時的に必要な証明書はもちろん、本番環境のサーバーでも使用可能。 しかも無料。これでもう社内稟議に悩まされることはありません。
ただし。
もちろんいい話ばかりではありません。
この Let’s Encrypt の証明書、有効期限が一律90日と、とても短いです。現実的な運用を考えると2ヶ月程度ごとに更新作業が必要となるでしょう。
さすがにそれは面倒くさい!
それだったらお金払って解決した方かいいかも・・・ と考えてしまうのも無理はありません。
そこで自動更新です。
運用例を紹介します。
概要
自動更新の運用の流れは以下の通りです。
- デイリーでバッチ起動
- 対象の証明書の期限をチェック
- 期限が30日以上ならば終了
- 新しい証明書を作成
- 作成した証明書を GitHub Actions の Secret に登録
- GitHub Actions で新しいサーバーコンテナをビルドし、レジストリに登録
- GitHub Actions で Terraform の apply を実行。サーバーをデプロイ
これを行うためのサーバーアプリケーションを構築します。
デイリーでバッチ起動
これは GCP Cloud Scheduler 等を使えば良いでしょう。 サーバーアプリケーションは Web サーバーとして構築するのが簡単だと思います。
対象の証明書の期限をチェック
対象のサーバーの証明書情報から期限を取得します。
例: Python の場合
SSLライブラリ とpyca/cryptographyライブラリ を使用します。
import datetime
import ssl
from cryptography import x509
def get_server_cert_not_valid_after(domain_name: str) -> datetime:
cert_pem = ssl.get_server_certificate((domain_name, 443))
certificate = x509.load_pem_x509_certificate(
cert_pem.encode("utf-8"))
return certificate.not_valid_after
この関数を実行することで証明書の期限が取得できます。
新しい証明書を作成
ACME クライアントを使用して新しい証明書を作成します。
手順中にDNSへの登録が必要になるので利用するDNSに対応したクライアントかカスタマイズができるクライアントを選択する必要があります。
参考までに、 Python 、Google Cloud DNS の組み合わせであればSewer でGoogle用のProviderを自作することで対応可能でした。 AWS ならば自作せずとも対応しているものが多いのでもっと簡単に対応できると思います。
作成した証明書を GitHub Actions の Secret に登録
GitHub にアクセスするためには OAuth アプリの登録が必要です。こちらを参考にしてください。 アプリケーションからのアクセスなのでデバイスフロー を有効にする必要があります。
登録が完了し、デバイスアクセストークンが取得できたら以下の手順で Secrets の登録ができます。 BASE64で登録するのが良いと思います。
- アクセストークンを使用し、Get a repository public key でパブリックキーを取得
- 前手順のパブリックキーを使用して証明書を暗号化
- アクセストークンを使用し、Create or update a repository secret で Secrets を更新
GitHub Actions で新しいサーバーコンテナをビルドし、レジストリに登録
証明書を組み込むサーバーのコンテナをビルドし、レジストリにpushするようなGitHub Actions を構成します。 対象のサーバーの GitHub リポジトリに組み込んでおけば良いでしょう。
コンテナビルド時に使用する証明書は Secret から取得するようにしておきます。 前手順で登録しているので最新の証明書を組み込んでビルドすることになります。
例: Nginx の場合のDockerfile
FROM nginx:1.21.5-alpine
ARG SERVER_CERT_BASE64
ARG SERVER_CERT_KEY_BASE64
RUN echo ${SERVER_CERT_BASE64} | base64 -d > /etc/nginx/server.crt
RUN echo ${SERVER_CERT_KEY_BASE64} | base64 -d > /etc/nginx/server.key
SERVER_CERT_*_BASE64
は Secrets から取得して docker build に渡す想定です。
そして、Create a workflow dispatch event で、 Actions を起動します。 そのためには actions 側で workflow_dispatch に対応しておく必要があります。 このあたりの詳細は、こちら を参照してください。
GitHub Actions で Terraform の apply を実行。サーバーをデプロイ
前手順でレジストリ上のコンテナが最新の証明書を含んだサーバーに更新されましたので、あとはそのコンテナをデプロイすれば完了です。
デプロイするためにはTerraform 等の IaC ツールを使うのが良いかと思います。 前回の記事 でその辺りの話をしているので参考にしてください。
所感
更新の手間さえなくなれば Let’s Encrypt の証明書を使うので十分です。
ただ、通常はクラウドのマネージド証明書を使うのであまり使う機会もなかったりしますが。 色々な条件でマネージド証明書が使えない場合もあるのでその際にはこのような仕組みを使ってみてはいかがでしょうか。