Skip to content

ARM 版 AL2023 で lego で Route53 認証をし、Let's Encrypt のワイルドカード証明証取得を自動化する

以前に lego を使い Route53 認証でサーバ証明書を取得する (2020/09/27 版) という、amd64 な Amazon Linux 2 ベースのメモを書きました。 今回は ARM 版 Amazon Linux 2023 をベースに、以前のメモと同じく lego を使って Let's Encrypt を Route53 認証し、ワイルドカード証明書を自動取得する方法をメモしておきます。

検証環境

Amazon Linux 2023 は ARM 版の t4g.micro インストールを利用しました。

対象 バージョン
Amazon Linux 2023.6.20241031
lego 4.19.2

IAM ポリシーの作成

以下の内容で IAM ポリシーを新規作成します。 <INSERT_YOUR_HOSTED_ZONE_ID_HERE> 部分は Route53 の管理画面で該当 DNS Zone 情報から取得します。 IAM ポリシーを作成したら、それを参照するロールを作成し、更にそれを参照するように EC2 インスタンスを設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:GetChange",
                "route53:ListHostedZonesByName",
                "route53:ListResourceRecordSets"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": [
                "arn:aws:route53:::hostedzone/<INSERT_YOUR_HOSTED_ZONE_ID_HERE>"
            ]
        }
    ]
}

lego のインストール

GitHub から ARM 用のバイナリをダウンロードし、インストールします。

curl -LO https://github.com/go-acme/lego/releases/download/v4.19.2/lego_v4.19.2_linux_arm64.tar.gz
tar zxvf lego_v4.19.2_linux_arm64.tar.gz
mv lego /usr/local/bin/
chmod 755 /usr/local/bin/lego
chown root:root /usr/local/bin/lego
rm CHANGELOG.md LICENSE lego_v4.19.2_linux_arm64.tar.gz

lego --version を実行してバージョン情報が表示されれば正しくインストールされています。

# lego --version
lego version 4.19.2 linux/arm64

Region の設定

lego で証明書を取得しようとすると以下のように「Invalid Configuration: Missing Region」というエラーになってしまいました。

1
2
3
4
2024/11/04 18:47:59 Could not obtain certificates:
 error: one or more domains had a problem:
[*.example.com] [*.example.com] acme: error presenting token: route53: failed to determine hosted zone ID: operation error Route 53: ListHostedZonesByName, failed to resolve service endpoint, endpoint rule error, Invalid Configuration: Missing Region
[example.com] [example.com] acme: error presenting token: route53: failed to determine hosted zone ID: operation error Route 53: ListHostedZonesByName, failed to resolve service endpoint, endpoint rule error, Invalid Configuration: Missing Region

Region を設定するには aws configure して設定ファイルを作成しても良いのですが、AccessKey / SecretKey を設定したく無い為、以下のように手動で ~/.aws/config ファイルを作成しました。

1
2
3
4
5
6
mkdir ~/.aws
cat << 'EOF' >> ~/.aws/config
[default]
region = ap-northeast-1
output = json
EOF

証明書の手動取得

lego を手動で実行し、証明書を取得する例は以下の通りです。 取得した証明書関連ファイルは /etc/letsencrypt/certificates 配下に保存されます。 IAM ポリシーベースで認証させている為、証明書取得時の DNS 認証用レコードは Route53 へ自動的に作成され、lego は対話的な入力は不要で終了します。 デフォルトでは ECDSA が利用されます。

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

明示的に RSA 指定したい場合は --key-type rsa2048 を追加します。

/usr/local/bin/lego \
  --accept-tos \
  --path=/etc/letsencrypt \
  --email="email@example.com" \
  --dns="route53" \
  --domains="example.com" \
  --domains="*.example.com" \
  --key-type rsa2048 \
  run

証明書の自動更新

証明書の更新は Systemd Timer で定期実行します。 今回は以下のような設定ファイルを作成しました。

/etc/systemd/system/lego-renew.timer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Unit]
Description=Lego Cert Renewal Timer

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

/etc/systemd/system/lego-renew.service

証明書の有効期限が 10 日を切ってから更新処理を行うようにする為、--days 10 を指定します。 証明書の初回発行時と同様、明示的に RSA 指定したい場合は --key-type rsa2048 を追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Unit]
Description=Lego Cert Renewal

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/lego --accept-tos --path=/etc/letsencrypt --email="email@example.com" --dns="route53" --domains="example.com" --domains="*.example.com" renew --days 10

[Install]
WantedBy=multi-user.target

タイマーを開始します。

systemctl daemon-reload
systemctl start lego-renew.timer
systemctl enable lego-renew.timer

ログの確認

動作ログを確認したい場合は以下を実行します。

journalctl -u lego-renew.timer

また「スケジューリング状況」(次、いつ実行されるのか?) を確認したい場合は以下を実行します。

systemctl list-timers

参考

lego のヘルプ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# lego --help
NAME:
   lego - Let's Encrypt client written in Go

USAGE:
   lego [global options] command [command options]

VERSION:
   4.19.2

