Heutzutage ist es schwierig, sich Systeme mit öffentlichen API-Endpunkten ohne TLS-Zertifikatschutz vorzustellen. Es gibt mehrere Möglichkeiten, Zertifikate auszustellen:
Im Rahmen dieses Beitrags werde ich hauptsächlich kostenlose Zertifikate besprechen, die innerhalb von AWS verwendet werden können, aber nicht nur von AWS-Diensten. Es ist klar, dass die Verwendung von etwas anderem als AWS Certificate Manager keinen Sinn macht, wenn Sie ausschließlich verwaltete AWS-Dienste verwenden und keine strengen Sicherheitsanforderungen haben. AWS Certificate Manager bietet eine sehr bequeme und schnelle Methode zum Ausstellen von Zertifikaten über DNS- oder HTTP-Herausforderungen. Sie unterliegen jedoch grundlegenden AWS-Einschränkungen, wenn Sie diese Zertifikate außerhalb von AWS-Diensten (API Gateway, ALB, NLB usw.) verwenden müssen, z. B. bei einer EC2-Instanz mit Nginx, die eine physische Zertifikatsdatei benötigt. Darüber hinaus zeigt AWS Certificate Manager den Zertifikatsinhalt nicht an, selbst wenn Sie ihn anfordern.
An dieser Stelle ist es an der Zeit, Sie an LetsEncrypt zu erinnern, ein Tool, das häufiger verwendet wird als Certificate Manager – zumindest, weil es nicht von der Cloud abhängig ist. Leider sind in AWS keine integrierten LetsEncrypt-Techniken zur Zertifikatsausstellung verfügbar. Es ist möglich, das Certbot-Tool für Ihre EC2- oder ECS-Dienste zu verwenden, aber in diesem Szenario müssen Sie überlegen, wie Sie den Erneuerungsprozess konfigurieren. Ich möchte auch nicht verschiedene Strategien kombinieren, da ich denke, dass es besser ist, für alles ein einziges Verfahren zu haben, da dies die gesamte Systemkomplexität reduziert.
Unter Berücksichtigung dieser Tatsache habe ich eine Lambda-Funktion erstellt, die LetsEncrypt-Zertifikate automatisch ausstellt und erneuert, ohne dass eine komplexe Konfiguration erforderlich ist. Das Zertifikat kann nach der ersten Zertifikatsausstellung zusammen mit AWS Certificate Manager-Zertifikaten in jedem AWS-Dienst mit ARN verwendet werden. Darüber hinaus können Sie eine physische Zertifikatsversion verwenden, die im AWS Secrets Manager an einem beliebigen Ort aufbewahrt wird, sei es eine EC2-Instanz mit Nginx oder ein anderer Ort.
In diesem Artikel gehe ich davon aus, dass Ihre DNS-Zone von AWS Route53 verwaltet wird.
Die in diesem Artikel beschriebene Lambda-Funktion ist in Go v1.22 geschrieben. Alle Ergebnisressourcen wie DNS-Einträge, Geheimnisse oder Zertifikate werden von der Amazon IAM-Rolle gesteuert, die standardmäßig über Terraform-Code erstellt wird. Die Abfolge der Lambda-Aktionen ist wie folgt:
aws_cloudwatch_event_target
erfolgt. Ereignisbeispiel: { "domainName": "hackernoon.referrs.me", "acmeUrl": "prod", "acmeEmail": "[email protected]", "reImportThreshold": 10, "issueType": "default", "storeCertInSecretsManager" : true }
DNS-01
Challenge, wenn die Anzahl der Tage bis zum Ablaufdatum kleiner ist als der reImportThreshold
. In diesem Schritt erstellt Lambda einen TXT
Eintrag, der den Domänennamen mit der AWS Route53-Zone abgleicht, und wartet, bis Ihr Zertifikat bereit ist.storeCertInSecretsManager
wahr ist.
Der Code
Das Lambda ist auf Go 1.22 geschrieben. Die Verwendung von so wenig Bibliotheken wie möglich hat mir geholfen, mein Ziel zu erreichen, den Code trocken zu halten. Die vollständige Liste der erforderlichen Go-Bibliotheken:
URL | Beschreibung |
---|---|
Bibliotheken, Beispiele und Tools, die Go-Entwicklern bei der Entwicklung von AWS-Lambda-Funktionen helfen. | |
AWS SDK für die Programmiersprache Go. | |
LetsEncrypt/ACME-Client und -Bibliothek. | |
Sinnvoller Umgang mit Nullwerten. | |
Strukturiertes, steckbares Protokollieren für Go. |
Docker-Image
Ich habe gcr.io/distroless/static:nonroot als grundlegendes Docker-Image verwendet. Für Go-Anwendungen, die keine libc benötigen, ist dieses Image perfekt. Es ist nicht komplett scratch
und enthält Folgendes:
Build-Prozess
Bei großen Softwareprojekten kann die Überwachung des Build-Prozesses zu einer mühsamen und zeitraubenden Aufgabe werden. Makefiles können dabei helfen, diesen Prozess zu automatisieren und zu rationalisieren und so sicherzustellen, dass Ihr Projekt effizient und konsistent erstellt wird. Aus diesem Grund verwende ich für alle meine Golang-Projekte am liebsten Makefile. Die Datei ist einfach:
##@ 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 ./...
Auf der CICD-Seite habe ich das typische Setup für Go-Anwendungen verwendet
GitHub Actions als kontinuierliche Integration.
ghcr.io als Docker-Registry. Im Vergleich zu DockerHub bietet dieses zwei wichtige Funktionen, die es zu meiner bevorzugten Verwendung machen:
kvendingoldo/semver-action : mein GitHub Actions-Plugin für automatische Versionierung. Es ist eine GitHub-Aktion, die SemVer -kompatible Tags für Repository-Commits generiert. Die Aktion kann Versionen verwalten, GitHub-Releases generieren und Release-Zweige innerhalb des Repositorys steuern. Sie funktioniert wunderbar mit Einzel- und Monorepos.
Automatische Änderungsprotokollgenerierung. Ich mag Änderungsprotokolle! Im Zusammenhang mit anderen OpenSource-Projekten, die ich betreue (z. B. https://github.com/tofuutils/tenv , https://github.com/tofuutils/tofuenv usw.), hat mein Team erkannt, wie wichtig es ist, Benutzer über Änderungen zu informieren.
golangci-lint . Meiner Meinung nach sollte der gesamte Code von einem statischen Codeanalysator überprüft werden. SonarQube kann nicht für alle Projekte eingerichtet werden, jedoch ist golangci meiner Meinung nach für kleine bis mittlere Go-Projekte ausreichend.
Der auf dieser Seite besprochene Code ist für Terraform und OpenTofu derselbe, aber ab Terraform v1.6 hat Hashicorp die Terraform-Lizenz auf Business Source License (BSL) v1.1 geändert. Wenn Sie auf der Grundlage von Terraform etwas Kommerzielles entwickeln, wechseln Sie so schnell wie möglich zu OpenTofu.
Wenn Sie mehrere Versionen von OpenTofu oder Terraform verwalten müssen, verwenden Sie die
Weitere Terraform-/OpenTofu-Beispiele finden Sie im Ordner „Beispiele“ im Git-Repository .
Um mit AWS LetsEncrypt Lambda über OpenTofu zu arbeiten, müssen Sie die folgenden Schritte ausführen:
Fügen Sie Ihrem OpenTofu-/Terraform-Code ein Modul hinzu
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 }
Angeben von Variablen
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 } ] }
Achten Sie auf die Variablen ecr_proxy_username
und ecr_proxy_access_token
. Standardmäßig kann AWS Lambda keine Bilder aus anderen Quellen als AWS ECR abrufen. Glücklicherweise hat das AWS-Team einen ECR-Proxy-Cache erstellt, der Bilder aus öffentlich verfügbaren Registern wie DockerHub oder GHCR abrufen und in ECR speichern kann. Trotz dieser Möglichkeit erlaubt AWS nicht, Bilder ohne Token abzurufen, auch nicht aus offenen öffentlichen Repositories. Daher müssen Sie ein persönliches GitHub-Token erwerben, um Zugriff auf vorgefertigte Docker-Bilder zu erhalten. Alternativ können Sie mein GitHub-Repository abrufen, das Bild lokal erstellen und es dann in Ihr bereits vorhandenes ECR-Repository hochladen. In diesem Szenario kann das Beispiel wie folgt geändert werden:
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>" }
Wenn Sie mit der Codeänderung fertig sind, führen Sie den folgenden Befehl aus, um OpenTofu mit dem OpenTofu-Versionsumschalter tenv zu installieren:
$ tenv tofu install
Führen Sie abschließend die folgenden Befehle aus, um den erstellten Code anzuwenden:
$ tofu init $ tofu plan $ tofu apply
Warten Sie, bis der Code in AWS bereitgestellt und Ereignisse ausgelöst werden. Nach einigen Minuten werden im Zertifikatsmanager fertige Zertifikate angezeigt. Beispiel:
Ab sofort kann AWS das ausgestellte Zertifikat für alle Dienste per ARN verwenden.
Wenn Sie das Zertifikat außerhalb von AWS-Diensten verwenden müssen oder Zugriff auf dessen Inhalt haben möchten, setzen Sie die Ereignisoption storeCertInSecretsManager
auf true
. In diesem Fall wird das Zertifikat in AWS Secrets Manager gespeichert, wenn Lambda die grundlegende Ausführung abgeschlossen hat. Dies bietet Benutzern mehr Flexibilität: Sie können den Inhalt des Zertifikats prüfen, direkt von EC2 aus damit arbeiten usw. Weitere Informationen zu AWS Secrets Manager finden Sie im offiziellen Handbuch.
Name | Beschreibung | Mögliche Werte | Standardwert | Beispiel | Erforderlich |
---|---|---|---|---|---|
| Formatierungstyp für Protokolle | TEXT | TEXT | JSON | ❌ |
| Anwendungsmodus. Stellen Sie | Wolke | lokal | Wolke | Wolke | ✅ |
| Protokollierungsebene | Panik|Schwerwiegend|Fehler|Warnung|Info|Debug|Trace | warnen | warnen | ❌ |
| Standard-AWS-Region. Nach der Bereitstellung in AWS werden die Einstellungen automatisch vorgenommen. | <jede gültige AWS-Region> | - | USA-Ost-1 | ✅ |
| Domänenname, für den das Zertifikat ausgestellt oder erneuert wird | jeder gültige Domänenname | - | hackernoon. verweist.me | ✅ |
| Die Produktions-LetsEncrypt-URL wird genutzt, wenn sie auf | Produktion | Bühne | Produkt | Produkt | ✅ |
| Mit dem LetsEncrypt-Zertifikat verknüpfte E-Mail-Adresse | jede gültige E-Mail | ✅ | ||
| Das Zertifikat wird erneuert, wenn seine Gültigkeitsdauer (TTL) | jede Ganzzahl > 0 | 10 | 10 | ✅ |
| Wenn | „wahr“ | „falsch“ | "FALSCH" | "FALSCH" | ❌ |
Im Rahmen der Arbeit mit aws-letsencrypt-lambda möchten Sie möglicherweise gelegentlich die Protokolle überprüfen. Dies ist ganz einfach:
/aws/lambda/kvendingoldo-letsencrypt-lambda
Gehen Sie zur Lambda-Funktion, die über OpenTofu erstellt wurde. Klicken Sie auf die Schaltfläche „Tests“.
Füllen Sie Test Event
aus und klicken Sie auf Test
“.
{ "domainName": "<YOUR_VALID_DOMAIN>", "acmeUrl": <stage | prod>, "acmeEmail": "<ANY_VALID_EMAIL>", "reImportThreshold": 10, "issueType": "<default | force>", "storeCertInSecretsManager" : <true | false> }
Beispiel 1:
{ "domainName": "hackernoon.referrs.me", "acmeUrl": "prod", "acmeEmail": "[email protected]", "reImportThreshold": 10, "issueType": "default" }
Warten Sie, bis die Ausführung abgeschlossen ist. Das Ausführungsprotokoll ist in Cloudwatch verfügbar. Normalerweise dauert das erste Problem etwa 5 Minuten.
Klonen Sie das Repository https://github.com/kvendingoldo/aws-letsencrypt-lambda auf Ihren Laptop
Konfigurieren Sie AWS CLI-Anmeldeinformationen über den offiziellen Leitfaden .
Untersuchen Sie den Abschnitt mit den Umgebungsvariablen und legen Sie die erforderliche Mindestanzahl an Variablen fest. Da LetsEncrypt die Anzahl der Wiederholungsversuche pro Stunde für ACME_URL="prod"
begrenzt, empfehle ich die Verwendung von ACME_URL="stage"
zum Testen. Beispiel für Umgebungsvariablen:
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"
Führen Sie das Lambda lokal mit dem folgenden Befehl aus:
go run main.go
Nach der erfolgreichen Ausführung von Lambda wird das folgende Protokoll angezeigt.
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
Das ist alles. Ab sofort kann AWS das ausgestellte Zertifikat bei allen Diensten per ARN oder an anderen Standorten verwenden, wo es physisch erforderlich ist, indem es es vom AWS Secrets Manager erhält.
Ich verwende die Lambda-Funktion seit fast vier Jahren in der Produktion. Im Laufe der Jahre haben sich verschiedene Aspekte der ursprünglichen Implementierung geändert:
Bisher verbot AWS die Verwendung von Nicht-ECR-Registern als Lambda-Quellen. Daran hat sich nichts geändert, allerdings hat AWS einen ECR-Proxy für GitHub, DockerHub und einige weitere Register hinzugefügt. Ohne diese Funktion mussten wir Lambda-Images manuell in unser persönliches ECR übertragen und die URL zum Image im Terraform-Code ersetzen. Jetzt erledigt der OpenTofu-Code dies automatisch über den ECR-Proxy.
Am Anfang habe ich überlegt, verschiedene Herausforderungen wie http-01
oder tls-alpn-01
einzuführen, aber vier Jahre lang hat mich niemand danach gefragt. Es ist immer noch bei GitHub Issues vorhanden , und wenn diese Funktion benötigt wird, können wir zusammenarbeiten, um sie zu erstellen.
Als das Projekt ursprünglich begann, wollte ich bei reinen EC2-Instanzen keine LetsEncrypt-Zertifikate verwenden, aber heutzutage ist das Standardpraxis. Wie ich bereits sagte, kann in bestimmten Situationen ein Zertifikat mithilfe der AWS-Befehlszeile von AWS Secrets Managed abgerufen werden.
Ich habe im Laufe der Jahre viel neuen Go-Code geschrieben, daher kann ich sagen, dass der ursprüngliche Lambda-Code in meinem Repository nicht so ausgefallen ist, wie er sein könnte. Es gibt einen erheblichen Unterschied zwischen ihm und meinem neuesten Go-Projekt, tenv (OpenTofu, Terraform, Terragrunt und Atmos Version Manager, geschrieben in Go), aber in jedem Fall wird der Code im Allgemeinen immer noch unterstützt, sodass Änderungen daran nicht allzu problematisch sein werden. Gelegentlich nehme ich erhebliche Refactorings vor, um den Code eleganter zu gestalten.
Das gleiche Lambda wird seit Jahren in verschiedenen Projekten verwendet. Darüber hinaus bin ich Mitbegründer der DevOps-Plattform cloudexpress.app , wo unser Team TLS-Zertifikate für alle unsere Kunden verwaltet und dabei das AWS LetsEncrypt Lambda verwendet, um die Automatisierungsprozesse zu vereinfachen.
Kommen wir nun zu den Zahlen. Über einen Zeitraum von 4 Jahren hat dieses Projekt vielen Menschen geholfen und wurde in zahlreichen OpenSource- und über 30 kommerziellen Projekten eingesetzt. Lambda stellt mehr als 2000 Zertifikate aus und möchte dabei nicht stehen bleiben.
AWS LetsEncrypt Lambda ist für Sie eine geeignete Lösung, wenn
Wenn Sie festgestellt haben, dass mindestens einer dieser Punkte auf Ihre Situation zutrifft, können Sie gerne AWS Lambda verwenden. Wenn Sie an der Entwicklung teilnehmen möchten, bin ich bei GitHub immer offen für neue Probleme und Pull Requests. Projekt-URL: https://github.com/kvendingoldo/aws-letsencrypt-lambda .