paint-brush
AWS LetsEncrypt Lambda または OpenTofu と Go を使用して AWS 用のカスタム TLS プロバイダーを作成した理由@kvendingoldo
794 測定値
794 測定値

AWS LetsEncrypt Lambda または OpenTofu と Go を使用して AWS 用のカスタム TLS プロバイダーを作成した理由

Alexander Sharov15m2024/06/06
Read on Terminal Reader

長すぎる; 読むには

AWS LetsEncrypt Lambda は、Let's Encrypt を介して AWS 証明書を発行、更新、管理する Go 1.22 ベースのアプリケーションです。証明書の発行後、証明書は AWS Certificate Manager と AWS Secret Manager 内に保存されます。Lambda は完全にカスタマイズ可能で、Terraform または OpenTofu を介して AWS にデプロイできます。また、問題なくローカルで実行することもできます。
featured image - AWS LetsEncrypt Lambda または OpenTofu と Go を使用して AWS 用のカスタム TLS プロバイダーを作成した理由
Alexander Sharov HackerNoon profile picture
0-item
1-item

最近では、TLS 証明書で保護されていないパブリック API エンドポイントを持つシステムを想像するのは困難です。証明書を発行する方法はいくつかあります。


  • 大手 TLS プロバイダーから購入できる有料のワイルドカード証明書
  • 企業のPKIシステムによって発行されるすべての下流証明書に署名する有料ルート証明書
  • LetsEncryptやAWS Certificate ManagerなどのTLSプロバイダーが発行する無料の証明書
  • OpenSSLまたは他のツールによって発行された自己署名証明書


この投稿では、主に AWS 内で使用できる無料の証明書について説明しますが、AWS サービスだけではありません。マネージド AWS サービスのみを使用し、厳格なセキュリティ要件がない場合は、AWS Certificate Manager以外のものを使用することは明らかに意味がありません。AWS Certificate Manager は、DNS または HTTP チャレンジを介して証明書を発行する非常に便利で迅速な方法を提供します。ただし、物理的な証明書ファイルを必要とする Nginx を実行する EC2 インスタンスなど、AWS サービス外 (API Gateway、ALB、NLB など) でこれらの証明書を使用する必要がある場合は、基本的な AWS の制限に直面します。さらに、リクエストした場合でも、AWS Certificate Manager は証明書の内容を表示しません。


ここで、Certificate Manager よりも広く使用されているツールであるLetsEncryptについて思い出すのにちょうどいいタイミングです。少なくとも、クラウドに依存しないという理由で。残念ながら、AWS には組み込みの LetsEncrypt 証明書発行技術はありません。EC2 または ECS サービスに certbot ツールを利用することは可能ですが、そのシナリオでは、更新プロセスの構成方法を検討する必要があります。また、システム全体の複雑さを軽減するために、すべてに対して単一の手順を使用する方が良いと考えているため、さまざまな戦略を組み合わせたくはありません。


それを考慮して、複雑な設定を必要とせずに LetsEncrypt 証明書を自動的に発行および更新する Lambda 関数を作成しました。最初の証明書発行後、証明書は AWS Certificate Manager 証明書とともに ARN を使用して任意の AWS サービスで利用できます。さらに、Nginx を実行している EC2 インスタンスでも他の場所でも、 AWS Secrets Managerに保存されている物理証明書バージョンを任意の場所で使用できます。

AWS LetsEncrypt Lambdaはどのように機能するのか

この記事では、DNS ゾーンが AWS Route53 によって管理されていると想定します。

この記事で説明する Lambda 関数は、Go v1.22 で記述されています。DNS レコード、シークレット、証明書などのすべての結果リソースは、デフォルトで Terraform コードによって作成される Amazon IAM ロールによって制御されます。Lambda アクションのシーケンスは次のとおりです。


  1. 証明書リストを含むイベントを取得します。通常、このイベントは、手動実行、またはaws_cloudwatch_event_target経由で行われた cron による実行の結果です。イベントの例:
 { "domainName": "hackernoon.referrs.me", "acmeUrl": "prod", "acmeEmail": "[email protected]", "reImportThreshold": 10, "issueType": "default", "storeCertInSecretsManager" : true }
  1. AWS Certificate Manager に証明書が存在するかどうかを確認します。存在する場合は、有効期限を確認します。
  2. 有効期限までの日数がreImportThresholdより少ない場合は、LetsEncrypt DNS-01チャレンジを開始します。このステップでは、Lambda がドメイン名を AWS Route53 ゾーンに一致させるTXTレコードを作成し、証明書の準備ができるまで待機します。
  3. 準備ができたら、Lambda は AWS Certificate Manager の証明書を更新します。
  4. storeCertInSecretsManagerが true の場合、Lambda は証明書ファイルを AWS Secrets Manager 内に保存します。


