らくがきちょう

なんとなく ~所属組織/団体とは無関係であり、個人の見解です~

GCP 上で Route53 DNS 認証を使い lego で Let's Encrypt のワイルドカード証明書を取得する

lego を利用して Let's Encrypt の証明書を取得する手順については以前に下記のメモを書きました。

今回は以下の環境で証明書を取得する手順をメモしておきます。 尚、今回は GCP 上の仮想マシンに CentOS8 を使いました。

  1. GCPAWS を併用している
  2. GCP 上の仮想マシンで証明書を取得する
  3. 証明書はワイルドカード証明書を取得する
  4. 証明局は Let's Encrypt を利用する
  5. 証明書の取得には Route53 による DNS 認証を用いる
  6. 証明書の取得には lego を利用する

目次

テスト環境

今回は以下の環境でテストを行いました。 Nginx をインストールしていますが、lego で証明書を取得するだけであれば、Nginx は不必要です (インストールした証明書を利用する Web サーバとして Nginx をインストールしていました)。

AWS に IAM ユーザを作成する

証明書取得時の認証には Route53 を利用する為、予め該当の Hosted Zone へアクセス許可を持った IAM ユーザを作成し、プログラマブルアクセス許可出来るようにしておきます。 該当 IAM ユーザへ最小限のアクセスのみ、許可する場合は下記のような IAM Policy を付与します (AdministratorAccess などでも良いですが、セキュリティ的には最小の権限に留めるべきです)。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:GetChange",
                "route53:ListHostedZonesByName"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": [
                "arn:aws:route53:::hostedzone/<INSERT_YOUR_HOSTED_ZONE_ID_HERE>"
            ]
        }
    ]
}

AWS CLI をインストールする

GCP 上に作成した仮想マシンから lego を使って Route53 へアクセス出来るよう、AWS CLI をインストールしておきます。

pip install awscli

AWS CLI のインストールが完了したら aws configure で初期設定をしておきます。 Access Key / Secret Access Key には予め作成しておいた AWS IAM ユーザのものを指定します。 Route53 にはリージョンの概念が無い (Global しか無い) 為、Default region name は任意のものを指定しておきます。 同様に Default output format も任意に指定しておきます。

# aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

尚、指定可能なリージョン名は利用できるリージョンで確認することが出来ます。 現時点では以下が指定可能です。

No. コード 名前
1 us-east-2 米国東部 (オハイオ)
2 us-east-1 米国東部(バージニア北部)
3 us-west-1 米国西部 (北カリフォルニア)
4 us-west-2 米国西部 (オレゴン)
5 ap-east-1 アジアパシフィック (香港)
6 ap-south-1 アジアパシフィック (ムンバイ)
7 ap-northeast-3 アジアパシフィック (大阪: ローカル)
8 ap-northeast-2 アジアパシフィック (ソウル)
9 ap-southeast-1 アジアパシフィック (シンガポール)
10 ap-southeast-2 アジアパシフィック (シドニー)
11 ap-northeast-1 アジアパシフィック (東京)
12 ca-central-1 カナダ (中部)
13 eu-central-1 欧州 (フランクフルト)
14 eu-west-1 欧州 (アイルランド)
15 eu-west-2 欧州 (ロンドン)
16 eu-west-3 欧州 (パリ)
17 eu-north-1 欧州 (ストックホルム)
18 me-south-1 中東 (バーレーン)
19 sa-east-1 南米 (サンパウロ)

lego をインストールする

GCP 上に作成した仮想マシン上に lego をインストールします。 現時点ではバージョン 3.3.0 が最新でした。

mkdir tmp
cd tmp
wget https://github.com/go-acme/lego/releases/download/v3.3.0/lego_v3.3.0_linux_amd64.tar.gz
tar zxvf lego_v3.3.0_linux_amd64.tar.gz
mv lego /usr/local/bin/
chmod 755 /usr/local/bin/lego
chown root:root /usr/local/bin/lego

ワイルドカード証明書を取得する

lego を使ってワイルドカード証明書を取得します。

lego --accept-tos \
    --path=/etc/letsencrypt \
    --email="email@example.com" \
    --dns="route53" \
    --domains="example.com" \
    --domains="*.example.com" \
    run

何度か、以下のようなエラーが出ていました。 実際に curl https://acme-v02.api.letsencrypt.org/directory するとタイムアウトしていたのですが、しばらく待ってから同じコマンドを実行すると問題無く証明書が取得出来ました。

2020/02/01 12:29:57 Could not create client: get directory at 'https://acme-v02.api.letsencrypt.org/directory': Get https://acme-v02.api.letsencrypt.org/directory: dial tcp XXX.XXX.XXX.XXX:443: i/o timeout

SSL/TLS サーバ証明書を利用するように設定する

取得した SSL/TLS サーバ証明書は必要なアプリケーションで利用します。 Web サーバで使いたい場合は Mozilla SSL Configuration Generator を使うと各種 Web サーバ向けの SSL/TLS サーバ証明書を利用した設定ファイルを生成することが出来ます。

f:id:sig9:20200201133604p:plain

例えば以下のように指定してみると、後述のコンフィグが作成されました。

  • サーバは Nginx
  • コンフィグの方針は Modern
  • Server Version は 1.17.8
  • OpenSSL Version は '1.1.1c'
  • HSTS (HTTP Strict Transport Security) は無効
  • OCSP Stapling は無効

f:id:sig9:20200201133606p:plain

# generated 2020-02-01, Mozilla Guideline v5.4, nginx 1.17.8, OpenSSL 1.1.1c, modern configuration, no HSTS, no OCSP
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.8&config=modern&openssl=1.1.1c&hsts=false&ocsp=false&guideline=5.4
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /path/to/signed_cert_plus_intermediates;
    ssl_certificate_key /path/to/private_key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    # modern configuration
    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers off;
}

更新

証明書を更新する場合は (run では無く) renew を指定します。 証明書の有効期限が指定より下回っている場合のみ、更新させるには --days で日付を指定します。 但し、--days は指定位置が renew より 後である必要がある ようですので、指定場所には要注意です。

lego --accept-tos \
    --path=/etc/letsencrypt \
    --email="email@example.com" \
    --dns="route53" \
    --domains="example.com" \
    --domains="*.example.com" \
    renew \
    --days 30

cron で定期処理させるには、例えば以下のように設定します。 下記では「毎週、日曜日の 2:00 に実行しています。

cat << EOF > /etc/cron.d/renew-cert
0 2 * * 0 root /usr/local/bin/lego --accept-tos --path=/etc/letsencrypt --email="email@example.com" --dns="route53" --domains="example.com" --domains="*.example.com" renew --days 30
EOF