Published on 00/00/0000
Last updated on 00/00/0000
Published on 00/00/0000
Last updated on 00/00/0000
Share
Share
10 min read
Share
HTTP-01
and DNS-01
.
GET
request to a url of the form http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>
and the response is a combination of the token and your ACME account key.
This is probably the simplest challenge to automate, but it can't be used for wildcard domains (for example *.example.com). For wildcard domains you have to solve the DNS-01
challenge.
DNS-01
challenge can be done by putting a specific value in a TXT
record for the given domain. As the main idea behind the ACME protocol is automation, this challenge type only makes sense if your DNS provider has an API.
There are several ACME clients which can handle the submitting of CSRs as well as solving the required challenges. One such client is certbot which can handle "legacy" environments (Apache, Nginx, etc.). If you are running your services in a Kubernetes cluster, your best bet is to use cert-manager. Let's take a look at how it works.
Ingress
to route your ingress traffic, cert-manager can automatically solve HTTP-01
challenges. It does this by spinning up a pod for each challenge, then applying the necessary routing changes to your Ingress
config.
When you interact with cert-manager you'll be using a couple of custom resource types: Issuer
, ClusterIssuer
, and Certificate
. There are a few other custom resource types involved, which aren't necessary to know about but which can be helpful in tracking down errors. These are the CertificateRequest
, Order
and Challenge
custom resources.
Let's see what each one is used for.
Issuer
or ClusterIssuer
resource describes one issuer entity. You will need at least one such resource in your cluster. We will be focusing on the ACME Issuer type. ACME is the protocol implemented by Let's Encrypt.
The difference between Issuer
and ClusterIssuer
is that the Issuer
's use is restricted to the namespace it's created in; it's not possible to reference it from a Certificate
resource in another namespace. A ClusterIssuer
, however, is global, usable from any Certificate
resource in the cluster.
A Certificate
resource describes the intention to acquire a certificate for one or more of your domains. Each certificate must reference an issuer resource. This will be the issuer used for acquiring the desired certificate.
Certificate
resource it proceeds with the creation of a CertificateRequest
.
This resource represents one request for a certificate. It contains the certificate signing request, which is itself encoded in PEM
format, and the certificate if it was already received, in which case Ready
will be set to True
.
This resource type comes into play when a certificate is requested from an ACME issuer. An Order
resource represents the order for a certificate to be issued.
Challenge
resources.
Not unlike the Order
resource type, this resource type plays a role when the certificate is requested from an ACME issuer. These resources describe the challenges cert-manager selected to solve.
If you need a hand with that, you can create a cluster with the Banzai Clouds Pipeline platform on five different clouds or on-premise. Pipeline is available online, for free at Try Pipeline.
KUBECONFIG
at your cluster.Ingress
controller in your cluster. If you used Banzai Cloud's Pipeline platform to create the cluster, you already have one along with a LoadBalancer
-type Service
:
$ kubectl get -n pipeline-system deploy/ingress-traefik svc/ingress-traefik
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-traefik 1/1 1 1 3m19s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-traefik LoadBalancer 10.10.231.18 a015671f691794291bd991eb635ed907-1982155256.us-east-2.elb.amazonaws.com 443:32271/TCP,80:30543/TCP 3m20s
The service should have port 80 and 443 open. Port 80 is for the ACME HTTP-01
challenge, and port 443 is for HTTPS traffic.kubectl apply -f https://github.com/jetstack/cert-
manager/releases/download/v0.15.1/cert-manager.yaml
LoadBalancer
typed Service
on which you want external traffic to enter your cluster. "External IP" might be a DNS name (as it is in the example above), in which case you will have to create a CNAME
record instead of an A
record.Set the DOMAIN
environment variable to your domain (example.com
will not work):
$ DOMAIN=example.com
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: default
spec:
selector:
app: httpbin
ports:
- port: 8080
protocol: TCP
targetPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: httpbin
namespace: default
labels:
app: httpbin
spec:
containers:
- image: kennethreitz/httpbin:latest
name: httpbin
ports:
- containerPort: 80
protocol: TCP
EOF
You can check if it's up and running through port-forwarding and curl:
$ kubectl port-forward svc/httpbin 8080 &
$ curl localhost:8080/get
Note: we will be using the staging provider to avoid hitting rate limits.First, set the
EMAIL
environment variable to your email address. Let's Encrypt will use this to contact you about expiring certificates and other issues related to your account.
$ EMAIL=some@email.address
Now, you're ready to create the issuer for Let's Encrypt:
$ kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
email: ${EMAIL}
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: example-issuer-account-key
solvers:
- http01:
ingress:
name: test-ingress
EOF
$ kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: default
spec:
backend:
serviceName: httpbin
servicePort: 8080
tls:
- hosts:
- ${DOMAIN}
secretName: test-ingress-cert
EOF
This ingress is not functional yet, because the secret being referenced does not exist.
If you remove the tls config, the service should be reachable on HTTP protocol:If it's not working, try the external IP of your ingress service to see if the problem is in your cluster or with the DNS resolution. If it works with the external IP, you might need to wait a couple of minutes for the DNS changes to propagate.$ curl ${DOMAIN}/get
HTTP-01
challenge. After the certificate is issued, the tls
config can be added back in.Issuer
and use it in your Certificate
resourcecurl --insecure https://${DOMAIN}/get
)Certificate
resource and change the issuer to the Let's Encrypt Issuer
Certificate
resource to instruct cert-manager to create a self signed certificate before attempting to request the properly signed one. This is basically the same as method 2, but automated.Certificate
resource with cert-manager.io/issue-temporary-certificate: "true"
.
$ kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: test-ingress
namespace: default
labels:
cert: test-ingress
annotations:
cert-manager.io/issue-temporary-certificate: "true"
spec:
secretName: test-ingress-cert
dnsNames:
- ${DOMAIN}
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
EOF
If everything is set up correctly, you should receive a certificate in a few minutes. You can check its progress by inspecting the Certificate
, CertificateRequest
, Order
and Challenge
resources. Take a look at the status and events sections.
$ kubectl describe certificate -l cert=test-ingress
$ kubectl describe certificaterequest -l cert=test-ingress
$ kubectl describe order -l cert=test-ingress
$ kubectl describe challenge
Note: as of cert-manager v0.15.1,Challenge
resources don't inherit the labels the way other resources do, so it's not as easy to select the challenges that correspond to yourCertificate
resource.
Try adding one or more entries (for example foo.< your-domain>) toThe received certificate will be placed in the secret with keydnsNames
in yourCertificate
resource, which you have not pointed to your ingress. Challenges for these domains won't be solvable, so you'll have time to inspect theChallenge
resources. You can also see the changes it made to yourIngress
resource.
tls.crt
and the corresponding private key with key tls.key
. You can check it with the following command:
$ kubectl describe secret test-ingress-cert
--insecure
flag is necessary when using the staging provider, which we did):
$ curl -v --insecure https://${DOMAIN}/get
...
* Server certificate:
* subject: CN=<your-domain>
* start date: Jul 3 19:57:58 2020 GMT
* expire date: Oct 1 19:57:58 2020 GMT
* issuer: CN=Fake LE Intermediate X1
...
Keeping cert-manager and your Certificate
resources in your cluster will ensure that all your certificates are renewed before expiry.Get emerging insights on emerging technology straight to your inbox.
Discover why security teams rely on Panoptica's graph-based technology to navigate and prioritize risks across multi-cloud landscapes, enhancing accuracy and resilience in safeguarding diverse ecosystems.
The Shift is Outshift’s exclusive newsletter.
Get the latest news and updates on cloud native modern applications, application security, generative AI, quantum computing, and other groundbreaking innovations shaping the future of technology.