AWS LetsEncrypt Lambda、シーケンス図。

Lambda実装の詳細

コード

Lambda は Go 1.22 で書かれています。できるだけ少ないライブラリを使用することで、コードをシンプルに保つという目標を維持することができました。必要な Go ライブラリの完全なリストは次のとおりです。

メールアドレス

説明

github.com/aws/aws-lambda-go

Go 開発者が AWS Lambda 関数を開発するのに役立つライブラリ、サンプル、ツール。

github.com/aws/aws-sdk-go-v2

Go プログラミング言語用の AWS SDK。

レゴ

LetsEncrypt / ACME クライアントとライブラリ。

github.com/guregu/null

null 許容値の適切な処理。

github.com/sirupsen/logrus

Go 用の構造化されたプラグ可能なロギング。


Dockerイメージ

基本的な Docker イメージとして、 gcr.io/distroless /static:nonroot を使用しました。libc を必要としない Go アプリケーションの場合、このイメージは最適です。これはscratchから完全に空ではなく、次のものが含まれています。


  • CA 証明書: 他のステージからコピーする必要はありません。
  • /etc/passwd: 非ルートなどのユーザーとグループが含まれます。
  • /tmp フォルダ。
  • tzdata: UTC 以外のタイムゾーンを設定する場合。


ビルドプロセス

大規模なソフトウェア プロジェクトでは、ビルド プロセスの監視は面倒で時間のかかる作業になることがあります。Makefile を使用すると、このプロセスを自動化して効率化し、プロジェクトを効率的かつ一貫してビルドできるようになります。そのため、私はすべての Golang プロジェクトで Makefile を使用することを好みます。ファイルはシンプルです。


 ##@ General help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) fmt: ## Run go fmt against code. go fmt ./... vet: ## Run go vet against code. go vet ./... ##@ Build build: fmt vet ## Build service binary. go build -o bin/lambda main.go run: vet ## Run service from your laptop. go run ./main.go ##@ Test lint: ## Run Go linter golangci-lint run ./... test: ## Run Go tests go test ./...


