Skip to main content

Issue an ACME Certificate with DNS01 Solver in CCE

A DNS01 challenge is a type of challenge used in the Domain Name System (DNS) to verify ownership of a domain during the process of obtaining an SSL/TLS certificate, often through services like Let's Encrypt.

When you request a certificate, the Certificate Authority (CA) needs to ensure that you have control over the domain for which you're requesting the certificate. The DNS01 challenge is one of the methods used to prove this control. Here's how it generally works:

  1. Challenge Issuance: The CA provides you with a unique token (a random string of characters) that needs to be added to your domain's DNS records.

  2. DNS Record Addition: You must create a specific DNS TXT record for your domain that includes the token provided by the CA. This record usually follows a format like _acme-challenge.example.com with a value corresponding to the token.

  3. Verification: Once you've added the TXT record to your domain's DNS configuration, the CA will query your domain's DNS records to look for the TXT record. If it finds the correct token, it confirms that you control the domain.

  4. Certificate Issuance: After successful verification, the CA will issue the SSL/TLS certificate.

The DNS01 challenge is commonly used because it allows for domain validation without needing to serve files over HTTP, which can be advantageous in certain situations, such as when you don't have a web server configured or when you're managing multiple subdomains.

One of the tools, that can be employed in the context of Kubernetes, to secure certificates from a Certificate Authority (CA) via the ACME protocol using the the DNS01 challenge, is cert-manager. Specifically for Open Telekom Cloud, we can use an additional webhook that acts as an ACME DNS01 solver for Open Telekom Cloud's Domain Name Service, cert-manager-webhook-opentelekomcloud.

Prerequisites​

Only prerequisite is cert-manager. If you don't have it already installed on your CCE Cluster, this can be very easily done using a Helm Chart:

helm repo add jetstack https://charts.jetstack.io 
helm repo update

helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.15.3 \
--set crds.enabled=true

Installing the ACME DNS01 Solver​

cert-manager-webhook-opentelekomcloud is an ACME DNS01 solver webhook for Open Telekom Cloud DNS written in Golang, and requires cert-manager to be installed first.

Acquiring Access/Secret Keys​

In the console, go to My Credentials -> Access Keys and either pick up an existing pair or create a new one:

alt text

Export this pair as environment variables:

export OS_ACCESS_KEY={value}
export OS_SECRET_KEY={value}

Installing the Helm Chart​

helm repo add cert-manager-webhook-opentelekomcloud https://akyriako.github.io/cert-manager-webhook-opentelekomcloud/
helm repo update

helm upgrade --install \
acme-dns cert-manager-webhook-opentelekomcloud/cert-manager-webhook-opentelekomcloud \
--set opentelekomcloud.accessKey=$OS_ACCESS_KEY \
--set opentelekomcloud.secretKey=$OS_SECRET_KEY \
--namespace cert-manager

Installing Cluster Issuers​

You are going to need one ClusterIssuer for the production and one for the staging Let's Encrypt endpoint.

warning

cert-manager has a known issue that prevents custom webhooks from working with an Issuer. For this reason, you must create your issuer as a ClusterIssuer instead.

Create and deploy the following manifest:

opentelekomcloud-letsencrypt-staging.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: opentelekomcloud-letsencrypt-staging
namespace: cert-manager
spec:
acme:
email: user@company.com
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: opentelekomcloud-letsencrypt-staging-tls-key
solvers:
- dns01:
webhook:
groupName: acme.opentelekomcloud.com
solverName: opentelekomcloud
config:
region: "eu-de"
accessKeySecretRef:
name: cert-manager-webhook-opentelekomcloud-creds
key: accessKey
secretKeySecretRef:
name: cert-manager-webhook-opentelekomcloud-creds
key: secretKey
note

Replace the placeholder email value user@company.com with the address you want to use when requesting certificates from Let's Encrypt.

kubectl apply -f opentelekomcloud-letsencrypt-staging.yaml

Requesting Certificates​

warning

Using the staging endpoint of Let's Encrypt before switching to production is considered best practice. Let's Encrypt applies rate limits to control how many certificates can be requested within a certain time. Testing against the staging environment helps you avoid exceeding these limits during development and validation.

Explicit​

Create and deploy the following manifest:

certificate-example-com
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate-example-com
spec:
dnsNames:
- '*.example.com'
issuerRef:
kind: ClusterIssuer
name: opentelekomcloud-letsencrypt
secretName: certificate-example-com-tls
note

Replace the placeholder DNS name *.example.com with a domain you own, which will be used to obtain a certificate from Let's Encrypt. Before applying the manifest ensure *.example.com can resolve to an actual public IP address via an A record in Open Telekom Cloud DNS service.

kubectl apply -f certificate-example-com

Implicit​

Let's create the manifests to deploy a demo workload based on traefik/whoami:

whoami.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 3
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami-service
spec:
selector:
app: whoami
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami-ingress
annotations:
cert-manager.io/cluster-issuer: opentelekomcloud-letsencrypt
spec:
ingressClassName: nginx
tls:
- hosts:
- whoami.example.com
secretName: whoami-example-com-tls
rules:
- host: "whoami.example.com"
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: whoami-service
port:
number: 80
warning

If you have opted-in for automatic DNS records creation via ExternalDNS, as described in Automate DNS Records Creation from CCE Ingresses with ExternalDNS, the required DNS A records will be created automatically. If not, before applying the manifest ensure the Elastic Load Balancer's Elastic IP is bound to whoami.example.com by creating manually an A record in Open Telekom Cloud DNS service.

kubectl apply -f whoami.yaml

When you open https://whoami.example.com in your browser, you'll see that the endpoint is served over HTTPS and secured with a valid certificate.