This is a cert-manager ACME DNS01 webhook solver that uses a local DNS server to solve DNS challenges. Inspired by the existing acme-dns project, this webhook aims to provide a slimmer and less complex alternative.
If you are using cloud-hosted DNS, you can use one of the many existing DNS-01 solvers to automatically solve DNS challenges. If you are hosting your own PKI / ACME instance, you can use the EAB (External Account Binding) feature to solve DNS challenges. However, if you are hosting your own DNS but not your own PKI / ACME instance, you need a way to solve dynamic DNS challenges for your certificates without updating your existing DNS tooling. This service aims to solve that problem.
- Delegate a DNS zone to the webhook server. Note this must be publicly accessible.
- eg.
acme.example.com
- eg.
- Before creating a certificate, create a DNS
CNAME
record pointing to the webhook server- eg. to generate a certificate for
mydomain.com
, create aCNAME
record for_acme-challenge.mydomain.com
pointing to_acme-challenge.mydomain.com.acme.example.com
. Note you simply append the delegated zone to the challenge domain.
- eg. to generate a certificate for
- When cert-manager creates a DNS challenge, the webhook server will receive a request to create a TXT record for
_acme-challenge.mydomain.com.acme.example.com
. Once this is validated, the webhook server will delete the TXT record, but the CNAME record will remain in place for future challenges.
The webhook can be configured and deployed with Helm. Check deploy/values.yaml
for the available configuration options.
If you stick with the defaults, you just need to edit the following values in the first few lines:
# The GroupName here is used to identify your company or business unit that
# created this webhook.
# For example, this may be "acme.mycompany.com".
# This name will need to be referenced in each Issuer's `webhook` stanza to
# inform cert-manager of where to send ChallengePayload resources in order to
# solve the DNS01 challenge.
# This group name should be **unique**, hence using your own company's domain
# here is recommended.
groupName: acme.example.com
# The nameserver is the authoritative nameserver that will be returned
# in queries for the domain name. This is usually the same as the domain
nameserver: "acme.example.com."
# The domain name is the domain name / zone that will be managed by this server.
domainName: "acme.example.com."
# The rname is the email address that will be used in the SOA record, where the
# @ symbol is replaced with a dot.
rname: "admin.acme.example.com."
# The publicIP is the IP address (or CNAME) that will point to this server.
# If you don't yet have this, such as in instances where your load balancer IP
# will be dynamically assigned, you can leave this blank and update it later.
publicIP: "1.2.3.4"
By default, the dnsService.Type
is ClusterIP
. In practice, the DNS service will need to be publicly available (or at least, the records it presents need to be publicly resolvable). This means that if you do not have a larger UDP-capable ingress gateway, you will need to change this to LoadBalancer
, and then update the publicIP
value to the IP address (or CNAME) of the load balancer.
Once you have configured the values, you can install the webhook with:
helm upgrade -i -n cert-manager \
cert-manager-webhook-acme-localdns \
./deploy/localdns \
-f /path/to/values.yaml
Once the webhook is installed, you can create an Issuer or ClusterIssuer with the following configuration:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: localdns-staging
namespace: cert-manager
spec:
acme:
email: admin@example.com
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: cm-issuer-localdns-staging
solvers:
- dns01:
cnameStrategy: Follow
webhook:
groupName: acme.example.com
solverName: localdns
Note the cnameStrategy
is set to Follow
. This is required to enable the webhook to follow the CNAME record to the correct zone.
Also, note that the groupName
and solverName
must match the values you configured in the webhook deployment.
Now that your issuer is installed, and assuming your DNS zone is delegated to the webhook server, you can create a certificate:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example.com
namespace: istio-system
spec:
secretName: example.com
issuerRef:
name: localdns-prod
kind: ClusterIssuer
dnsNames:
- 'example.com'
You should now be able to do a DNS lookup for _acme-challenge.example.com
and see the CNAME record pointing to _acme-challenge.example.com.acme.example.com
, which in turn should return the TXT record for the challenge. Once the challenge is complete, the TXT record will be deleted, but the CNAME record will remain in place for future challenges.
Whereas acme-dns
exposes a REST interface and requires token-based auth, localdns
exposes a Kubernetes cluster-internal webhook and uses TLS client certificates for authentication. This means that the webhook is only accessible from within the cluster, and only cert-manager is able to authenticate to the webhook.
The UDP/53 DNS service which is intended to be exposed to the public internet is a separate service from the cluster-internal webhook service, enabling further service mesh RBAC if desired. The DNS service is also configured to only respond to queries for the domain name that is managed by the webhook, and only for the TXT record that is being validated, ensuring it remains authoritative for only the domain name that is delegated to it.