CICD側ではGoアプリケーションの典型的なセットアップを使用しました

  1. 継続的インテグレーションとしてのGitHub Actions

  2. ghcr.io をDocker レジストリとして使用します。DockerHub と比較すると、こちらの方が私が好んで使用する 2 つの重要な機能があります。

    1. GHCR と GitHub Actions のスムーズな統合により、ビルド、テスト、およびデプロイメントのワークフローを GitHub リポジトリから直接簡単に自動化できます。これにより、生産性が向上し、開発プロセスが簡素化されます。
    2. GHCR は GitHub の権限モデルを活用し、ユーザーがコード リポジトリに使用するのと同じチームと権限を使用してコンテナ イメージへのアクセスを管理できるようにします。これにより、ユーザー管理が簡素化され、セキュリティが強化されます。
  3. kvendingoldo/semver-action : 自動バージョン管理用の GitHub Actions プラグイン。リポジトリ コミットのSemVer互換タグを生成する GitHub アクションです。このアクションは、バージョンを管理し、GitHub リリースを生成し、リポジトリ内のリリース ブランチを制御できます。単一リポジトリとモノリポジトリの両方でうまく機能します。

  4. 自動変更ログ生成。私は変更ログを楽しんでいます。私が管理している他のオープンソース プロジェクト (例: https://github.com/tofuutils/tenvhttps://github.com/tofuutils/tofuenvなど) のコンテキストでは、私のチームは変更についてユーザーに通知することの重要性を認識しました。

  5. golangci-lint 。私の見解では、すべてのコードは静的コード アナライザーでレビューする必要があります。SonarQube はすべてのプロジェクトに設定できるわけではありませんが、小規模から中規模の Go プロジェクトには golangci で十分だと私は考えています。

  6. コードスペル.ymlコードチェックに加えて、特に大量のドキュメントがある場合は、文法を検証することをお勧めします。

Terraform/OpenTofu 経由で Lambda を AWS にデプロイする方法

このページで説明されているコードは Terraform と OpenTofu で同じですが、Terraform v1.6 以降、Hashicorp は Terraform ライセンスを Business Source License (BSL) v1.1 に変更しました。Terraform 上で商用のものを開発している場合は、できるだけ早く OpenTofu に切り替えてください。

OpenTofuまたはTerraformの複数のバージョンを管理する必要がある場合は、テンヴ- Go で書かれた OpenTofu、Terraform、Terragrunt、Atmos バージョン マネージャー。

その他の Terraform / OpenTofu の例は、 Git リポジトリ内の examples フォルダーにあります。

OpenTofu 経由で AWS LetsEncrypt Lambda を使用するには、次の手順を実行する必要があります。

  1. OpenTofu / Terraformコードにモジュールを追加する

    module "letsencrypt_lambda" { source = "[email protected]:kvendingoldo/aws-letsencrypt-lambda.git//files/terraform/module?ref=0.31.4" blank_name = "kvendingoldo-letsencrypt-lambda" tags = var.tags cron_schedule = var.letsencrypt_lambda_cron_schedule events = var.letsencrypt_lambda_events ecr_proxy_username = var.ecr_proxy_username ecr_proxy_access_token = var.ecr_proxy_access_token }
  2. 変数を指定する

    variable "tags" { default = { hackernoon : "demo" } } variable "ecr_proxy_username" { default = "kvendingoldo" } variable "ecr_proxy_access_token" { default = "ghp_xxx" } variable "letsencrypt_lambda_cron_schedule" { default = "rate(168 hours)" } variable "letsencrypt_lambda_events" { default = [ { "acmRegion" : "us-east-1", "route53Region" : "us-east-1", "domainName" : "hackernoon.referrs.me", "acmeUrl" : "stage", "acmeEmail" : "[email protected]", "reImportThreshold" : 100, "issueType" : "default", "storeCertInSecretsManager" : false } ] }
  3. 変数ecr_proxy_usernameecr_proxy_access_tokenに注意してください。デフォルトでは、AWS Lambda は AWS ECR 以外のソースからイメージをプルできません。幸い、AWS チームは ECR プロキシキャッシュを作成しました。これは、DockerHub や GHCR などの公開レジストリからイメージをフェッチして ECR 内に保存できます。この可能性にもかかわらず、AWS では、公開されているパブリックリポジトリからであっても、トークンなしでイメージをプルすることは許可されていません。そのため、ビルド済みの Docker イメージにアクセスするには、個人の GitHub トークンを取得する必要があります。または、私の GitHub リポジトリをプルし、ローカルでイメージをビルドしてから、既存の ECR リポジトリにアップロードすることもできます。このシナリオでは、例を次のように変更できます。

     module "letsencrypt_lambda" { source = "../../" blank_name = "kvendingoldo-letsencrypt-lambda" tags = var.tags cron_schedule = var.letsencrypt_lambda_cron_schedule events = var.letsencrypt_lambda_events ecr_proxy_enabled = false ecr_image_uri = "<YOUR_ACCOUNT_ID>.dkr.ecr.us-east-2.amazonaws.com/aws_letsencrypt_lambda:<VERSION>" }


  4. コードの変更が完了したら、次のコマンドを実行して、OpenTofu バージョン スイッチャーtenvで OpenTofu をインストールします。

     $ tenv tofu install
  5. 最後に、次のコマンドを実行して、生成されたコードを適用します。

     $ tofu init $ tofu plan $ tofu apply
  6. コードが AWS にデプロイされ、イベントがトリガーされるまで待ちます。数分後、証明書マネージャー内に準備完了の証明書が表示されます。例:

    AWS Certificate Manager 内の AWS LetsEncrypt Lambda で発行された証明書のリスト。

  7. 今後、AWS は発行された証明書を ARN によってあらゆるサービスで使用できるようになります。

  8. 証明書を AWS サービス外で使用したり、そのコンテンツにアクセスしたりする必要がある場合は、 storeCertInSecretsManagerイベントオプションをtrueに設定します。この場合、Lambda が基本実行を完了すると、証明書は AWS Secrets Manager に保存されます。これにより、ユーザーはより柔軟に証明書のコンテンツを調べたり、EC2 から直接操作したりできるようになります。AWS Secrets Manager の詳細については、公式ガイドをお読みください。

    AWS Secrets Manager 内に保存されている発行済み証明書の例。


環境変数

名前

説明

可能な値

デフォルト値

必須

FORMATTER_TYPE

ログのフォーマッタタイプ

JSON | テキスト

文章

翻訳

MODE

アプリケーション モード。AWS 実行の場合はcloudモード、ローカル テストの場合はlocalモードを設定します。

クラウド | ローカル

LOG_LEVEL

ログレベル

パニック|致命的|エラー|警告|情報|デバッグ|トレース

警告する

警告する

AWS_REGION

デフォルトの AWS リージョン。AWS へのデプロイ後は自動的に設定されます。

<有効な AWS リージョン>

-

米国東部1

DOMAIN_NAME

証明書が発行または更新されるドメイン名

有効なドメイン名

-

hackernoon.referrs.meより

ACME_URL

プロダクションの LetsEncrypt URL は、 prodに設定されている場合、使用されます。そうでない場合は、 stage URL が使用されます。

プロダクション | ステージ

製品

製品

ACME_EMAIL

LetsEncrypt証明書にリンクされたメールアドレス

有効なメールアドレス

アレクサンダー・シャロフ@cloudexpress.app

アレクサンダー・シャロフ@cloudexpress.app

REIMPORT_THRESHOLD

証明書の有効期間 (TTL) がREIMPORT_THRESHOLDに等しい場合、証明書は更新されます。

任意の整数 > 0

10

10

STORE_CERT_IN_SECRETSMANAGER

trueの場合、Lambda は証明書を Certificate Manager と Secrets Manager の両方に保存します。

「真」 | 「偽」

"間違い"

"間違い"


LetsEncrypt Lambda ログを確認する方法

aws-letsencrypt-lambdaの作業範囲では、ログを確認する必要がある場合があります。これは非常に簡単に実行できます。

  1. AWS Cloudwatchにアクセスし、「ロググループ」をクリックします。
  2. OpenTofu コードで指定した名前のログ グループを検索します。たとえば、私の場合は/aws/lambda/kvendingoldo-letsencrypt-lambdaです。
  3. グループに移動し、リストから目的のストリームを選択し、ログを確認します。


AWS UI 経由で Lambda を手動でトリガーする方法

  1. OpenTofu で作成された Lambda 関数に移動します。「テスト」ボタンをクリックします。

    AWS LetsEncrypt Lambda: UI インターフェース

  2. Test Eventを入力してTestクリックします

    { "domainName": "<YOUR_VALID_DOMAIN>", "acmeUrl": <stage | prod>, "acmeEmail": "<ANY_VALID_EMAIL>", "reImportThreshold": 10, "issueType": "<default | force>", "storeCertInSecretsManager" : <true | false> }

    例1:

     { "domainName": "hackernoon.referrs.me", "acmeUrl": "prod", "acmeEmail": "[email protected]", "reImportThreshold": 10, "issueType": "default" }


  3. 実行が完了するまでお待ちください。実行ログは Cloudwatch で確認できます。通常、最初の発行には約 5 分かかります。

Lambdaをローカルでテストする方法

  1. https://github.com/kvendingoldo/aws-letsencrypt-lambdaリポジトリをラップトップにクローンします。

  2. 公式ガイドに従って AWS Cli 認証情報を設定します。

  3. 環境変数セクションを調べて、必要な変数の最小数を設定します。LetsEncrypt はACME_URL="prod"に対して 1 時間あたりの再試行回数を制限するため、テストにはACME_URL="stage"を使用することをお勧めします。環境変数の例:

     export AWS_REGION="us-east-2" export MODE=local export DOMAIN_NAME="hackernoon.referrs.me" export ACME_URL="stage" export ACME_EMAIL="[email protected]" export REIMPORT_THRESHOLD=10 export ISSUE_TYPE="default" export STORE_CERT_IN_SECRETSMANAGER="true"
  4. 次のコマンドを使用して、Lambda をローカルで実行します。

     go run main.go
  5. Lambda の実行が成功すると、次のログが表示されます。

     INFO[0000] Starting lambda execution ... INFO[0000] Lambda will use STAGING ACME URL; If you need to use PROD URL specify it via 'ACME_URL' or pass in event body INFO[0000] Certificate found, arn is arn:aws:acm:us-east-2:004867756392:certificate/72f872fd-e577-43f4-ae38-6833962630af. Trying to renew ... INFO[0000] Checking certificate for domain 'hackernoon.referrs.me' with arn 'arn:aws:acm:us-east-2:004867756392:certificate/72f872fd-e577-43f4-ae38-6833962630af' INFO[0000] Certificate status is 'ISSUED' INFO[0000] Certificate in use by [] INFO[0000] Certificate valid until 2024-08-31 13:50:49 +0000 UTC (89 days left) INFO[0000] Try to get certificate for hackernoon.referrs.me domain 2024/06/02 17:56:23 [INFO] acme: Registering account for [email protected] 2024/06/02 17:56:24 [INFO] [hackernoon.referrs.me, www.hackernoon.referrs.me] acme: Obtaining bundled SAN certificate 2024/06/02 17:56:25 [INFO] [hackernoon.referrs.me] AuthURL: https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/12603809394 2024/06/02 17:56:25 [INFO] [www.hackernoon.referrs.me] AuthURL: https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/12603809404 2024/06/02 17:56:25 [INFO] [hackernoon.referrs.me] acme: Could not find solver for: tls-alpn-01 2024/06/02 17:56:25 [INFO] [hackernoon.referrs.me] acme: Could not find solver for: http-01 2024/06/02 17:56:25 [INFO] [hackernoon.referrs.me] acme: use dns-01 solver 2024/06/02 17:56:25 [INFO] [www.hackernoon.referrs.me] acme: Could not find solver for: tls-alpn-01 2024/06/02 17:56:25 [INFO] [www.hackernoon.referrs.me] acme: Could not find solver for: http-01 2024/06/02 17:56:25 [INFO] [www.hackernoon.referrs.me] acme: use dns-01 solver 2024/06/02 17:56:25 [INFO] [hackernoon.referrs.me] acme: Preparing to solve DNS-01 2024/06/02 17:56:26 [INFO] Wait for route53 [timeout: 5m0s, interval: 4s] 2024/06/02 17:57:00 [INFO] [www.hackernoon.referrs.me] acme: Preparing to solve DNS-01 2024/06/02 17:57:00 [INFO] Wait for route53 [timeout: 5m0s, interval: 4s] 2024/06/02 17:57:30 [INFO] [hackernoon.referrs.me] acme: Trying to solve DNS-01 2024/06/02 17:57:30 [INFO] [hackernoon.referrs.me] acme: Checking DNS record propagation. [nameservers=109.122.99.130:53,109.122.99.129:53] 2024/06/02 17:57:34 [INFO] Wait for propagation [timeout: 5m0s, interval: 4s] 2024/06/02 17:57:46 [INFO] [hackernoon.referrs.me] The server validated our request 2024/06/02 17:57:46 [INFO] [www.hackernoon.referrs.me] acme: Trying to solve DNS-01 2024/06/02 17:57:46 [INFO] [www.hackernoon.referrs.me] acme: Checking DNS record propagation. [nameservers=109.122.99.130:53,109.122.99.129:53] 2024/06/02 17:57:50 [INFO] Wait for propagation [timeout: 5m0s, interval: 4s] 2024/06/02 17:58:30 [INFO] [www.hackernoon.referrs.me] The server validated our request 2024/06/02 17:58:30 [INFO] [hackernoon.referrs.me] acme: Cleaning DNS-01 challenge 2024/06/02 17:58:30 [INFO] Wait for route53 [timeout: 5m0s, interval: 4s] 2024/06/02 17:59:09 [INFO] [www.hackernoon.referrs.me] acme: Cleaning DNS-01 challenge 2024/06/02 17:59:09 [INFO] Wait for route53 [timeout: 5m0s, interval: 4s] 2024/06/02 17:59:43 [INFO] [hackernoon.referrs.me, www.hackernoon.referrs.me] acme: Validations succeeded; requesting certificates 2024/06/02 17:59:43 [INFO] Wait for certificate [timeout: 30s, interval: 500ms] 2024/06/02 17:59:45 [INFO] [hackernoon.referrs.me] Server responded with a certificate. INFO[0203] Certificate has been successfully imported. Arn is arn:aws:acm:us-east-2:004867756392:certificate/72f872fd-e577-43f4-ae38-6833962630af INFO[0204] Secret updated successfully. SecretId: arn:aws:secretsmanager:us-east-2:004867756392:secret:hackernoon.referrs.me-NioT77 INFO[0204] Lambda has been completed
  6. 以上です。今後、AWS は発行された証明書を AWS Secrets Manager から取得することで、ARN によるあらゆるサービスや物理的に必要なその他の場所で使用できるようになります。


AWSで4年以上使用した実務経験

私はほぼ 4 年間、本番環境で Lambda 関数を使用しています。長年にわたり、初期実装のさまざまな側面が変化してきました。

  1. 以前、AWS は Lambda ソースとして ECR 以外のレジストリを使用することを禁止していました。これは変更されていませんが、AWS は GitHub、DockerHub、およびいくつかの追加レジストリ用の ECR プロキシを追加しました。この機能がなければ、Lambda イメージを手動で個人の ECR にプッシュし、Terraform コードでイメージの URL を置き換える必要がありました。現在、OpenTofu コードは ECR プロキシを介してこれを自動的に実行します。

  2. 当初はhttp-01tls-alpn-01など、さまざまなチャレンジを導入することを検討しましたが、4年間誰もそれについて質問しませんでした。これは今でも GitHub の問題に存在しており、この機能が必要な場合は協力して作成できます。

  3. プロジェクトが始まった当初は、純粋な EC2 インスタンスで LetsEncrypt 証明書を利用したくなかったのですが、今ではそれが標準的な方法になっています。前に述べたように、特定の状況では、AWS CLI を使用して AWS Secrets Managed から証明書を取得できます。

  4. 私は長年にわたり、Go で新しいコードをたくさん書いてきました。そのため、私のリポジトリにある元の Lambda コードは、それほど洗練されていないことがわかります。このコードと、私の最新の Go プロジェクトであるtenv (Go で書かれた OpenTofu、Terraform、Terragrunt、および Atmos バージョン マネージャー) の間には大きな違いがありますが、いずれにしても、コードは引き続き一般的にサポートされているため、コードに変更を加えてもそれほど問題にはなりません。コードをよりエレガントにするために、大幅なリファクタリングを行うこともあります。

  5. 同じ Lambda が、さまざまなプロジェクトで何年も使用されています。さらに、私は DevOps プラットフォームcloudexpress.appの共同設立者でもあり、私たちのチームはAWS LetsEncrypt Lambdaを使用してすべてのクライアントの TLS 証明書を管理し、自動化プロセスを簡素化しています。


さて、数字についてお話ししましょう。 4年間にわたって、このプロジェクトは多くの人々を助け、数多くのオープンソースや30以上の商用プロジェクトで使用されてきました。Lambdaは2000以上の証明書を発行しており、これにとどまるつもりはありません。

結論

AWS LetsEncrypt Lambdaは、次のような場合に適したソリューションです。

  • 証明書の物理バージョンが必要であり、EC2 Nginx などの AWS ネイティブ以外のサービスからそれを使用します。
  • TLS 証明書の発行と更新プロセス (ログの確認、更新日の設定など) の管理を AWS Certificate Manager に依存したくない。
  • 証明書の有効期限が切れたとき、またはもうすぐ期限が切れるとき、LetsEncrypt から電子メール通知を受け取りたい。
  • Golang コードを変更してソリューションをカスタマイズしたい (たとえば、LetsEncrypt チャレンジの変更、Hashicorp Vault への証明書の保存など)。


これらのポイントの少なくとも 1 つが状況に当てはまる場合は、AWS Lambda をぜひご利用ください。また、開発に参加したい場合は、GitHub で新しい問題やプル リクエストをいつでも受け付けています。プロジェクト URL: https://github.com/kvendingoldo/aws-letsencrypt-lambda