Hey all, thanks for the replies on this topic. To provide some background, I’m implementing a custom ACME client based off of the acmephp project.
https://github.com/acmephp/acmephp/blob/master/src/Core/AcmeClient.php#L180-L204 is where the challenge authorization flow is happening and things are stuck in a loop until the timeout is met.
What I’m implementing on my end that seems to correct things is as follows :
/**
* @test
*/
public function filtersOutDns01WhenValidHttp01ChallengeExists()
{
$domain = 'example.com';
$challenges = [
$domain => [
new AuthorizationChallenge($domain, "valid", 'http-01', $domain, "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'dns-01', $domain, "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'tls-alpn-01', $domain, "this is a token", random_bytes(32))
],
"*.$domain" => [
new AuthorizationChallenge($domain, "pending", 'http-01', "*.$domain", "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'dns-01', "*.$domain", "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'tls-alpn-01', "*.$domain", "this is a token", random_bytes(32))
]
];
$this->assertCount(1, RequestANewCertificate::filterAuthorizationChallenges($challenges));
}
/**
* @test
*/
public function filtersAuthorizationChallengeReturnsTwoDns01ChallengesWhenAllChallengesArePending()
{
$domain = 'example.com';
$challenges = [
$domain => [
new AuthorizationChallenge($domain, "pending", 'http-01', $domain, "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'dns-01', $domain, "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'tls-alpn-01', $domain, "this is a token", random_bytes(32))
],
"*.$domain" => [
new AuthorizationChallenge($domain, "pending", 'http-01', "*.$domain", "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'dns-01', "*.$domain", "this is a token", random_bytes(32)),
new AuthorizationChallenge($domain, "pending", 'tls-alpn-01', "*.$domain", "this is a token", random_bytes(32))
]
];
$this->assertCount(2, RequestANewCertificate::filterAuthorizationChallenges($challenges));
}
This is the most pertinent detail here as my implementation about filtering logic isn’t particularly interesting, rather that a challenge group with an already solved challenge – even if it isn’t the same type is solved because then the order becomes valid.
By pre-filtering the already valid authorizations at a per (subdomain / wildcard) level the problem is obviated and the order will succeed – was able to issue the remaining 600 certs we needed last night / this morning. Given that I’m relying on the upstream acmephp implementation for challengeAuthorization I’m guessing the problem is related to some of the previous comments; however, for now, I have a working implementation now that I’m filtering in a way that meets the above test condition. Perhaps something like this could / should be implemented at the AcmePHP core level or perhaps it’s up to implementing parties to do that. Either way, the above test gets things working for me where I have a little flat list of challenges to loop through and complete.