Can not register for example.com and *.example.com at same time with dns-01 challenge #16

Closed
opened 2019-02-16 05:18:03 +00:00 by Ghost · 4 comments

When we register with like example.com and *.example.com. acme will test all the domains parallelly, and then request certificates serially.

example.com and *.example.com using the same challenge dns name _acme-challenge.example.com, when testing domains, it will fail for competition. But we can disable testing by setting skipChallengeTest to avoid test failing temporary.

But the following step has another issue that will lead to failure.

That is the removeChallenge flow:

https://git.coolaj86.com/coolaj86/acme-v2.js/src/branch/master/node.js#L389

      if ('valid' === resp.body.status) {
        if (me.debug) { console.debug('poll: valid'); }

        try {
          if (1 === options.removeChallenge.length) {
            options.removeChallenge(auth).then(function () {}, function () {});
          } else if (2 === options.removeChallenge.length) {
            options.removeChallenge(auth, function (err) { return err; });
          } else {
            options.removeChallenge(identifier.value, ch.token, function () {});
          }
        } catch(e) {}
        return resp.body;
      }

Here we returned without waiting removing complete. When executing 2rd domain to update the same dns record, there's a great chance that the record has been deleted.

When we register with like `example.com` and `*.example.com`. acme will test all the domains parallelly, and then request certificates serially. `example.com` and `*.example.com` using the same challenge dns name `_acme-challenge.example.com`, when testing domains, it will fail for competition. But we can disable testing by setting `skipChallengeTest` to avoid test failing temporary. But the following step has another issue that will lead to failure. That is the `removeChallenge` flow: https://git.coolaj86.com/coolaj86/acme-v2.js/src/branch/master/node.js#L389 ```js if ('valid' === resp.body.status) { if (me.debug) { console.debug('poll: valid'); } try { if (1 === options.removeChallenge.length) { options.removeChallenge(auth).then(function () {}, function () {}); } else if (2 === options.removeChallenge.length) { options.removeChallenge(auth, function (err) { return err; }); } else { options.removeChallenge(identifier.value, ch.token, function () {}); } } catch(e) {} return resp.body; } ``` Here we returned without waiting removing complete. When executing 2rd domain to update the same dns record, there's a great chance that the record has been deleted.
Author

The TXT records could be multiple. So the removeChallenge sequence should not be the problem.

So we should call challenge.set concurrently for example.com and *.example.com before requesting acme server to validate?

The TXT records could be multiple. So the `removeChallenge` sequence should not be the problem. So we should call `challenge.set` concurrently for `example.com` and `*.example.com` before requesting acme server to validate?
Author

ACME server validate TXT record only once for one order?

I have validated two TXT records as acem-v2.js's sequence, but ACME server only found the first TXT records for two requests. So the second is not match.

ACME server validate TXT record only once for one order? I have validated two TXT records as acem-v2.js's sequence, but ACME server only found the first TXT records for two requests. So the second is not match.
Author

cerbot works well with this.

It seams that update all dns record of domains at the same time before submitting to ACME server in certbot.

It is relative codes below.

https://github.com/certbot/certbot/blob/master/certbot/plugins/dns_common.py#L46

    def perform(self, achalls): # pylint: disable=missing-docstring
        self._setup_credentials()

        self._attempt_cleanup = True

        responses = []
        for achall in achalls:
            domain = achall.domain
            validation_domain_name = achall.validation_domain_name(domain)
            validation = achall.validation(achall.account_key)

            self._perform(domain, validation_domain_name, validation)
            responses.append(achall.response(achall.account_key))

        # DNS updates take time to propagate and checking to see if the update has occurred is not
        # reliable (the machine this code is running on might be able to see an update before
        # the ACME server). So: we sleep for a short amount of time we believe to be long enough.
        logger.info("Waiting %d seconds for DNS changes to propagate",
                    self.conf('propagation-seconds'))
        sleep(self.conf('propagation-seconds'))

        return responses
`cerbot` works well with this. It seams that update all dns record of domains at the same time before submitting to ACME server in `certbot`. It is relative codes below. https://github.com/certbot/certbot/blob/master/certbot/plugins/dns_common.py#L46 ```python def perform(self, achalls): # pylint: disable=missing-docstring self._setup_credentials() self._attempt_cleanup = True responses = [] for achall in achalls: domain = achall.domain validation_domain_name = achall.validation_domain_name(domain) validation = achall.validation(achall.account_key) self._perform(domain, validation_domain_name, validation) responses.append(achall.response(achall.account_key)) # DNS updates take time to propagate and checking to see if the update has occurred is not # reliable (the machine this code is running on might be able to see an update before # the ACME server). So: we sleep for a short amount of time we believe to be long enough. logger.info("Waiting %d seconds for DNS changes to propagate", self.conf('propagation-seconds')) sleep(self.conf('propagation-seconds')) return responses ```
Owner

@taoyuan, sorry I didn't reply to this sooner. I had it on my plate to look at that.

I just spent a bunch of time going through the wildcard use case and adding a bunch of tests.

The plugin architecture is 10x simpler now and if you still need the node / javascript solution I'm confident that you'll find it functions exactly as it should now.

If you want to write a DNS plugin, here's the test harness:

And here's the fully-tested reference implementation:

I'm going to close this out, but feel free to re-open it.

Thanks for being so diligent in really digging into the problem to uncover helpful information.

@taoyuan, sorry I didn't reply to this sooner. I had it on my plate to look at that. I just spent a bunch of time going through the wildcard use case and adding a bunch of tests. The plugin architecture is 10x simpler now and if you still need the node / javascript solution I'm confident that you'll find it functions exactly as it should now. If you want to write a DNS plugin, here's the test harness: * https://git.coolaj86.com/coolaj86/greenlock-challenge-test.js And here's the fully-tested reference implementation: * https://git.coolaj86.com/coolaj86/greenlock-challenge-dns.js I'm going to close this out, but feel free to re-open it. Thanks for being so diligent in really digging into the problem to uncover helpful information.
Sign in to join this conversation.
No Label
No Milestone
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: coolaj86/acme.js-ARCHIVED#16
No description provided.