Cert-Manager and Ambassador Edge Stack
Note: The Ambassador Edge Stack can issue and manage certificate with the ACME HTTP-01 challenge.
cert-manager is still required for DNS-01 challenges for wildcard domains and when using Ambassador OSS.
Creating and managing certificates in Kubernetes is made simple with Jetstack's cert-manager. Cert-manager will automatically create and renew TLS certificates and store them in Kubernetes secrets for easy use in a cluster. Ambassador will automatically watch for secret changes and reload certificates upon renewal.
Note: Ambassador Edge Stack will automatically create and renew TLS certificates with the HTTP-01 challenge. You should use cert-manager if you need support for the DNS-01 challenge and/or wildcard certificates.
Install Cert-Manager
There are many different ways to install cert-manager. For simplicity, we will use Helm.
- Install cert-manager
kubectl create ns cert-managerkubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.11.1/cert-manager-no-webhook.yaml
Note: The resource validation webhook is not mandatory, but if implemented, requires additional configuration.
Issuing Certificates
cert-manager issues certificates from a CA such as Let's Encrypt. It does this using the ACME protocol which supports various challenge mechanisms for verifying ownership of the domain.
Issuer
An Issuer
or ClusterIssuer
identifies which Certificate Authority cert-manager will use to issue a certificate. Issuer
is a namespaced resource allowing you to use different CAs in each namespace, a ClusterIssuer
is used to issue certificates in any namespace. Configuration depends on which ACME challenge you are using.
Certificate
A Certificate is a namespaced resource that references an Issuer
or ClusterIssuer
for issuing certificates. Certificate
s define the DNS name(s) a key and certificate should be issued for, as well as the secret to store those files (e.g. ambassador-certs
). Configuration depends on which ACME challenge you are using.
By duplicating issuers, certificates, and secrets one can support multiple domains with SNI.
Challenge
cert-manager supports two kinds of ACME challenges that verify domain ownership in different ways: HTTP-01 and DNS-01.
DNS-01 Challenge
The DNS-01 challenge verifies domain ownership by proving you have control over its DNS records. Issuer configuration will depend on your DNS provider. This example uses AWS Route53.
Create the IAM policy specified in the cert-manager AWS Route53 documentation.
Note the
accessKeyID
and create a secret namedprod-route53-credentials-secret
holding thesecret-access-key
.Create and apply a
ClusterIssuer
:---apiVersion: cert-manager.io/v1alpha2kind: ClusterIssuermetadata:name: letsencrypt-prodnamespace: defaultspec:acme:email: example@example.comserver: https://acme-v02.api.letsencrypt.org/directoryprivateKeySecretRef:name: letsencrypt-proddns01:providers:- name: route53route53:region: us-east-1accessKeyID: {SECRET_KEY}secretAccessKeySecretRef:name: prod-route53-credentials-secretkey: secret-access-keyCreate and apply a certificate:
```yaml---apiVersion: cert-manager.io/v1alpha2kind: Certificatemetadata:name: ambassador-certsnamespace: defaultspec:secretName: ambassador-certsissuerRef:name: letsencrypt-prodkind: ClusterIssuercommonName: example.comdnsNames:- example.comacme:config:- dns01:provider: route53domains:- example.com```
Verify the secret is created
$ kubectl get secretsNAME TYPE DATA AGEambassador-certs kubernetes.io/tls 2 1hambassador-token-846d5 kubernetes.io/service-account-token 3 2hdefault-token-4l772 kubernetes.io/service-account-token 3 2h
HTTP-01 Challenge
The HTTP-01 challenge verifies ownership of the domain by sending a request for a specific file on that domain. cert-manager accomplishes this by sending a request to a temporary pod with the prefix /.well-known/acme-challenge/
. To perform this challenge:
Create a
ClusterIssuer
:---apiVersion: cert-manager.io/v1alpha2kind: ClusterIssuermetadata:name: letsencrypt-prodspec:acme:email: example@example.comserver: https://acme-v02.api.letsencrypt.org/directoryprivateKeySecretRef:name: letsencrypt-prodhttp01:serviceType: ClusterIPsolvers:- http01:ingress:class: nginxselector: {}Configure a
Certificate
to use thisClusterIssuer
:---apiVersion: cert-manager.io/v1alpha2kind: Certificatemetadata:name: ambassador-certs# cert-manager will put the resulting Secret in the same Kubernetes namespace# as the Certificate. Therefore you should put this Certificate in the same namespace as Ambassador.# eg. if you deploy ambassador to ambassador namespace, you need to change to namespace: ambassadornamespace: defaultspec:# naming the secret name certificate ambassador-certs is important because# ambassador just look for this particular namesecretName: ambassador-certsissuerRef:name: letsencrypt-prodkind: ClusterIssuerdnsNames:- example.comacme:config:- http01:ingressClass: nginxdomains:- example.comApply both the
ClusterIssuer
andCertificate
After applying both of these YAML manifests, you will notice that cert-manager has spun up a temporary pod named
cm-acme-http-solver-xxxx
but no certificate has been issued. Check the cert-manager logs and you will see a log message that looks like this:$ kubectl logs cert-manager-756d6d885d-v7gmg...Preparing certificate default/ambassador-certs with issuerCalling GetOrderCalling GetAuthorizationCalling HTTP01ChallengeResponseCleaning up old/expired challenges for Certificate default/ambassador-certsCalling GetChallengewrong status code '404'Looking up Ingresses for selector certmanager.k8s.io/acme-http-domain=161156668,certmanager.k8s.io/acme-http-token=1100680922Error preparing issuer for certificate default/ambassador-certs: http-01 self check failed for domain "example.comCreate a Mapping for the
/.well-known/acme-challenge/
route.
cert-manager uses an Ingress
resource to issue the challenge to /.well-known/acme-challenge/
but, since Ambassador is not an Ingress
, we will need to create a Mapping
so the cert-manager can reach the temporary pod.
---apiVersion: getambassador.io/v2kind: Mappingmetadata:name: acme-challenge-mappingspec:prefix: /.well-known/acme-challenge/rewrite: ""service: acme-challenge-service---apiVersion: v1kind: Servicemetadata:name: acme-challenge-servicespec:ports:- port: 80targetPort: 8089selector:acme.cert-manager.io/http01-solver: "true"
Apply the YAML and wait a couple of minutes. cert-manager will retry the challenge and issue the certificate.
Verify the secret is created:
$ kubectl get secretsNAME TYPE DATA AGEambassador-certs kubernetes.io/tls 2 1hambassador-token-846d5 kubernetes.io/service-account-token 3 2hdefault-token-4l772 kubernetes.io/service-account-token 3 2h
Questions?
We’re here to help. If you have questions, join our Slack or contact us.