Certbot dns-digitalocean plugin and CNAME

I am testing to use certbot with dns-digitalocean plugin. Since my primary DNS does not support dynamic DNS update, I set up a subdomain digitalocean-ns.example.com and delegate this subdomain to digitalocean. Then I set up a _acme-challenge.test.example.com CNAME record to _acme-challenge.test.digitalocean-ns.example.com

Now the problem is, how can I tell dns-digitalocean plugin to update _acme-challenge.test.digitalocean-ns.example.com.instead of _acme-challenge.test.example.com? Or I have to use cloudflare (certbot-dns-cloudflare-cname) instead? I chose digitalocean just because I have an existing account there, and feel lazy to create a new cloudflare account.

Last I knew, certbot didn't support publishing DNS challenges with CNAME redirects. You'd need to use an alternative client that does.


This is a real pain for me, because I have a existing centralized certbot installation that already issued a lot of certificates and have all sort of custom dns authorization hook and deploy scripts to remotely deploying the certificates to the servers.

FYI, since this installation is done in the infancy of DNS-01, there was not really good acme agent support and I had to write my own dns hook to interact with my own powerdns. I want to switch to digitalocean (or whatsever) and retire my powerdns (and the associated dns hooks) for maintainability. If I have to change acme agent, I would have to rewrite all my server script deployment scripts.

In this case I would rather try the unofficial plugin certbot-dns-cloudflare-cname instead. I want to keep my existing certbot cert issuance config. However I would need to upgrade the certbot server first. This server is running on ubuntu 18.04 and only offers python 3.6, which does not meet certbot-dns-cloudflare-cname minimum requirement

I wonder if it would work to install a newer Python with snap. (I'm not sure; I haven't tried that setup.)


Also, do you know how to program in Python?

The actual code for certbot-dns-digitalocean is pretty small (it mostly relies on an external Python library implementation of the DigitalOcean API).

It would probably be possible to just change the _compute_record_name function at the very end in order to do what you want. Or, if it's too much of a nuisance to change this in the context of a version of the certbot-dns-digitalocean package, copy this file outside of that context entirely, make that change, and then wrap the invocation of that Python script with an auth-hook script.


Not that simple. It already failed at the beginning: _find_domain threw error message "Unable to determine base domain" before actually going through _compute_record_name. We need to resolve the CNAME first before actually executing this plugin. Although it is possible to handle in plugin, this should be certbot's duty instead.

FYI, this is the actual patch required to make CNAME _acme-challenge works on DigitalOcean
(I have an internal DNS that have different view from external, therefore I hard coded DNS server in my code)

--- certbot_dns_digitalocean/_internal/dns_digitalocean.py.orig 2023-03-25 00:03:50.695051857 +0800
+++ certbot_dns_digitalocean/_internal/dns_digitalocean.py      2023-03-25 00:02:37.322021202 +0800
@@ -6,6 +6,8 @@

 import digitalocean

+import dns.resolver
 from certbot import errors
 from certbot.plugins import dns_common
 from certbot.plugins.dns_common import CredentialsConfiguration
@@ -26,6 +28,8 @@
     def __init__(self, *args: Any, **kwargs: Any) -> None:
         super().__init__(*args, **kwargs)
         self.credentials: Optional[CredentialsConfiguration] = None
+        self.resolver = dns.resolver.Resolver(configure = False)
+        self.resolver.nameservers = [""]

     def add_parser_arguments(cls, add: Callable[..., None],
@@ -47,10 +51,12 @@

     def _perform(self, domain: str, validation_name: str, validation: str) -> None:
+        validation_name = self.resolver.canonical_name(validation_name).to_text().rstrip(".")
         self._get_digitalocean_client().add_txt_record(domain, validation_name, validation,

     def _cleanup(self, domain: str, validation_name: str, validation: str) -> None:
+        validation_name = self.resolver.canonical_name(validation_name).to_text().rstrip(".")
         self._get_digitalocean_client().del_txt_record(domain, validation_name, validation)

     def _get_digitalocean_client(self) -> "_DigitalOceanClient":
@@ -81,7 +87,7 @@

-            domain = self._find_domain(domain_name)
+            domain = self._find_domain(record_name)
             # The TTL value is set to the SOA record's TTL. Unless set to a falsy value,
             # the optional TTL argument to add_txt_record() would be ignored.
             # See https://github.com/certbot/certbot/pull/9149 for details.
@@ -126,7 +132,7 @@

-            domain = self._find_domain(domain_name)
+            domain = self._find_domain(record_name)
         except digitalocean.Error as e:
             logger.debug('Error finding domain using the DigitalOcean API: %s', e)

I applaud your efforts but can't you just change your Authoritative DNS Servers at your domain registrar to be Digital Ocean's? Then you don't need the CNAME or updated scripts.

Using the CNAME to DO could also work by using the --manual authentication along with the --manual-auth-hook (link here) (and here)


Changing primary DNS hosting to third party is a political matter that probably need lengthy process. CNAME _acme-challenge to DO is much simpler by comparision


Also for security reason, it is better to separate certbot to only access the CNAME delegated zone dedicated for _acme-challenge, instead of full zone. DigitalOcean API cannot limit the API key scope. Having the API key will allow the program to access all resources in the project.


Sounds like you need to CNAME to some other DSP.


Or at least, create a new account (or probably new project is enough?) in DigitalOcean, if I were to host my primary zone to DigitalOcean (currently my primary zone is self-hosted)

My point is CNAME may still be necessary to limit the resource access level, even though my primary host supports dynamic update.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.