大量のELBのSSL証明書をAWS CLIで簡単に更新する

2016.01.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWSのロードバランサであるELBではSSLの暗号化通信をクライアント<->ELB間で完結させるためのSSL Termination機能が備えられています。ELBにSSL証明書を配置することで、ELB配下のEC2にサーバ証明書が不要になりました。このメリットは、暗号化処理の負荷をWEBサーバからELBにオフロードすることができる点と、証明書の更新作業時に各サーバにサーバ証明書を配布して回る手間を軽減することが出来る点だと考えています。

とはいえ、証明書の更新時期が来たらELBに設置している証明書を更新する必要があります。通常のFQDN形式のサーバ証明書なら紐づくELBは1台だと思いますが、ワイルドカード証明書を利用している場合には更新対象のELBが数十台にもなることもあります。

この更新作業を繰り返し手動でManagement Consoleから実行するのは筋が悪いので、AWS CLIのみで更新を完結させる方法を紹介します。

手順

今回の作業では、AWS CLIとjqがインストールされており、必要なアクセスキーなどの設定が済んでいる環境を前提としています。

手順は以下の通りです。

  1. サーバ証明書のアップロード
  2. 更新前のサーバ証明書のARNを確認
  3. 更新前のサーバ証明書が割り当てられているELBの取得
  4. サーバ証明書の更新

手順1: サーバ証明書のアップロード

AWSでは、ELBやCloudFrontに紐付けるサーバ証明書はIAMで一括管理されています。サーバ証明書のアップロードにはIAMのupload-server-certificate APIを利用します。

$ aws iam upload-server-certificate \
    --certificate-body file://<証明書ファイル名> \
    --certificate-chain file://<中間証明書ファイル名> \
    --private-key file://<秘密鍵ファイル名> \
    --server-certificate-name <証明書の名前>

--server-certificate-nameでは、任意の名前を指定することができます。アップロード後はAPI経由では証明書のコモンネームや有効期限を確認することができないので、それらを示す名前が望ましいでしょう。

アップロードに成功すると、以下のようなレスポンスが返ってきます。

{
    "ServerCertificateMetadata": {
        "ServerCertificateId": "ASCAI3LCYWTOVUTK5IIOO",
        "ServerCertificateName": "<指定した証明書の名前>",
        "Expiration": "2030-12-31T05:07:43Z",
        "Path": "/",
        "Arn": "arn:aws:iam::0123456789012:server-certificate/<指定した証明書の名前>",
        "UploadDate": "2016-01-12T20:12:18.802Z"
    }
}

レスポンス内のArnを後で利用するので控えておきましょう。

手順2: 現在のサーバ証明書のARNを確認

現在利用しているのサーバ証明書を、一覧から確認します。list-server-certificatesを利用し、レスポンス内のServerCertificateNameExpirationから判断できるでしょう。

$ aws iam list-server-certificates

{
    "ServerCertificateMetadataList": [
        {
            "ServerCertificateId": "ASCAI3LCYWTOVUTK5IIOO",
            "ServerCertificateName": "foo_old.example.com_20160130",
            "Expiration": "2016-01-30T05:07:43Z",
            "Path": "/",
            "Arn": "arn:aws:iam::0123456789012:server-certificate/foo_example.com_20160130",
            "UploadDate": "2015-01-12T20:12:18Z"
        },
        {
            "ServerCertificateId": "ASCAI7BOUEQDJIHB4W3EQ",
            "ServerCertificateName": "foo_new.example.com_20170130",
            "Expiration": "2017-01-30T23:59:59Z",
            "Path": "/",
            "Arn": "arn:aws:iam::0123456789012:server-certificate/cert",
            "UploadDate": "2016-01-12T03:58:17Z"
        }
    ]
}

上記の例では、foo_old.example.com_20160130が更新前の証明書になります。このArnも次のステップで使うので控えておきましょう。

手順3: 更新前のサーバ証明書が割り当てられているELB一覧の取得

次に、更新前のサーバ証明書を利用しているELBの一覧を取得します。ELBの取得にはdescribe-load-balancers、絞り込みには上記のステップで取得した更新前サーバ証明書のARN(手順2)を利用して、jqを使って絞り込みます。

$ aws elb describe-load-balancers \
    | jq -r '.LoadBalancerDescriptions[] | select(.ListenerDescriptions[].Listener.SSLCertificateId == "<更新前サーバ証明書のARN>") | .LoadBalancerName'

このコマンドの結果として、更新前のSSL証明書を利用しているELBのELB名を取得することができます。

手順4: サーバ証明書の更新

ELBのサーバ証明書更新にはset-load-balancer-listener-ssl-certificateAPIを利用します。 オプションには、ロードバランサ名と更新後サーバ証明書のARN(手順1)を指定する必要があります。

$ aws elb set-load-balancer-listener-ssl-certificate \
      --load-balancer-name <ロードバランサ名> \
      --load-balancer-port 443 \
      --ssl-certificate-id <更新後サーバ証明書のARN>

ロードバランサが複数ある場合は、手順3の結果をループさせて利用しましょう。手順3と手順4を組み合わせると以下のスクリプトが完成します。

#!/bin/bash -e

# 次の2行は手順1,2の結果を記載
new_cert_arn=<更新後SSL証明書のARN>
old_cert_arn=<更新前SSL証明書のARN>

target_elbs=$(aws elb describe-load-balancers \
    | jq -r '.LoadBalancerDescriptions[] | select(.ListenerDescriptions[].Listener.SSLCertificateId == "'${old_cert_arn}'") | .LoadBalancerName')

for elb in $target_elbs; do
    echo "${elb}の証明書更新を実施します。"

    aws elb set-load-balancer-listener-ssl-certificate \
        --load-balancer-name ${elb} \
        --load-balancer-port 443 \
        --ssl-certificate-id ${new_cert_arn} \
done

(2016/02/01: スクリプトの誤表記を修正)

このスクリプトを実行すれば更新作業は完了です。かなり更新作業が簡略化できたのではないでしょうか。もちろん、更新後はブラウザや利用されるクライアントからの動作確認を忘れずに実施しましょう。

今回の作業では、手順4のset-load-balancer-listener-ssl-certificateにてSSLのポートを443で決め打ちしている点です。一般ユーザ向けのWEBサイトであれば443を利用していると思いますが、その他の用途でSSLを別のポートで利用されている方は状況に応じてポートを変更してください。「ポートがELBごとに違う!」という方も、下のようにjqでゴニョゴニョすれば利用しているポートを出すことは可能です。

# 更新前サーバ証明書を利用しているELBのポート取得
$ aws elb desribe-load-balancers \
    | jq'.LoadBalancerDescriptions[].ListenerDescriptions[] | select(.Listener.SSLCertificateId == "<更新前証明書のARN>") | .Listener.LoadBalancerPort'

面倒な繰り返し作業をスクリプト化して、快適なAWS CLI + jqライフを!