Repository navigation
Add tutorial for AWS Certificate Manager ACME endpoints #2360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
wallrj
wants to merge
1
commit into
cert-manager:master
Choose a base branch
from
wallrj:aws-acm-acme-docs
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1587,3 +1587,6 @@ pujitha | |
| requestmanager | ||
| repoints | ||
| textproto | ||
| secp | ||
| prevalidation | ||
| Prevalidation | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,289 @@ | ||
| --- | ||
| title: Issue public certificates from AWS Certificate Manager (ACM) using ACME | ||
| description: | | ||
| Learn how to configure cert-manager to request publicly trusted | ||
| certificates from an AWS Certificate Manager (ACM) ACME endpoint, | ||
| using External Account Binding and pre-approved domain validation. | ||
| --- | ||
|
|
||
| In this tutorial you will learn how to use cert-manager with an | ||
| [AWS Certificate Manager (ACM) ACME endpoint](https://docs.aws.amazon.com/acm/latest/userguide/acm-acme.html). | ||
|
|
||
| ACM can run a managed, RFC 8555 compliant ACME server for your AWS account. | ||
| It issues publicly trusted certificates from Amazon Trust Services | ||
| to workloads that run outside the AWS managed services, | ||
| such as Kubernetes clusters, on-premises servers, and hybrid environments. | ||
| cert-manager talks to it like any other ACME server. | ||
|
|
||
| > ⚠️ This tutorial has not yet been verified against a live ACM ACME endpoint. | ||
| > The steps are derived from the AWS documentation and from the cert-manager source code. | ||
| > If you try it, please [report your results](https://github.com/cert-manager/website/issues). | ||
|
|
||
| ## How ACM ACME differs from Let's Encrypt | ||
|
|
||
| Most public ACME servers, such as Let's Encrypt, require the client to prove | ||
| control of each domain every time it requests a certificate. | ||
| cert-manager does that by solving HTTP-01 or DNS-01 challenges. | ||
|
|
||
| ACM ACME endpoints work differently: | ||
|
|
||
| - **Domains are approved in advance.** An AWS administrator creates an | ||
| *ACME domain validation* for each domain, backed by a persistent CNAME record. | ||
| The ACME server then returns authorizations that are already `valid`, | ||
| so cert-manager never needs to create a Challenge resource. | ||
| - **Every ACME account must use External Account Binding (EAB).** | ||
| Each EAB is bound to an IAM role which ACM assumes when it issues or revokes a certificate. | ||
| You control who can issue certificates with IAM policies and Service Control Policies, | ||
| and every issuance is logged in CloudTrail. | ||
| - **Certificates are valid for 45 days.** | ||
| cert-manager renews them automatically using its default renewal settings. | ||
| - **Issuance can take up to two minutes.** | ||
| cert-manager waits for the ACME server to finish, so no extra configuration is needed. | ||
|
|
||
| Use ACM ACME when you want publicly trusted certificates for Kubernetes workloads | ||
| and you want AWS to govern which clusters may issue certificates for which domains. | ||
| If you only need certificates for AWS managed services such as | ||
| Elastic Load Balancing or CloudFront, use ACM directly instead. | ||
| If you need a private CA, use the | ||
| [aws-privateca-issuer](https://github.com/cert-manager/aws-privateca-issuer) instead. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - An AWS account and the [AWS CLI](https://aws.amazon.com/cli/) configured with permissions to manage ACM and IAM. | ||
| - A public DNS zone that you control. The examples use a Route 53 hosted zone, | ||
| which lets ACM create the validation CNAME record for you. | ||
| - A Kubernetes cluster with [cert-manager installed](../../installation/README.md). | ||
|
|
||
| ## Step 1: Create an ACME endpoint | ||
|
|
||
| Create an endpoint with pre-approved authorization. | ||
| `--contact REQUIRED` makes the endpoint reject ACME accounts that do not supply an email address. | ||
| `AllowedKeyAlgorithms` restricts the key types that the endpoint will sign. | ||
|
|
||
| ```bash | ||
| aws acm create-acme-endpoint \ | ||
| --authorization-behavior PRE_APPROVED \ | ||
| --contact REQUIRED \ | ||
| --certificate-authority '{ | ||
| "PublicCertificateAuthority": { | ||
| "AllowedKeyAlgorithms": ["RSA_2048", "EC_prime256v1", "EC_secp384r1"] | ||
| } | ||
| }' | ||
| ``` | ||
|
|
||
| Note the `AcmeEndpointArn` in the output. | ||
| Then retrieve the ACME directory URL of the endpoint: | ||
|
|
||
| ```bash | ||
| aws acm describe-acme-endpoint \ | ||
| --acme-endpoint-arn arn:aws:acm:${AWS_REGION}:${AWS_ACCOUNT_ID}:acme-endpoint/${ACME_ENDPOINT_ID} \ | ||
| --query 'EndpointUrl' --output text | ||
| ``` | ||
|
|
||
| The URL has the form `https://acm-acme-enroll.<region>.api.aws/<endpoint-id>/directory`. | ||
| You will use it as the `server` field of the cert-manager Issuer. | ||
|
|
||
| ## Step 2: Pre-approve your domain | ||
|
|
||
| Create an ACME domain validation for the domain, or parent domain, of the certificates you want to issue. | ||
| The `DomainScope` controls whether the exact domain, its subdomains, and wildcards are allowed. | ||
| If you supply a Route 53 `HostedZoneId`, ACM creates the validation CNAME record for you. | ||
| Otherwise, read the CNAME name and value from the output and create the record yourself. | ||
|
|
||
| ```bash | ||
| aws acm create-acme-domain-validation \ | ||
| --acme-endpoint-arn arn:aws:acm:${AWS_REGION}:${AWS_ACCOUNT_ID}:acme-endpoint/${ACME_ENDPOINT_ID} \ | ||
| --domain-name example.com \ | ||
| --prevalidation-options '{ | ||
| "DnsPrevalidation": { | ||
| "DomainScope": { | ||
| "ExactDomain": "ENABLED", | ||
| "Subdomains": "ENABLED", | ||
| "Wildcards": "DISABLED" | ||
| }, | ||
| "HostedZoneId": "'"${HOSTED_ZONE_ID}"'" | ||
| } | ||
| }' | ||
| ``` | ||
|
|
||
| Wait until the validation status is `VALID`. This can take up to 72 hours, | ||
| but is usually much faster when ACM manages the Route 53 record. | ||
|
|
||
| ```bash | ||
| aws acm describe-acme-domain-validation \ | ||
| --acme-domain-validation-arn ${ACME_DOMAIN_VALIDATION_ARN} \ | ||
| --query 'Status' --output text | ||
| ``` | ||
|
|
||
| > ⚠️ The CNAME record must stay in place for as long as you want to issue certificates. | ||
| > If it is removed, new Orders fail with the ACME error `urn:ietf:params:acme:error:dns`. | ||
|
|
||
| ## Step 3: Create an IAM role and EAB credentials | ||
|
|
||
| ACM assumes an IAM role on behalf of the ACME account each time it issues a certificate. | ||
| Create a role with the following trust policy: | ||
|
|
||
| ```json | ||
| { | ||
| "Version": "2012-10-17", | ||
| "Statement": [ | ||
| { | ||
| "Effect": "Allow", | ||
| "Principal": { "Service": "acm-acme.amazonaws.com" }, | ||
| "Action": ["sts:AssumeRole", "sts:TagSession", "sts:SetSourceIdentity"], | ||
| "Condition": { | ||
| "StringLikeIfExists": { "sts:SourceIdentity": "acm-acme-*" } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Attach a permissions policy that allows `acm:RequestCertificate`. | ||
| Add `acm:RevokeCertificate` only if you want this account to be able to revoke certificates. | ||
|
|
||
| Then create an External Account Binding bound to that role. | ||
| The optional expiration limits how long the EAB can be used to register new ACME accounts. | ||
| It does not affect accounts that have already registered. | ||
|
|
||
| ```bash | ||
| aws acm create-acme-external-account-binding \ | ||
| --acme-endpoint-arn arn:aws:acm:${AWS_REGION}:${AWS_ACCOUNT_ID}:acme-endpoint/${ACME_ENDPOINT_ID} \ | ||
| --role-arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/AcmeIssuanceRole \ | ||
| --expiration '{"Value": 7, "Type": "DAYS"}' | ||
| ``` | ||
|
|
||
| Retrieve the credentials: | ||
|
|
||
| ```bash | ||
| aws acm get-acme-external-account-binding-credentials \ | ||
| --acme-external-account-binding-arn ${ACME_EAB_ARN} | ||
| ``` | ||
|
|
||
| The output contains a `KeyId` and a `MacKey`. | ||
| `KeyId` goes in the `keyID` field of the Issuer. | ||
| `MacKey` goes in a Secret. | ||
|
|
||
| Create one EAB per cluster, so that you can revoke a single cluster's access without affecting the others. | ||
|
|
||
| ## Step 4: Store the MAC key in a Secret | ||
|
|
||
| cert-manager expects the MAC key in a Secret, encoded as a base64 URL string. | ||
| See [External Account Bindings](../../configuration/acme/README.md#external-account-bindings) | ||
| for details of the expected encoding. | ||
|
|
||
| ```bash | ||
| kubectl create secret generic acm-acme-eab \ | ||
| --namespace cert-manager \ | ||
| --from-literal=secret='<MacKey from the previous step>' | ||
| ``` | ||
|
|
||
| ## Step 5: Create a ClusterIssuer | ||
|
|
||
| ```yaml | ||
| apiVersion: cert-manager.io/v1 | ||
| kind: ClusterIssuer | ||
| metadata: | ||
| name: acm-acme | ||
| spec: | ||
| acme: | ||
| # The EndpointUrl from Step 1 | ||
| server: https://acm-acme-enroll.<region>.api.aws/<endpoint-id>/directory | ||
| # Required when the endpoint was created with --contact REQUIRED | ||
| email: platform-team@example.com | ||
| privateKeySecretRef: | ||
| name: acm-acme-account-key | ||
| externalAccountBinding: | ||
| # The KeyId from Step 3 | ||
| keyID: <KeyId> | ||
| keySecretRef: | ||
| name: acm-acme-eab | ||
| key: secret | ||
| # The ACM ACME endpoint pre-approves domains, so cert-manager never | ||
| # needs to solve a challenge. No solvers are required. | ||
| solvers: [] | ||
| ``` | ||
|
|
||
| ```bash | ||
| kubectl apply -f clusterissuer.yaml | ||
| kubectl describe clusterissuer acm-acme | ||
| ``` | ||
|
|
||
| The ClusterIssuer is `Ready` once cert-manager has registered the ACME account. | ||
| If registration fails, check that the `keyID` and Secret match the EAB credentials, | ||
| that the EAB has not expired, and that `email` is set. | ||
|
|
||
| ## Step 6: Request a certificate | ||
|
|
||
| ```yaml | ||
| apiVersion: cert-manager.io/v1 | ||
| kind: Certificate | ||
| metadata: | ||
| name: www-example-com | ||
| namespace: default | ||
| spec: | ||
| secretName: www-example-com-tls | ||
| dnsNames: | ||
| - www.example.com | ||
| issuerRef: | ||
| name: acm-acme | ||
| kind: ClusterIssuer | ||
| privateKey: | ||
| # Must be one of the AllowedKeyAlgorithms of the endpoint. | ||
| # ECDSA P-256 is the default in the ACM console; cert-manager | ||
| # defaults to RSA 2048 if privateKey is omitted. | ||
| algorithm: ECDSA | ||
| size: 256 | ||
| ``` | ||
|
|
||
| ```bash | ||
| kubectl apply -f certificate.yaml | ||
| kubectl wait --for=condition=Ready certificate/www-example-com --timeout=5m | ||
| ``` | ||
|
|
||
| Because the domain is pre-approved, the Order goes straight from `pending` to `ready` | ||
| and cert-manager finalizes it without creating any Challenge resources. | ||
| Issuance can take up to two minutes. | ||
|
|
||
| You can confirm that ACM issued the certificate by listing certificates with the `ACME` origin: | ||
|
|
||
| ```bash | ||
| aws acm list-certificates --certificate-key-pair-origins ACME | ||
| ``` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| See [Troubleshooting Issuing ACME Certificates](../../troubleshooting/acme.md) for general guidance. | ||
| These problems are specific to ACM ACME endpoints: | ||
|
|
||
| - **The Order fails with `urn:ietf:params:acme:error:dns`.** | ||
| The validation CNAME record has been removed or changed. Recreate it. | ||
| - **The Order fails with `urn:ietf:params:acme:error:unauthorized` or a similar rejection.** | ||
| The requested domain is not covered by a `VALID` domain validation on this endpoint, | ||
| or the certificate key algorithm is not in `AllowedKeyAlgorithms`. | ||
| - **The Order creates a Challenge, or the Order has the event `Failed to determine a valid solver configuration`.** | ||
| The ACME server returned a `pending` authorization for one of the domains, | ||
| which means that domain is not pre-approved. Add a domain validation for it, or fix the `DomainScope`. | ||
| - **The ClusterIssuer is not `Ready` and reports an account registration error.** | ||
| Check the EAB `KeyId`, the Secret contents, the EAB expiration, and that `email` is set. | ||
| - **Issuance fails with an access denied error after the Order is finalized.** | ||
| The IAM role trust policy or permissions are wrong, or a Service Control Policy blocks `acm:RequestCertificate`. | ||
| Look for the `IssueCertificate` event in CloudTrail. | ||
|
|
||
| ## Limitations | ||
|
|
||
| - ACM does not renew ACME certificates. cert-manager handles renewal. | ||
| - ACME certificates cannot be attached to Elastic Load Balancing, CloudFront, or API Gateway. | ||
| - Revocation is only possible through the ACME protocol. cert-manager does not revoke certificates. | ||
| - A certificate can contain at most 100 domain names. | ||
| - ACM endpoints are regional. Create the endpoint in the Region closest to your cluster, | ||
| or use [AWS PrivateLink](https://docs.aws.amazon.com/acm/latest/userguide/acm-acme-privatelink.html) | ||
| to reach it without traversing the public internet. | ||
|
|
||
| ## Further reading | ||
|
|
||
| - [AWS: ACME certificate automation in ACM](https://docs.aws.amazon.com/acm/latest/userguide/acm-acme.html) | ||
| - [AWS: ACME domain validation](https://docs.aws.amazon.com/acm/latest/userguide/acm-acme-domain-validation.html) | ||
| - [AWS: External Account Binding](https://docs.aws.amazon.com/acm/latest/userguide/acm-acme-eab.html) | ||
| - [AWS: Troubleshooting ACME](https://docs.aws.amazon.com/acm/latest/userguide/troubleshooting-acme.html) | ||
| - [cert-manager: ACME issuer configuration](../../configuration/acme/README.md) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The slug here is not , it's a different uuid