AWS上でLet’s Encryptの証明書更新(HTTP認証)を行う方法

AWSのEC2インスタンス上において、Let’s EncryptのSSL証明書更新時のみセキュリティグループを解放し認証を通す方法について解説します。

経緯

本サイトの環境をAWS上に全面移行

本ブログサイトの環境については2023年末にAWS上に完全移行しました。
アーキティクチャはこんな感じとなっています。

極力運用コストを抑えるため、Amazon CloudFrontにキャッシュさせるようにしています。
また、WP Offload Mediaプラグインによるメディアライブラリのオフロードと、記事更新時には関連オブジェクトのキャッシュクリアをするよう作り込んでいます。

このあたりは別サイトに記事を書いておりますので、参考にしてください。

また極力コストを抑えるため、EC2インスタンスはパブリックサブネット上に配置しElastic IPを用いて公開し、AWSマネージドのプレフィックスリストを用いてCloudFrontからの通信のみ開けるようにしています。

CloudFront用のマネージドプレフィックスリストは2022年2月から提供されるようになり、このようなケチケチ構成にも採用することができるのは良いですね。

この構成で、WAF込みで概ね$20/月くらいに収まっているイメージです。
(EC2 Instance Savings Plan/1年 利用)

本来であれば、RDS(MySQL)もしくはAuroraも使いたいんですけどね。
お財布の関係でEC2インスタンス内でオールインワンとしました。

CloudFront-EC2間の通信も暗号化したい

さて、今回はAmazon CloudFront – EC2間の通信に着目したいと思います。

今やAmazon CloudFront-Origin間はAWSネットワーク内であることが保証されているので、必ずしも暗号化が必須とまでは言えないかとは思いますが、できれば暗号化したいと思うのではないでしょうか。

通常の構成であれば、Application Load Balancer(ALB)とAWS Certificate Manager(ACM)の構成で終端するのですが、今回は採用することができません。
そこで、Let’s Encryptの無料証明書を用いて暗号化を行うこととしたいと思います。

Let’s Encryptの証明書を取得するためには、申請したFQDNが申請者のものであるかを検証することが必要です。
現在以下の方式がありますが、最もよく使われるのはHTTP-01チャレンジとなります。

  • HTTP-01チャレンジ
  • DNS-01チャレンジ
  • TLS-ALPN-01チャレンジ

HTTP-01チャレンジを成功させるためには、当然ながらHTTP(80/tcp)ポートを開ける必要があります。
Let’s EncryptのIP範囲は公開されていないので、Any(0.0.0.0/0)からの通信を開ける必要があるのですが、通常時は全く不要なので公開することには躊躇する方も多いでしょう。

そこで証明書更新の認証時のみ、AWS CLIを用いてセキュリティグループを変更し公開するようにしたいと思います。

 

手順

IAMユーザー/アクセスキーの発行

AWS CLIをEC2インスタンス上で利用するにあたり、IAMユーザーのアクセスキーを発行します。
なお最小権限の原則に則り、既存セキュリティグループにルールを追加・削除する権限のみ付与します。

IAMのマネジメントコンソールにアクセスし、ユーザー ⇒ ユーザーの作成を選択します。
適当なユーザー名を入力します。

IAMポリシーはビルドインのものではなく今回は手動で作成しますので、ポリシーの作成へ

以下の内容のポリシーを作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ModifySecurityGroup",
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        }
    ]
}

適当なポリシー名を入力して作成します。

続いて作成したポリシーをIAMユーザーに割り当てます。
作成したポリシーをアタッチします。

設定内容を確認して、ユーザーを作成します。

ユーザー作成後、当該ユーザーを開きアクセスキーを作成します。

CLIを選択します。

アクセスキーが発行されました。
このアクセスキー/シークレットアクセスキーをメモしておきます。

AWS CLIの初期設定

EC2インスタンスにログイン後、以下のようにアクセスキーを登録します。
なお複数権限を使い分けできるよう、AWS CLIのプロファイル機能を利用しています。

以下の作業は証明書更新処理の関連でrootユーザーで実行しています。
# aws configure --profile modify-sg
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]:

 

シェルスクリプトの作成

以下のシェルスクリプトを作成します。
なお、編集する対象のセキュリティグループID(※ sg-XXXXXXXXXXXXXXの部分)を把握しておく必要があります。

#!/bin/bash
/usr/bin/aws ec2 authorize-security-group-ingress --group-id sg-XXXXXXXXXXXXXXXXX --protocol tcp --port 80 --cidr 0.0.0.0/0 --profile modify-sg
/usr/bin/certbot renew
/usr/bin/aws ec2 revoke-security-group-ingress --group-id sg-XXXXXXXXXXXXXXXXX --protocol tcp --port 80 --cidr 0.0.0.0/0 --profile modify-sg

見ての通りではありますが、certbotによる証明書更新の前後にセキュリティグループの編集を行っています。
本シェルスクリプトをcronで実行することで、自動的にLet’s Encryptの証明書更新を行うことが可能です。

CLIコマンドについては以下のサイトを参考にしました。ありがとうございます。

まとめ

以上により、Amazon CloudFrontの裏側においても、Let’s Encryptの証明書を用いて暗号化できるようになりました。

バッチ処理するときだけ一時的に公開したい、あるいはアウトバウンド通信を許可したいというユースケースは多いと思いますので、応用は利くと思います。

ぜひご参考にしてください。では

コメント