Overriding the default challenge domain in DNS authenticators#5350
Overriding the default challenge domain in DNS authenticators#5350quinot wants to merge 7 commits intocertbot:masterfrom quinot:topic/dns-follow-cnames
Conversation
|
Thanks for the PR! I see this as an important addition to the existing functionality of DNS plugins, and we definitely will need to handle it. I read through the PR code, and there's a few points I would like to raise for discussion:
That said, I really would love to see the functionality in our range of DNS plugins as it is an important and effective way to avoid storing credentials to the actual DNS zone in the boxes themselves. I would propose following changes to the PR:
These changes would make it clear from the get go which plugin, and which credentials need to be used in addition of avoiding adding complexity and a new dependency. Let me know what you think! |
|
Hi @joohoi , thanks for your feedback! Simply overriding the target name was an option I did consider. I opted for doing CNAME chain resolution instead out of a DRY consideration: requiring the user to provide the target of the CNAME repeats some information we know already exists in the DNS. Also, one thing to keep in mind is that we need to handle the case of multiple subjectaltnames being requested: in that case you have different target names, so the command line option to override the challenge domain needs to be per-subjectaltname (just like -w is for webroot). On the other hand, I do see the merit of not dragging in an additional dependency. So, I'll send in a modified version:
instead of having to write:
(However I think the second, more verbose form needs to be supported anyway, so I'll steal some code from webroot -- I'll try to see if some of it can be shared). |
|
I look forward to the changes. The proposed convenience functionality of variable record part seems like a good idea where multiple SAN domains are needed. And there definitely is a need for the webroot style mapping as well, good catch! |
|
@joohoi The new code is here, with CI passing! I had to tweak the testsuite a bit in order to avoid having to repeat configuration options for every DNS plugin. Common options are now cleanly set in a single place. |
|
Thanks! We're currently busy with mitigating the removal of TLS-SNI, but I'll try to get back to review this PR as soon as possible. |
|
Understood, thanks for the heads-up, and good luck with the SNI issue! |
joohoi
left a comment
There was a problem hiding this comment.
I'm terribly sorry it took so long to get back to reviewing this important pull request but here we go. I really like the new design and the general code quality is excellent. Thank you for working on this!
However there's a few required changes that are blocking the merge currently. The most important of course is the current state of being unable to use the DNS plugins without the override challenges CLI flag.
| domain = achall.domain | ||
| acme_loc = achall.validation_domain_name(achall.domain) | ||
|
|
||
| challenge_map = self.conf('override-challenge-map') |
There was a problem hiding this comment.
In the current state Certbot crashes when run without the override parameter.
If the --dns-###-override-challenge parameter isn't provided on the commandline, these values do not get initialized in the configuration, and Certbot will error out here.
I would like to see (at least some) test coverage for this case too.
There was a problem hiding this comment.
Ah, indeed an oversight.
Test coverage here will be interesting, as currently all DNS plugin tests provide a fake config which is assumed to reflect the actual namespace from argument parsing, and here what we'd want is a test based on the actual argument parser.
There was a problem hiding this comment.
Sorry, I should have been more clear here. Mocking or changing the test config object is completely fine for this purpose. The argument parsing is done and tested elsewhere in the project. What I'm interested here is testing the inner plumbing with and without the parsed configuration variables present to prevent regressions in the future.
certbot/plugins/dns_common.py
Outdated
| 'location. The specified value may include "{domain}" ' | ||
| 'and "{acme}" format strings, which will respectively ' | ||
| 'expand to the domain being requested, and the corresponding ' | ||
| 'standard ACME challenge location.') |
There was a problem hiding this comment.
The otherwise clear documentation is a bit fuzzy about the definition of {acme} format string. I think this should be clarified.
There was a problem hiding this comment.
OK, will try... {acme} is the default location, i.e. if domain is "example.com", {acme} is "_acme-challenge.example.com".
| for domain in namespace.domains: | ||
| self.challenge_map.setdefault(domain, | ||
| getattr(namespace, self.dest)) | ||
| setattr(namespace, self.dest, override_chall) |
There was a problem hiding this comment.
This probably should happen before the for loop above, as all the values in self.challenge_map are mapped to the default value of the parameter ({acme}) if not. This results to every domain getting the default value as an override domain.
There was a problem hiding this comment.
This part was actually as intended. The override option is intended to apply to domains that appear after it on the command line, so the loop here applies the previous value to previous domains. This is similar to what the webroot plugin does.
There was a problem hiding this comment.
(The new commit pushed today includes additional comments here to make the flow clearer).
|
I have pushed a new commit that fixes the case of no override specified. I have adjusted the testsuite as mentioned in my earlier comment, so that both this case, and the case of override specifieds, are tested with the actual argument parsing circuitry, instead of a mock configuration. I also had successful certificate issuance dry-runs, both with and without override specified. |
There was a problem hiding this comment.
Unfortunately, the way that most of the current Certbot DNS plugins work is that they resolve the zone root using the domain variable passed to _perform() function as a base to resolve the zone root to. This results into following scenario:
User wants to get certificate for actualdomain.tld but uses records under validationdomain.tld for the DNS validation by pointing CNAME record _acme-challenge.actualdomain.tld to actual.validationdomain.tld.
- Certbot CLI
certbot certonly -a dns-cloudflare --dns-cloudflare-override-challenge actual.validationdomain.tld -d actualdomain.tld _perform()gets arguments: domain=actualdomain.tldvalidation_name=actual.validationdomain.tldvalidation=...- Value of
domaingets used as zone root, andvalidation_namewill be used as the record name.
As a result, a following record would be created:
actual.validationdomain.tld.actualdomain.tld TXT "..."
| self._attempt_cleanup = False | ||
|
|
||
| @classmethod | ||
| def inject_parser_options(cls, parser, name): |
There was a problem hiding this comment.
I would like to see a short docstring here to explain the need for kwargs expansion etc. This is to make the functionality clear for someone reading the code in a year from now.
There was a problem hiding this comment.
(for the record, this has been done)
|
Hmmm right indeed. As a matter of fact, the one I was able to extensively test (RFC 2136) did have that issue. I looked for it in other plugins, but missed the fact that the shared Lexicon module had it too. So, let's see:
|
|
I have a few thoughts regarding the arguments passed to the actual DNS plugin method I feel that a solution where the When the validation domain is overridden by the newly added CLI argument, I don't think there would be any reason for the actual executor part (the DNS plugin) to handle the original domain if it's explicitly overridden. I might be missing something, but wasn't able to come up with an situation where this wouldn't be the case. How would you feel to change the behavior as such? |
|
If you suggestion is to remove the original domain for the arguments of _perform, and keep only the validation (challenge) domain, yes, that makes sense; as a matter of fact that's the direction I've been taking, already removing the original domain parameter from the various {add,del}_txt_record functions. I can go one step further and also remove it from _perform, that will be a good cleanup. |
I'm afraid we can't do that in order to keep the API intact with the external DNS plugins. But what I think we should do is to override the value with domain from |
|
I see your point with not changing the API for external plugins. So what you suggest is actually passing the same value (the challenge domain, possibly overridden) for both parameters of _perform, right? |
Correct! If you see some issues in this approach, please let me know and we can figure a better way to handle this. Currently I think that this would be most sane way to handle the issue at hand. |
|
OK, good, that's what I implemented in the last commit (80a6754). |
|
Change rebased to resolve conflicts with latest changes for Google DNS. |
|
I think that leaving the actual plugin (executor) code like it is currently would be the best way forward, as when not overriding the domain, it will save a few queries for finding the domain root. When the domain is overridden, the plugins would then just use the (also overridden) domain value for the SOA resolution. |
|
Hi @joohoi, sorry for the delay in getting back to you, I was on vacation. |
|
Hi @joohoi, where do we stand with the current situation? As I said, restoring the previous behaviour of starting the lookup at the enclosing domain in the case where it's not overridden will save a few queries, but will reintroduce a bug whereby the case where _acme-challenge is itself a delegation apex is not handled correctly. |
|
Yeah! You are absolutely correct, and this was addressed separately in another PR for rfc2136 plugin. The review process is unfortunately taking a while, as we have to go through all the different providers functionality separately. Sorry for the delay, but this is being worked on! |
|
Good catch! Route53 plugin fixed. |
|
@joohoi, can you give an update on the status here? I don't think you need to do the work, but I'd like to know what still needs to be done so someone else can take over. |
|
I have tested it against all but Google DNS service, and the few that we don't have credentials for. Everything should be in order regarding the functionality. I still have some concerns regarding the CLI help text, and feel that it should be clarified a bit, but am unsure how to actually do this while keeping the help text short enough. |
|
One possible clarification would be to add to the help text:
I'll be happy to take care of it if you think this addition would be useful. |
|
@joohoi ping? Shall I update the help text as indicated Is there anything else still blocking merge? |
|
I would definitely appreciate this getting merged! I've got a need for this exact use case in the very near future :) |
|
Following CNAMEs for DNS challenges is what's blocking our IRC network (hackint.org) from migrating to Letsencrypt at this point, so additional priority given to this would be highly appreciated. |
|
Nearly an year later... will this merge ever happen? @joohoi |
|
Still no feedback more than three weeks after my last message here. I do not have the bandwidth to maintain a fork of certbot in the long term, so I will be looking into other ACME clients. I find it quite unfortunate that we were not able to have this useful feature incorporated in the "official" one. |
|
I'm sorry you haven't gotten a response here. Both joohoi and I have been on personal leave. While we often are unable to immediately review PRs (as Certbot has only 1 full time developer right now), we certainly try to avoid people getting no response for weeks at a time. We appreciate this contribution and plan to look at it in the coming weeks. In the meantime, if another client works better for you, please use it! The important thing is you're able to get a cert for your site. |
|
Unfortunately no. It's still on our radar, but we haven't had time to get to it yet. As far as I know, the biggest issue here is manually testing the changes here against all affected providers. |
|
@quinot, are you OK with us moving your code here into a branch on our repo and building off of it and testing it ourselves when we finally have cycles to work on this? |
|
@bmw feel free to. |
|
Done. I created #6566 and put your work in the quinot/topic/dns-follow-cnames branch. Thanks again for your help @quinot. |
|
Is the functionality to use an alias for the dns challenge merged to master? Or do I need to use the branch https://github.com/certbot/certbot/tree/quinot/topic/dns-follow-cnames to have it? |
Support a new option --dns--follow-cnames whereby
certbot will follow a (chain of) CNAME record(s) at the
challenge domain (_acme-challenge.) and instead
update the target domain.
Independently of this option, this change also incidentally fixes
the possibly uncommon use case where _acme-challenge is itself a
delegation apex (previously the RFC2136 plugin would generate an
update for the wrong zone in that case).