COMMANDS:
   run      Register an account, then create and install a certificate
   revoke   Revoke a certificate
   renew    Renew a certificate
   dnshelp  Shows additional help for the '--dns' global option
   list     Display certificates and accounts information.
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --domains value, -d value [ --domains value, -d value ]      Add a domain to the process. Can be specified multiple times.
   --server value, -s value                                     CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory") [$LEGO_SERVER]
   --accept-tos, -a                                             By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service. (default: false)
   --email value, -m value                                      Email used for registration and recovery contact.
   --csr value, -c value                                        Certificate signing request filename, if an external CSR is to be used.
   --eab                                                        Use External Account Binding for account registration. Requires --kid and --hmac. (default: false) [$LEGO_EAB]
   --kid value                                                  Key identifier from External CA. Used for External Account Binding. [$LEGO_EAB_KID]
   --hmac value                                                 MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. [$LEGO_EAB_HMAC]
   --key-type value, -k value                                   Key type to use for private keys. Supported: rsa2048, rsa3072, rsa4096, rsa8192, ec256, ec384. (default: "ec256")
   --filename value                                             (deprecated) Filename of the generated certificate.
   --path value                                                 Directory to use for storing the data. (default: "/root/.lego") [$LEGO_PATH]
   --http                                                       Use the HTTP-01 challenge to solve challenges. Can be mixed with other types of challenges. (default: false)
   --http.port value                                            Set the port and interface to use for HTTP-01 based challenges to listen on. Supported: interface:port or :port. (default: ":80")
   --http.proxy-header value                                    Validate against this HTTP header when solving HTTP-01 based challenges behind a reverse proxy. (default: "Host")
   --http.webroot value                                         Set the webroot folder to use for HTTP-01 based challenges to write directly to the .well-known/acme-challenge file. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge
   --http.memcached-host value [ --http.memcached-host value ]  Set the memcached host(s) to use for HTTP-01 based challenges. Challenges will be written to all specified hosts.
   --http.s3-bucket value                                       Set the S3 bucket name to use for HTTP-01 based challenges. Challenges will be written to the S3 bucket.
   --tls                                                        Use the TLS-ALPN-01 challenge to solve challenges. Can be mixed with other types of challenges. (default: false)
   --tls.port value                                             Set the port and interface to use for TLS-ALPN-01 based challenges to listen on. Supported: interface:port or :port. (default: ":443")
   --dns value                                                  Solve a DNS-01 challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
   --dns.disable-cp                                             (deprecated) use dns.propagation-disable-ans instead. (default: false)
   --dns.propagation-disable-ans                                By setting this flag to true, disables the need to await propagation of the TXT record to all authoritative name servers. (default: false)
   --dns.propagation-rns                                        By setting this flag to true, use all the recursive nameservers to check the propagation of the TXT record. (default: false)
   --dns.propagation-wait value                                 By setting this flag, disables all the propagation checks of the TXT record and uses a wait duration instead. (default: 0s)
   --dns.resolvers value [ --dns.resolvers value ]              Set the resolvers to use for performing (recursive) CNAME resolving and apex domain determination. For DNS-01 challenge verification, the authoritative DNS server is queried directly. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
   --http-timeout value                                         Set the HTTP timeout value to a specific value in seconds. (default: 0)
   --dns-timeout value                                          Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name server queries. (default: 10)
   --pem                                                        Generate an additional .pem (base64) file by concatenating the .key and .crt files together. (default: false)
   --pfx                                                        Generate an additional .pfx (PKCS#12) file by concatenating the .key and .crt and issuer .crt files together. (default: false) [$LEGO_PFX]
   --pfx.pass value                                             The password used to encrypt the .pfx (PCKS#12) file. (default: "changeit") [$LEGO_PFX_PASSWORD]
   --pfx.format value                                           The encoding format to use when encrypting the .pfx (PCKS#12) file. Supported: RC2, DES, SHA256. (default: "RC2") [$LEGO_PFX_FORMAT]
   --cert.timeout value                                         Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
   --overall-request-limit value                                ACME overall requests limit. (default: 18)
   --user-agent value                                           Add to the user-agent sent to the CA to identify an application embedding lego-cli
   --help, -h                                                   show help
   --version, -v                                                print the version

lego renew のヘルプ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# lego renew --help
NAME:
   lego renew - Renew a certificate

USAGE:
   lego renew [command options]

OPTIONS:
   --days value                              The number of days left on a certificate to renew it. (default: 30)
   --ari-enable                              Use the renewalInfo endpoint (draft-ietf-acme-ari) to check if a certificate should be renewed. (default: false)
   --ari-wait-to-renew-duration value        The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint. (default: 0s)
   --reuse-key                               Used to indicate you want to reuse your current private key for the new certificate. (default: false)
   --no-bundle                               Do not create a certificate bundle by adding the issuers certificate to the new certificate. (default: false)
   --must-staple                             Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego. (default: false)
   --not-before value                        Set the notBefore field in the certificate (RFC3339 format)
   --not-after value                         Set the notAfter field in the certificate (RFC3339 format)
   --preferred-chain value                   If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.
   --always-deactivate-authorizations value  Force the authorizations to be relinquished even if the certificate request was successful.
   --renew-hook value                        Define a hook. The hook is executed only when the certificates are effectively renewed.
   --no-random-sleep                         Do not add a random sleep before the renewal. We do not recommend using this flag if you are doing your renewals in an automated way. (default: false)
   --help, -h                                show help