From 8bae34876ba6b2a8eb4ab58fbcbda0aa7e73506c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 4 Jul 2019 22:45:08 +0200 Subject: [PATCH 001/126] Deprecate commands in favor of run --- src/Cli/Command/AuthorizeCommand.php | 2 ++ src/Cli/Command/CheckCommand.php | 2 ++ src/Cli/Command/RegisterCommand.php | 2 ++ src/Cli/Command/RequestCommand.php | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 27ceb038..fd786262 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -56,6 +56,8 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->error('This command is deprecated. Use command "run" instead'); + $client = $this->getClient(); $domains = $input->getArgument('domains'); diff --git a/src/Cli/Command/CheckCommand.php b/src/Cli/Command/CheckCommand.php index 412562cd..69db5ee1 100644 --- a/src/Cli/Command/CheckCommand.php +++ b/src/Cli/Command/CheckCommand.php @@ -55,6 +55,8 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->error('This command is deprecated. Use command "run" instead'); + $repository = $this->getRepository(); $client = $this->getClient(); $domains = $input->getArgument('domains'); diff --git a/src/Cli/Command/RegisterCommand.php b/src/Cli/Command/RegisterCommand.php index e768244b..52e2e656 100644 --- a/src/Cli/Command/RegisterCommand.php +++ b/src/Cli/Command/RegisterCommand.php @@ -54,6 +54,8 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->error('This command is deprecated. Use command "run" instead'); + $repository = $this->getRepository(); /* diff --git a/src/Cli/Command/RequestCommand.php b/src/Cli/Command/RequestCommand.php index bf19c901..6b8befe6 100644 --- a/src/Cli/Command/RequestCommand.php +++ b/src/Cli/Command/RequestCommand.php @@ -87,6 +87,8 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->error('This command is deprecated. Use command "run" instead'); + $this->repository = $this->getRepository(); $this->client = $this->getClient(); $this->actionHandler = $this->getActionHandler(); From 9eeb20a1fd1ed164a76978d965b99ab3afaff5a9 Mon Sep 17 00:00:00 2001 From: Alexander Obuhovich Date: Thu, 18 Jul 2019 20:15:39 +0300 Subject: [PATCH 002/126] Adding Gandi.Net DNS solver class --- src/Cli/Command/AuthorizeCommand.php | 2 +- src/Cli/Command/CheckCommand.php | 2 +- src/Cli/Resources/services.xml | 6 + src/Core/Challenge/Dns/GandiSolver.php | 197 +++++++++++++++++++ tests/Core/Challenge/Dns/GandiSolverTest.php | 105 ++++++++++ 5 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 src/Core/Challenge/Dns/GandiSolver.php create mode 100644 tests/Core/Challenge/Dns/GandiSolverTest.php diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 27ceb038..4a912891 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -32,7 +32,7 @@ protected function configure() { $this->setName('authorize') ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53)', 'http'), + new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'List of domains to ask an authorization for'), ]) ->setDescription('Ask the ACME server for an authorization token to check you are the owner of a domain') diff --git a/src/Cli/Command/CheckCommand.php b/src/Cli/Command/CheckCommand.php index 412562cd..72c63f8d 100644 --- a/src/Cli/Command/CheckCommand.php +++ b/src/Cli/Command/CheckCommand.php @@ -34,7 +34,7 @@ protected function configure() { $this->setName('check') ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53)', 'http'), + new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), new InputOption('no-test', 't', InputOption::VALUE_NONE, 'Whether or not internal tests should be disabled'), new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The list of domains to check the authorization for'), ]) diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index fa582e3f..72e9953d 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -182,6 +182,12 @@ + + + + + + diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php new file mode 100644 index 00000000..2072e985 --- /dev/null +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Dns; + +use AcmePhp\Core\Challenge\ConfigurableServiceInterface; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Webmozart\Assert\Assert; + +/** + * ACME DNS solver with automate configuration of a Gandi.Net. + * + * @author Alexander Obuhovich + */ +class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface +{ + use LoggerAwareTrait; + /** + * @var DnsDataExtractor + */ + private $extractor; + + /** + * @var ClientInterface + */ + private $client; + + /** + * @var array + */ + private $cacheZones; + + /** + * @var string + */ + private $apiKey; + + /** + * @param DnsDataExtractor $extractor + * @param ClientInterface $client + */ + public function __construct( + DnsDataExtractor $extractor = null, + ClientInterface $client = null + ) { + $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; + $this->client = null === $client ? new Client() : $client; + $this->logger = new NullLogger(); + } + + /** + * Configure the service with a set of configuration. + * + * @param array $config + */ + public function configure(array $config) + { + $this->apiKey = $config['api_key']; + } + + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge) + { + return 'dns-01' === $authorizationChallenge->getType(); + } + + /** + * {@inheritdoc} + */ + public function solve(AuthorizationChallenge $authorizationChallenge) + { + return $this->solveAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function solveAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + + $this->client->request( + 'PUT', + 'https://dns.api.gandi.net/api/v5/domains/' . $topLevelDomain . '/records/' . $subDomain . '/TXT', + [ + 'headers' => [ + 'X-Api-Key' => $this->apiKey, + ], + 'json' => [ + 'rrset_type' => 'TXT', + 'rrset_ttl' => 600, + 'rrset_name' => $subDomain, + 'rrset_values' => [$recordValue] + ] + ] + ); + + // TODO: Validate that record was set. + } + } + + /** + * {@inheritdoc} + */ + public function cleanup(AuthorizationChallenge $authorizationChallenge) + { + return $this->cleanupAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function cleanupAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + + $this->client->request( + 'DELETE', + 'https://dns.api.gandi.net/api/v5/domains/' . $topLevelDomain . '/records/' . $subDomain . '/TXT', + [ + 'headers' => [ + 'X-Api-Key' => $this->apiKey, + ], + ] + ); + } + } + + /** + * @param string $domain + * + * @return string + */ + protected function getTopLevelDomain($domain) + { + return \implode('.', \array_slice(\explode('.', $domain), -2)); + } + + + /** + * @param AuthorizationChallenge[] $authorizationChallenges + * + * @return AuthorizationChallenge[][] + */ + private function groupAuthorizationChallengesPerDomain(array $authorizationChallenges) + { + $groups = []; + foreach ($authorizationChallenges as $authorizationChallenge) { + $groups[$authorizationChallenge->getDomain()][] = $authorizationChallenge; + } + + return $groups; + } + + /** + * @param AuthorizationChallenge[] $authorizationChallenges + * + * @return AuthorizationChallenge[][] + */ + private function groupAuthorizationChallengesPerRecordName(array $authorizationChallenges) + { + $groups = []; + foreach ($authorizationChallenges as $authorizationChallenge) { + $groups[$this->extractor->getRecordName($authorizationChallenge)][] = $authorizationChallenge; + } + + return $groups; + } +} diff --git a/tests/Core/Challenge/Dns/GandiSolverTest.php b/tests/Core/Challenge/Dns/GandiSolverTest.php new file mode 100644 index 00000000..67df188b --- /dev/null +++ b/tests/Core/Challenge/Dns/GandiSolverTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests\AcmePhp\Core\Challenge\Dns; + +use AcmePhp\Core\Challenge\Dns\DnsDataExtractor; +use AcmePhp\Core\Challenge\Dns\GandiSolver; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use GuzzleHttp\ClientInterface; +use PHPUnit\Framework\TestCase; + +class GandiSolverTest extends TestCase +{ + public function testSupports() + { + $typeDns = 'dns-01'; + $typeHttp = 'http-01'; + + $mockExtractor = $this->prophesize(DnsDataExtractor::class); + $mockClient = $this->prophesize(ClientInterface::class); + $stubChallenge = $this->prophesize(AuthorizationChallenge::class); + + $solver = new GandiSolver($mockExtractor->reveal(), $mockClient->reveal()); + + $stubChallenge->getType()->willReturn($typeDns); + $this->assertTrue($solver->supports($stubChallenge->reveal())); + + $stubChallenge->getType()->willReturn($typeHttp); + $this->assertFalse($solver->supports($stubChallenge->reveal())); + } + + public function testSolve() + { + $domain = 'sub-domain.bar.com'; + $recordName = '_acme-challenge.sub-domain.bar.com.'; + $recordValue = 'record_value'; + + $mockExtractor = $this->prophesize(DnsDataExtractor::class); + $mockClient = $this->prophesize(ClientInterface::class); + $stubChallenge = $this->prophesize(AuthorizationChallenge::class); + + $solver = new GandiSolver($mockExtractor->reveal(), $mockClient->reveal()); + + $mockExtractor->getRecordName($stubChallenge->reveal())->willReturn($recordName); + $mockExtractor->getRecordValue($stubChallenge->reveal())->willReturn($recordValue); + $stubChallenge->getDomain()->willReturn($domain); + + $mockClient->request( + 'PUT', + 'https://dns.api.gandi.net/api/v5/domains/bar.com/records/_acme-challenge.sub-domain/TXT', + [ + 'headers' => [ + 'X-Api-Key' => 'stub', + ], + 'json' => [ + 'rrset_type' => 'TXT', + 'rrset_ttl' => 600, + 'rrset_name' => '_acme-challenge.sub-domain', + 'rrset_values' => ['record_value'] + ] + ] + )->shouldBeCalled(); + + $solver->configure(['api_key' => 'stub']); + $solver->solve($stubChallenge->reveal()); + } + + public function testCleanup() + { + $domain = 'sub-domain.bar.com'; + $recordName = '_acme-challenge.sub-domain.bar.com.'; + $recordValue = 'record_value'; + + $mockExtractor = $this->prophesize(DnsDataExtractor::class); + $mockClient = $this->prophesize(ClientInterface::class); + $stubChallenge = $this->prophesize(AuthorizationChallenge::class); + + $solver = new GandiSolver($mockExtractor->reveal(), $mockClient->reveal()); + + $mockExtractor->getRecordName($stubChallenge->reveal())->willReturn($recordName); + $mockExtractor->getRecordValue($stubChallenge->reveal())->willReturn($recordValue); + $stubChallenge->getDomain()->willReturn($domain); + + $mockClient->request( + 'DELETE', + 'https://dns.api.gandi.net/api/v5/domains/bar.com/records/_acme-challenge.sub-domain/TXT', + [ + 'headers' => [ + 'X-Api-Key' => 'stub', + ], + ] + )->shouldBeCalled(); + + $solver->configure(['api_key' => 'stub']); + $solver->cleanup($stubChallenge->reveal()); + } +} From 62d9b1b8b8230d1e0fe09ba94919185d7896385d Mon Sep 17 00:00:00 2001 From: Alexander Obuhovich Date: Thu, 18 Jul 2019 20:28:44 +0300 Subject: [PATCH 003/126] CS fixes --- src/Core/Challenge/Dns/GandiSolver.php | 15 +++++++-------- tests/Core/Challenge/Dns/GandiSolverTest.php | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index 2072e985..f6276aaa 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -50,7 +50,7 @@ class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServ /** * @param DnsDataExtractor $extractor - * @param ClientInterface $client + * @param ClientInterface $client */ public function __construct( DnsDataExtractor $extractor = null, @@ -99,11 +99,11 @@ public function solveAll(array $authorizationChallenges) $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); - $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); $this->client->request( 'PUT', - 'https://dns.api.gandi.net/api/v5/domains/' . $topLevelDomain . '/records/' . $subDomain . '/TXT', + 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', [ 'headers' => [ 'X-Api-Key' => $this->apiKey, @@ -112,8 +112,8 @@ public function solveAll(array $authorizationChallenges) 'rrset_type' => 'TXT', 'rrset_ttl' => 600, 'rrset_name' => $subDomain, - 'rrset_values' => [$recordValue] - ] + 'rrset_values' => [$recordValue], + ], ] ); @@ -140,11 +140,11 @@ public function cleanupAll(array $authorizationChallenges) $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); $recordName = $this->extractor->getRecordName($authorizationChallenge); - $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); $this->client->request( 'DELETE', - 'https://dns.api.gandi.net/api/v5/domains/' . $topLevelDomain . '/records/' . $subDomain . '/TXT', + 'https://dns.api.gandi.net/api/v5/domains/'.$topLevelDomain.'/records/'.$subDomain.'/TXT', [ 'headers' => [ 'X-Api-Key' => $this->apiKey, @@ -164,7 +164,6 @@ protected function getTopLevelDomain($domain) return \implode('.', \array_slice(\explode('.', $domain), -2)); } - /** * @param AuthorizationChallenge[] $authorizationChallenges * diff --git a/tests/Core/Challenge/Dns/GandiSolverTest.php b/tests/Core/Challenge/Dns/GandiSolverTest.php index 67df188b..19915113 100644 --- a/tests/Core/Challenge/Dns/GandiSolverTest.php +++ b/tests/Core/Challenge/Dns/GandiSolverTest.php @@ -64,8 +64,8 @@ public function testSolve() 'rrset_type' => 'TXT', 'rrset_ttl' => 600, 'rrset_name' => '_acme-challenge.sub-domain', - 'rrset_values' => ['record_value'] - ] + 'rrset_values' => ['record_value'], + ], ] )->shouldBeCalled(); From 83e47d34031eac63e3fd5be5312e84b0f8bb798a Mon Sep 17 00:00:00 2001 From: Alexander Obuhovich Date: Fri, 19 Jul 2019 08:28:07 +0300 Subject: [PATCH 004/126] Removed unused code --- src/Core/Challenge/Dns/GandiSolver.php | 32 -------------------------- 1 file changed, 32 deletions(-) diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index f6276aaa..f1e98a78 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -116,8 +116,6 @@ public function solveAll(array $authorizationChallenges) ], ] ); - - // TODO: Validate that record was set. } } @@ -163,34 +161,4 @@ protected function getTopLevelDomain($domain) { return \implode('.', \array_slice(\explode('.', $domain), -2)); } - - /** - * @param AuthorizationChallenge[] $authorizationChallenges - * - * @return AuthorizationChallenge[][] - */ - private function groupAuthorizationChallengesPerDomain(array $authorizationChallenges) - { - $groups = []; - foreach ($authorizationChallenges as $authorizationChallenge) { - $groups[$authorizationChallenge->getDomain()][] = $authorizationChallenge; - } - - return $groups; - } - - /** - * @param AuthorizationChallenge[] $authorizationChallenges - * - * @return AuthorizationChallenge[][] - */ - private function groupAuthorizationChallengesPerRecordName(array $authorizationChallenges) - { - $groups = []; - foreach ($authorizationChallenges as $authorizationChallenge) { - $groups[$this->extractor->getRecordName($authorizationChallenge)][] = $authorizationChallenge; - } - - return $groups; - } } From c08bb1f019abc6f0394aee057b70ac971e010742 Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Sun, 28 Jul 2019 20:41:07 +0800 Subject: [PATCH 005/126] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ef309996..d422eb1b 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "acmephp/acmephp", + "name": "trustocean/acmephp", "description": "Let's Encrypt client written in PHP", "type": "project", "license": "MIT", From ca5a355c59d5d90d46c228e530926cefbce391ec Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 00:52:57 +0800 Subject: [PATCH 006/126] enhancement --- src/Cli/Command/AuthorizeCommand.php | 51 +++++++++++++++- .../Command/Helper/KeyOptionCommandTrait.php | 58 +++++++++++++++++++ src/Cli/Command/RequestCommand.php | 56 ------------------ src/Core/AcmeClient.php | 8 ++- src/Core/AcmeClientV2Interface.php | 2 +- 5 files changed, 116 insertions(+), 59 deletions(-) diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 27ceb038..5bfeb2b6 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -19,12 +19,21 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use AcmePhp\Cli\Command\Helper\KeyOptionCommandTrait; +use AcmePhp\Ssl\CertificateRequest; /** * @author Titouan Galopin */ class AuthorizeCommand extends AbstractCommand { + use KeyOptionCommandTrait; + + /** + * @var RepositoryInterface + */ + private $repository; + /** * {@inheritdoc} */ @@ -34,6 +43,14 @@ protected function configure() ->setDefinition([ new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53)', 'http'), new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'List of domains to ask an authorization for'), + new InputOption('country', null, InputOption::VALUE_REQUIRED, 'Your country two-letters code (field "C" of the distinguished name, for instance: "US")'), + new InputOption('province', null, InputOption::VALUE_REQUIRED, 'Your country province (field "ST" of the distinguished name, for instance: "California")'), + new InputOption('locality', null, InputOption::VALUE_REQUIRED, 'Your locality (field "L" of the distinguished name, for instance: "Mountain View")'), + new InputOption('organization', null, InputOption::VALUE_REQUIRED, 'Your organization/company (field "O" of the distinguished name, for instance: "Acme PHP")'), + new InputOption('unit', null, InputOption::VALUE_REQUIRED, 'Your unit/department in your organization (field "OU" of the distinguished name, for instance: "Sales")'), + new InputOption('email', null, InputOption::VALUE_REQUIRED, 'Your e-mail address (field "E" of the distinguished name)'), + new InputOption('alternative-name', 'a', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Alternative domains for this certificate'), + new InputOption('key-type', 'k', InputOption::VALUE_REQUIRED, 'The type of private key used to sign certificates (one of RSA, EC)', 'RSA'), ]) ->setDescription('Ask the ACME server for an authorization token to check you are the owner of a domain') ->setHelp(<<<'EOF' @@ -56,8 +73,11 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->repository = $this->getRepository(); + $client = $this->getClient(); $domains = $input->getArgument('domains'); + $keyType = $input->getOption('key-type'); $solverName = strtolower($input->getOption('solver')); @@ -68,8 +88,37 @@ protected function execute(InputInterface $input, OutputInterface $output) $solver = $solverLocator->get($solverName); $this->debug('Solver found', ['name' => $solverName]); + $alternativeNames = $domains; + $domain = $alternativeNames[0]; + sort($alternativeNames); + + $introduction = <<<'EOF' + +There is currently no certificate for domain %s in the Acme PHP storage. As it is the +first time you request a certificate for this domain, some configuration is required. + +Generating domain key pair... +EOF; + + $this->info(sprintf($introduction, $domain)); + + /* @var KeyPair $domainKeyPair */ + $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair( + $this->createKeyOption($keyType) + ); + $this->repository->storeDomainKeyPair($domain, $domainKeyPair); + + $this->debug('Domain key pair generated and stored', [ + 'domain' => $domain, + 'public_key' => $domainKeyPair->getPublicKey()->getPEM(), + ]); + $distinguishedName = $this->getOrCreateDistinguishedName($domain, $alternativeNames); + $this->notice('Distinguished name informations have been stored locally for this domain (they won\'t be asked on renewal).'); + $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); + $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + $this->notice(sprintf('Requesting an authorization token for domains %s ...', implode(', ', $domains))); - $order = $client->requestOrder($domains); + $order = $client->requestOrder($domains, $csr); $this->notice('The authorization tokens was successfully fetched!'); $authorizationChallengesToSolve = []; foreach ($order->getAuthorizationsChallenges() as $domainKey => $authorizationChallenges) { diff --git a/src/Cli/Command/Helper/KeyOptionCommandTrait.php b/src/Cli/Command/Helper/KeyOptionCommandTrait.php index 3a2edc31..175813b4 100644 --- a/src/Cli/Command/Helper/KeyOptionCommandTrait.php +++ b/src/Cli/Command/Helper/KeyOptionCommandTrait.php @@ -13,6 +13,7 @@ use AcmePhp\Ssl\Generator\EcKey\EcKeyOption; use AcmePhp\Ssl\Generator\RsaKey\RsaKeyOption; +use AcmePhp\Ssl\DistinguishedName; /** * @author Jérémy Derussé @@ -30,4 +31,61 @@ private function createKeyOption($keyType) throw new \InvalidArgumentException(sprintf('The keyType "%s" is not valid. Supported types are: RSA, EC', strtoupper($keyType))); } } + + + /** + * Retrieve the stored distinguishedName or create a new one if needed. + * + * @param string $domain + * @param array $alternativeNames + * + * @return DistinguishedName + */ + private function getOrCreateDistinguishedName($domain, array $alternativeNames) + { + if ($this->repository->hasDomainDistinguishedName($domain)) { + $original = $this->repository->loadDomainDistinguishedName($domain); + + $distinguishedName = new DistinguishedName( + $domain, + $this->input->getOption('country') ?: $original->getCountryName(), + $this->input->getOption('province') ?: $original->getStateOrProvinceName(), + $this->input->getOption('locality') ?: $original->getLocalityName(), + $this->input->getOption('organization') ?: $original->getOrganizationName(), + $this->input->getOption('unit') ?: $original->getOrganizationalUnitName(), + $this->input->getOption('email') ?: $original->getEmailAddress(), + $alternativeNames + ); + } else { + // Ask DistinguishedName + $distinguishedName = new DistinguishedName( + $domain, + $this->input->getOption('country'), + $this->input->getOption('province'), + $this->input->getOption('locality'), + $this->input->getOption('organization'), + $this->input->getOption('unit'), + $this->input->getOption('email'), + $alternativeNames + ); + + /** @var DistinguishedNameHelper $helper */ + $helper = $this->getHelper('distinguished_name'); + + if (!$helper->isReadyForRequest($distinguishedName)) { + $this->info("\n\nSome informations about you or your company are required for the certificate:\n"); + + $distinguishedName = $helper->ask( + $this->getHelper('question'), + $this->input, + $this->output, + $distinguishedName + ); + } + } + + $this->repository->storeDomainDistinguishedName($domain, $distinguishedName); + + return $distinguishedName; + } } diff --git a/src/Cli/Command/RequestCommand.php b/src/Cli/Command/RequestCommand.php index bf19c901..7e0d10a1 100644 --- a/src/Cli/Command/RequestCommand.php +++ b/src/Cli/Command/RequestCommand.php @@ -342,60 +342,4 @@ private function executeRenewal($domain, array $alternativeNames) throw $e; } } - - /** - * Retrieve the stored distinguishedName or create a new one if needed. - * - * @param string $domain - * @param array $alternativeNames - * - * @return DistinguishedName - */ - private function getOrCreateDistinguishedName($domain, array $alternativeNames) - { - if ($this->repository->hasDomainDistinguishedName($domain)) { - $original = $this->repository->loadDomainDistinguishedName($domain); - - $distinguishedName = new DistinguishedName( - $domain, - $this->input->getOption('country') ?: $original->getCountryName(), - $this->input->getOption('province') ?: $original->getStateOrProvinceName(), - $this->input->getOption('locality') ?: $original->getLocalityName(), - $this->input->getOption('organization') ?: $original->getOrganizationName(), - $this->input->getOption('unit') ?: $original->getOrganizationalUnitName(), - $this->input->getOption('email') ?: $original->getEmailAddress(), - $alternativeNames - ); - } else { - // Ask DistinguishedName - $distinguishedName = new DistinguishedName( - $domain, - $this->input->getOption('country'), - $this->input->getOption('province'), - $this->input->getOption('locality'), - $this->input->getOption('organization'), - $this->input->getOption('unit'), - $this->input->getOption('email'), - $alternativeNames - ); - - /** @var DistinguishedNameHelper $helper */ - $helper = $this->getHelper('distinguished_name'); - - if (!$helper->isReadyForRequest($distinguishedName)) { - $this->info("\n\nSome informations about you or your company are required for the certificate:\n"); - - $distinguishedName = $helper->ask( - $this->getHelper('question'), - $this->input, - $this->output, - $distinguishedName - ); - } - } - - $this->repository->storeDomainDistinguishedName($domain, $distinguishedName); - - return $distinguishedName; - } } diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 118c11c2..a3ab6580 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -131,10 +131,15 @@ public function requestAuthorization($domain) /** * {@inheritdoc} */ - public function requestOrder(array $domains) + public function requestOrder(array $domains, $csr = null) { Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s'); + $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; + $csrContent = $this->csrSigner->signCertificateRequest($csr); + $csrContent = trim(str_replace($humanText, '', $csrContent)); + $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + $payload = [ 'identifiers' => array_map( function ($domain) { @@ -145,6 +150,7 @@ function ($domain) { }, array_values($domains) ), + 'csr' => $csrContent, ]; $response = $this->getHttpClient()->signedKidRequest('POST', $this->getResourceUrl(ResourcesDirectory::NEW_ORDER), $this->getResourceAccount(), $payload); diff --git a/src/Core/AcmeClientV2Interface.php b/src/Core/AcmeClientV2Interface.php index c1f4f75d..42eba089 100644 --- a/src/Core/AcmeClientV2Interface.php +++ b/src/Core/AcmeClientV2Interface.php @@ -44,7 +44,7 @@ interface AcmeClientV2Interface extends AcmeClientInterface * * @return CertificateOrder the Order returned by the Certificate Authority */ - public function requestOrder(array $domains); + public function requestOrder(array $domains, $csr = null); /** * Request a certificate for the given domain. From 9b572c9eead821f0b62aadda66019d28647fa85a Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 01:07:21 +0800 Subject: [PATCH 007/126] fix --- src/Core/AcmeClient.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index a3ab6580..5050a0b6 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -137,6 +137,36 @@ public function requestOrder(array $domains, $csr = null) $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; $csrContent = $this->csrSigner->signCertificateRequest($csr); + $csrContent = '-----BEGIN CERTIFICATE REQUEST----- +MIIFMTCCAxkCAQAwgZQxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTER +MA8GA1UEBwwIU2hhbmdoYWkxDTALBgNVBAoMBElUNjgxDTALBgNVBAsMBElUNjgx +GDAWBgNVBAMMD3d3dy5pdDY4LmNvbS5jbjEnMCUGCSqGSIb3DQEJARYYeGlhb2h1 +aS5sYW1AZS5oZXhkYXRhLmNuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAyXK6RJP6l0oOMKAFaommyfZJO6/RmHTs6i2PMw+OleiAFNAhPGUmQG+yICBn +rHKdIannniUqtp1LurlfGUKlhYiA67Pqu0inXNdbA2yGeNtQl8tz4OnGq8kQcElX +rCahFApXC9AYUBEoHm2q6fJr93Y/E+nPRNN/a0+c79SIrGZi9oYswZnITL4jFZmC +bhcPln+U6/qCiTDUDdcaF2vXz7h4kKoLfrdJHFtj/nUwY1VIzB9S6Ded9DIaMQp+ +WEFcqbWpfRKDwxSWEc6jp6of7+PAeIIY56SVkfurAHJ0/WENx/ToyjgodiiWj0YU +fRWacxGV7rAOKXWCXCDxtV0scYxuE10J2R6qtW0Iu89db7YkJwXhCpYyq9tDChvU +EbmoYCPxBUs0o58U0Rz6ljbICIGv8Ah/ULtX006C/GGrpwB3tyL4daZN66k5heSm +uCGfpf8Oc+aHoT2vzxOGNZ1TlCTkdgHS12BiP+V2iRjuaLxg3TCgRdTTAIOOoVZ3 +NpWTN1ovBvKFpcTznJMUue1eQKVTQSkk7Zuj4U9FohDOnMjFduTRgDGS6SxgD60M +W6s4r/iJ3ayqUMLZXGsUxliPzFi7hk9k1fq3Byd30FDvvY0pBDkxI2aPeWiOqfrR +20hvj9A+nYjYFtFVjSz8XqhK0hjAds4Q3PW/kg7Ad0QWisECAwEAAaBXMFUGCSqG +SIb3DQEJDjFIMEYwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLAYDVR0RBCUwI4IP +d3d3Lml0NjguY29tLmNughB3d3cyLml0NjguY29tLmNuMA0GCSqGSIb3DQEBCwUA +A4ICAQC+x0qMxs22NpLjJkqY/9AZVKC5i2PudbeYQzV0H/KJwU5YBWwjL+wQxPJ1 +NBx4PAtJ5Ka9VK63J8S6rGVY7x46AbgEPHlKmU57ejxM3pcS+AA8aoEfO/eudw3s +uIKDKQJBJ9j71u7J3KiarHTDSQtjuQkiDgcmREb7kMqW+rfFsUk5NGm8OAZ+E+ib +uUqFcEBH751hTtvBLY/OZ+0xViUHwkWsfnFY8rwqV90sP9aihewIOyJ/AZaVHm0q +sQWgL0Up2AK9nknFBrPKMaPceljGkIw4l93MJnE5EJhZI3Ev2Kj39+mDelQw7suZ +XqUyluoFXu4OQzSXNF7mQujsfrR3GIqzRv6NytkGYIkY7rKRkM2EMoPulHzBT6fZ +LC1KaxNV//jWci5xVS0L2vdlvHOAOR+MnI7Nrimn7X6I1Sr7lnrHJvSkMBnUlmvZ +aJ/0IBddR6LMMefYbw/aaklwtuvhDRdwU8jbYok4CBEfW0jzSpIMNjrsbCNCgOgs +bCVQXNz9esiuabJ8iUwRHJi14hDHWbhrs8YsBkyOk468wD231IVDfU72p8t6Zb8Q +kMwBJMhvle21vrBjHMn+gQhoP1aHlUyEIhVS7G/nvAm5ahDY9GTNiLihBgCHzXaQ +oAeJpcfRxQIMUVhVHg1SLpzooWvb6mhSR6J7m8jzWOKLrmaYSw== +-----END CERTIFICATE REQUEST-----'; $csrContent = trim(str_replace($humanText, '', $csrContent)); $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); From 2e5997ae9182555482f60b11976e480a7a5fb082 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 01:17:50 +0800 Subject: [PATCH 008/126] done auth --- src/Core/AcmeClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 5050a0b6..5622d0d0 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -396,7 +396,7 @@ private function createAuthorizationChallenge($domain, array $response) $response['type'], $response['url'], $response['token'], - $response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint()) + isset($response['filecontent']) ? $response['filecontent'] : ($response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint())) ); } } From 85778e141aa91865b9f231c0b984944e60ed8eba Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 14:05:30 +0800 Subject: [PATCH 009/126] support csrEager --- src/Cli/Command/RunCommand.php | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 52ba77e6..87452242 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -96,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $certificate ); } else { - $order = $this->challengeDomains($domainConfig); + $order = $this->challengeDomains($domainConfig, $keyOption); $response = $this->requestCertificate($order, $domainConfig, $keyOption); } @@ -228,7 +228,7 @@ private function requestCertificate(CertificateOrder $order, $domainConfig, KeyO return $response; } - private function challengeDomains(array $domainConfig) + private function challengeDomains(array $domainConfig, KeyOption $keyOption) { $solverConfig = $domainConfig['solver']; $domain = $domainConfig['domain']; @@ -246,8 +246,36 @@ private function challengeDomains(array $domainConfig) $client = $this->getClient(); $domains = array_unique(array_merge([$domain], $domainConfig['subject_alternative_names'])); + + $domain = $domainConfig['domain']; + $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); + + $repository = $this->getRepository(); + $client = $this->getClient(); + $distinguishedName = new DistinguishedName( + $domainConfig['domain'], + $domainConfig['distinguished_name']['country'], + $domainConfig['distinguished_name']['state'], + $domainConfig['distinguished_name']['locality'], + $domainConfig['distinguished_name']['organization_name'], + $domainConfig['distinguished_name']['organization_unit_name'], + $domainConfig['distinguished_name']['email_address'], + $domainConfig['subject_alternative_names'] + ); + + if ($repository->hasDomainKeyPair($domain)) { + $domainKeyPair = $repository->loadDomainKeyPair($domain); + } else { + $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair($keyOption); + $repository->storeDomainKeyPair($domain, $domainKeyPair); + } + + $repository->storeDomainDistinguishedName($domain, $distinguishedName); + + $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + $this->output->writeln('Requesting certificate order...'); - $order = $client->requestOrder($domains); + $order = $client->requestOrder($domains, $csr); $authorizationChallengesToSolve = []; foreach ($order->getAuthorizationsChallenges() as $domain => $authorizationChallenges) { From 5a63b88bc999332a7fcdeba76d561ad45f91fac5 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 15:06:09 +0800 Subject: [PATCH 010/126] update --- src/Core/Challenge/Http/FilesystemSolver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Http/FilesystemSolver.php b/src/Core/Challenge/Http/FilesystemSolver.php index 72652f46..5dbd9e0a 100644 --- a/src/Core/Challenge/Http/FilesystemSolver.php +++ b/src/Core/Challenge/Http/FilesystemSolver.php @@ -89,6 +89,7 @@ public function cleanup(AuthorizationChallenge $authorizationChallenge) { $checkPath = $this->extractor->getCheckPath($authorizationChallenge); - $this->filesystem->delete($checkPath); + // @TODO: trustocean can't delete. because it requires async to verify + //$this->filesystem->delete($checkPath); } } From e75810ecfe3b9beb35fbd7e2ee3a26f84a6810b2 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 15:11:13 +0800 Subject: [PATCH 011/126] trustocean can't delete. because it requires async to verify --- src/Core/AcmeClient.php | 30 ------------------- .../Filesystem/Adapter/FlysystemAdapter.php | 2 ++ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 5622d0d0..4cd3a383 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -137,36 +137,6 @@ public function requestOrder(array $domains, $csr = null) $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; $csrContent = $this->csrSigner->signCertificateRequest($csr); - $csrContent = '-----BEGIN CERTIFICATE REQUEST----- -MIIFMTCCAxkCAQAwgZQxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTER -MA8GA1UEBwwIU2hhbmdoYWkxDTALBgNVBAoMBElUNjgxDTALBgNVBAsMBElUNjgx -GDAWBgNVBAMMD3d3dy5pdDY4LmNvbS5jbjEnMCUGCSqGSIb3DQEJARYYeGlhb2h1 -aS5sYW1AZS5oZXhkYXRhLmNuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC -AgEAyXK6RJP6l0oOMKAFaommyfZJO6/RmHTs6i2PMw+OleiAFNAhPGUmQG+yICBn -rHKdIannniUqtp1LurlfGUKlhYiA67Pqu0inXNdbA2yGeNtQl8tz4OnGq8kQcElX -rCahFApXC9AYUBEoHm2q6fJr93Y/E+nPRNN/a0+c79SIrGZi9oYswZnITL4jFZmC -bhcPln+U6/qCiTDUDdcaF2vXz7h4kKoLfrdJHFtj/nUwY1VIzB9S6Ded9DIaMQp+ -WEFcqbWpfRKDwxSWEc6jp6of7+PAeIIY56SVkfurAHJ0/WENx/ToyjgodiiWj0YU -fRWacxGV7rAOKXWCXCDxtV0scYxuE10J2R6qtW0Iu89db7YkJwXhCpYyq9tDChvU -EbmoYCPxBUs0o58U0Rz6ljbICIGv8Ah/ULtX006C/GGrpwB3tyL4daZN66k5heSm -uCGfpf8Oc+aHoT2vzxOGNZ1TlCTkdgHS12BiP+V2iRjuaLxg3TCgRdTTAIOOoVZ3 -NpWTN1ovBvKFpcTznJMUue1eQKVTQSkk7Zuj4U9FohDOnMjFduTRgDGS6SxgD60M -W6s4r/iJ3ayqUMLZXGsUxliPzFi7hk9k1fq3Byd30FDvvY0pBDkxI2aPeWiOqfrR -20hvj9A+nYjYFtFVjSz8XqhK0hjAds4Q3PW/kg7Ad0QWisECAwEAAaBXMFUGCSqG -SIb3DQEJDjFIMEYwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLAYDVR0RBCUwI4IP -d3d3Lml0NjguY29tLmNughB3d3cyLml0NjguY29tLmNuMA0GCSqGSIb3DQEBCwUA -A4ICAQC+x0qMxs22NpLjJkqY/9AZVKC5i2PudbeYQzV0H/KJwU5YBWwjL+wQxPJ1 -NBx4PAtJ5Ka9VK63J8S6rGVY7x46AbgEPHlKmU57ejxM3pcS+AA8aoEfO/eudw3s -uIKDKQJBJ9j71u7J3KiarHTDSQtjuQkiDgcmREb7kMqW+rfFsUk5NGm8OAZ+E+ib -uUqFcEBH751hTtvBLY/OZ+0xViUHwkWsfnFY8rwqV90sP9aihewIOyJ/AZaVHm0q -sQWgL0Up2AK9nknFBrPKMaPceljGkIw4l93MJnE5EJhZI3Ev2Kj39+mDelQw7suZ -XqUyluoFXu4OQzSXNF7mQujsfrR3GIqzRv6NytkGYIkY7rKRkM2EMoPulHzBT6fZ -LC1KaxNV//jWci5xVS0L2vdlvHOAOR+MnI7Nrimn7X6I1Sr7lnrHJvSkMBnUlmvZ -aJ/0IBddR6LMMefYbw/aaklwtuvhDRdwU8jbYok4CBEfW0jzSpIMNjrsbCNCgOgs -bCVQXNz9esiuabJ8iUwRHJi14hDHWbhrs8YsBkyOk468wD231IVDfU72p8t6Zb8Q -kMwBJMhvle21vrBjHMn+gQhoP1aHlUyEIhVS7G/nvAm5ahDY9GTNiLihBgCHzXaQ -oAeJpcfRxQIMUVhVHg1SLpzooWvb6mhSR6J7m8jzWOKLrmaYSw== ------END CERTIFICATE REQUEST-----'; $csrContent = trim(str_replace($humanText, '', $csrContent)); $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); diff --git a/src/Core/Filesystem/Adapter/FlysystemAdapter.php b/src/Core/Filesystem/Adapter/FlysystemAdapter.php index a61cf0b0..2ae329ea 100644 --- a/src/Core/Filesystem/Adapter/FlysystemAdapter.php +++ b/src/Core/Filesystem/Adapter/FlysystemAdapter.php @@ -39,6 +39,8 @@ public function write($path, $content) public function delete($path) { + // @TODO: trustocean can't delete. because it requires async to verify + return; $isOnRemote = $this->filesystem->has($path); if ($isOnRemote && !$this->filesystem->delete($path)) { throw $this->createRuntimeException($path, 'delete'); From 24491ec689aaa6b930c5e319f3738502d8c97dc5 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 15:27:02 +0800 Subject: [PATCH 012/126] support rfc8555 csrEager --- src/Core/Protocol/AuthorizationChallenge.php | 50 +++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index 887a5e2f..f2b08476 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -50,6 +50,21 @@ class AuthorizationChallenge */ private $payload; + /** + * @var string|null + */ + private $path; + + /** + * @var string|null + */ + private $verifyurl; + + /** + * @var string|null + */ + private $filecontent; + /** * @param string $domain * @param string $status @@ -57,8 +72,11 @@ class AuthorizationChallenge * @param string $url * @param string $token * @param string $payload + * @param string $path + * @param string $verifyurl + * @param string $filecontent */ - public function __construct($domain, $status, $type, $url, $token, $payload) + public function __construct($domain, $status, $type, $url, $token, $payload, $path = null, $verifyurl = null, $filecontent = null) { Assert::stringNotEmpty($domain, 'Challenge::$domain expected a non-empty string. Got: %s'); Assert::stringNotEmpty($status, 'Challenge::$status expected a non-empty string. Got: %s'); @@ -73,6 +91,9 @@ public function __construct($domain, $status, $type, $url, $token, $payload) $this->url = $url; $this->token = $token; $this->payload = $payload; + $this->path = $path; + $this->verifyurl = $verifyurl; + $this->filecontent = $filecontent; } /** @@ -87,6 +108,9 @@ public function toArray() 'url' => $this->getUrl(), 'token' => $this->getToken(), 'payload' => $this->getPayload(), + 'path' => $this->getPath(), + 'verifyurl' => $this->getVerifyurl(), + 'filecontent' => $this->getFilecontent(), ]; } @@ -170,4 +194,28 @@ public function getPayload() { return $this->payload; } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @return string + */ + public function getVerifyurl() + { + return $this->verifyurl; + } + + /** + * @return string + */ + public function getFilecontent() + { + return $this->filecontent; + } } From 9712e7eb81b2744728ebe96638221fb84f399691 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 16:03:09 +0800 Subject: [PATCH 013/126] fix --- src/Core/Challenge/Http/FilesystemSolver.php | 4 +--- src/Core/Filesystem/Adapter/FlysystemAdapter.php | 2 -- src/Core/Protocol/AuthorizationChallenge.php | 11 +++++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Core/Challenge/Http/FilesystemSolver.php b/src/Core/Challenge/Http/FilesystemSolver.php index 5dbd9e0a..e7abad1e 100644 --- a/src/Core/Challenge/Http/FilesystemSolver.php +++ b/src/Core/Challenge/Http/FilesystemSolver.php @@ -88,8 +88,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) public function cleanup(AuthorizationChallenge $authorizationChallenge) { $checkPath = $this->extractor->getCheckPath($authorizationChallenge); - - // @TODO: trustocean can't delete. because it requires async to verify - //$this->filesystem->delete($checkPath); + $this->filesystem->delete($checkPath); } } diff --git a/src/Core/Filesystem/Adapter/FlysystemAdapter.php b/src/Core/Filesystem/Adapter/FlysystemAdapter.php index 2ae329ea..a61cf0b0 100644 --- a/src/Core/Filesystem/Adapter/FlysystemAdapter.php +++ b/src/Core/Filesystem/Adapter/FlysystemAdapter.php @@ -39,8 +39,6 @@ public function write($path, $content) public function delete($path) { - // @TODO: trustocean can't delete. because it requires async to verify - return; $isOnRemote = $this->filesystem->has($path); if ($isOnRemote && !$this->filesystem->delete($path)) { throw $this->createRuntimeException($path, 'delete'); diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index f2b08476..deb25a01 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -127,7 +127,10 @@ public static function fromArray(array $data) $data['type'], $data['url'], $data['token'], - $data['payload'] + $data['payload'], + isset($data['path']) ? $data['path'] : null, + isset($data['verifyurl']) ? $data['verifyurl'] : null, + isset($data['filecontent']) ? $data['filecontent'] : null ); } @@ -196,7 +199,7 @@ public function getPayload() } /** - * @return string + * @return string|null */ public function getPath() { @@ -204,7 +207,7 @@ public function getPath() } /** - * @return string + * @return string|null */ public function getVerifyurl() { @@ -212,7 +215,7 @@ public function getVerifyurl() } /** - * @return string + * @return string|null */ public function getFilecontent() { From 9bb6dd4e607eb2daf517abdec3ba1e9782abce3a Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 16:13:52 +0800 Subject: [PATCH 014/126] =?UTF-8?q?=E6=9A=82=E6=97=B6=E5=B1=8F=E8=94=BD?= =?UTF-8?q?=E5=88=A0=E9=99=A4=EF=BC=8C=E5=9B=A0=E4=B8=BAcomodoca=E6=98=AF?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Http/FilesystemSolver.php | 2 +- src/Core/Challenge/Http/HttpDataExtractor.php | 2 +- src/Core/Filesystem/Adapter/FlysystemAdapter.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/Challenge/Http/FilesystemSolver.php b/src/Core/Challenge/Http/FilesystemSolver.php index e7abad1e..47c3a25d 100644 --- a/src/Core/Challenge/Http/FilesystemSolver.php +++ b/src/Core/Challenge/Http/FilesystemSolver.php @@ -88,6 +88,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) public function cleanup(AuthorizationChallenge $authorizationChallenge) { $checkPath = $this->extractor->getCheckPath($authorizationChallenge); - $this->filesystem->delete($checkPath); + //$this->filesystem->delete($checkPath); } } diff --git a/src/Core/Challenge/Http/HttpDataExtractor.php b/src/Core/Challenge/Http/HttpDataExtractor.php index 562da7d8..468108b2 100644 --- a/src/Core/Challenge/Http/HttpDataExtractor.php +++ b/src/Core/Challenge/Http/HttpDataExtractor.php @@ -46,7 +46,7 @@ public function getCheckUrl(AuthorizationChallenge $authorizationChallenge) public function getCheckPath(AuthorizationChallenge $authorizationChallenge) { return sprintf( - '/.well-known/acme-challenge/%s', + $authorizationChallenge->getPath() ? ($authorizationChallenge->getPath() . '%s') : '/.well-known/acme-challenge/%s', $authorizationChallenge->getToken() ); } diff --git a/src/Core/Filesystem/Adapter/FlysystemAdapter.php b/src/Core/Filesystem/Adapter/FlysystemAdapter.php index a61cf0b0..f55c855d 100644 --- a/src/Core/Filesystem/Adapter/FlysystemAdapter.php +++ b/src/Core/Filesystem/Adapter/FlysystemAdapter.php @@ -39,6 +39,7 @@ public function write($path, $content) public function delete($path) { + return; $isOnRemote = $this->filesystem->has($path); if ($isOnRemote && !$this->filesystem->delete($path)) { throw $this->createRuntimeException($path, 'delete'); From 126ebb8e4b6f6d95a304f880dc45c124fceb56fe Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 17:33:37 +0800 Subject: [PATCH 015/126] csrEager --- src/Core/AcmeClient.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 4cd3a383..435d98c5 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -366,7 +366,10 @@ private function createAuthorizationChallenge($domain, array $response) $response['type'], $response['url'], $response['token'], - isset($response['filecontent']) ? $response['filecontent'] : ($response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint())) + isset($response['filecontent']) ? $response['filecontent'] : ($response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint())), + isset($response['path']) ? $response['path'] : null, + isset($response['verifyurl']) ? $response['verifyurl'] : null, + isset($response['filecontent']) ? $response['filecontent'] : null ); } } From 9fe2f209cf449f655b4daf94784d157bed94c5d5 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 30 Jul 2019 22:54:02 +0800 Subject: [PATCH 016/126] fixbug #1, implement csrEager --- src/Cli/Command/AuthorizeCommand.php | 30 +++---- src/Cli/Command/RunCommand.php | 98 +++++++++++----------- src/Cli/Repository/Repository.php | 12 +-- src/Cli/Repository/RepositoryInterface.php | 3 +- src/Core/AcmeClient.php | 40 ++++++--- src/Core/AcmeClientV2Interface.php | 4 +- src/Core/Protocol/ResourcesDirectory.php | 16 ++++ src/Ssl/CertificateResponse.php | 6 +- 8 files changed, 127 insertions(+), 82 deletions(-) diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 5bfeb2b6..481f0461 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -102,21 +102,23 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->info(sprintf($introduction, $domain)); - /* @var KeyPair $domainKeyPair */ - $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair( - $this->createKeyOption($keyType) - ); - $this->repository->storeDomainKeyPair($domain, $domainKeyPair); - - $this->debug('Domain key pair generated and stored', [ - 'domain' => $domain, - 'public_key' => $domainKeyPair->getPublicKey()->getPEM(), - ]); - $distinguishedName = $this->getOrCreateDistinguishedName($domain, $alternativeNames); - $this->notice('Distinguished name informations have been stored locally for this domain (they won\'t be asked on renewal).'); - $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); - $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + $csr = null; + if ($this->getClient()->isCsrEager()) { + /* @var KeyPair $domainKeyPair */ + $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair( + $this->createKeyOption($keyType) + ); + $this->repository->storeDomainKeyPair($domain, $domainKeyPair); + $this->debug('Domain key pair generated and stored', [ + 'domain' => $domain, + 'public_key' => $domainKeyPair->getPublicKey()->getPEM(), + ]); + $distinguishedName = $this->getOrCreateDistinguishedName($domain, $alternativeNames); + $this->notice('Distinguished name informations have been stored locally for this domain (they won\'t be asked on renewal).'); + $this->notice(sprintf('Loading the order related to the domains %s ...', implode(', ', $domains))); + $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + } $this->notice(sprintf('Requesting an authorization token for domains %s ...', implode(', ', $domains))); $order = $client->requestOrder($domains, $csr); $this->notice('The authorization tokens was successfully fetched!'); diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 87452242..c85249b7 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -100,7 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $response = $this->requestCertificate($order, $domainConfig, $keyOption); } - $this->installCertificate($response, $domainConfig['install']); + $this->installCertificate($domain, $response, $domainConfig['install']); } } @@ -134,12 +134,12 @@ private function register($email, KeyOption $keyOption) } } - private function installCertificate(CertificateResponse $response, array $actions) + private function installCertificate($domain, CertificateResponse $response, array $actions) { $this->output->writeln( sprintf( 'Installing certificate for domain %s...', - $response->getCertificateRequest()->getDistinguishedName()->getCommonName() + $domain ) ); @@ -198,32 +198,35 @@ private function requestCertificate(CertificateOrder $order, $domainConfig, KeyO $repository = $this->getRepository(); $client = $this->getClient(); - $distinguishedName = new DistinguishedName( - $domainConfig['domain'], - $domainConfig['distinguished_name']['country'], - $domainConfig['distinguished_name']['state'], - $domainConfig['distinguished_name']['locality'], - $domainConfig['distinguished_name']['organization_name'], - $domainConfig['distinguished_name']['organization_unit_name'], - $domainConfig['distinguished_name']['email_address'], - $domainConfig['subject_alternative_names'] - ); + $csr = null; + if (!$client->isCsrEager()) { + $distinguishedName = new DistinguishedName( + $domainConfig['domain'], + $domainConfig['distinguished_name']['country'], + $domainConfig['distinguished_name']['state'], + $domainConfig['distinguished_name']['locality'], + $domainConfig['distinguished_name']['organization_name'], + $domainConfig['distinguished_name']['organization_unit_name'], + $domainConfig['distinguished_name']['email_address'], + $domainConfig['subject_alternative_names'] + ); - if ($repository->hasDomainKeyPair($domain)) { - $domainKeyPair = $repository->loadDomainKeyPair($domain); - } else { - $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair($keyOption); - $repository->storeDomainKeyPair($domain, $domainKeyPair); - } + if ($repository->hasDomainKeyPair($domain)) { + $domainKeyPair = $repository->loadDomainKeyPair($domain); + } else { + $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair($keyOption); + $repository->storeDomainKeyPair($domain, $domainKeyPair); + } - $repository->storeDomainDistinguishedName($domain, $distinguishedName); + $repository->storeDomainDistinguishedName($domain, $distinguishedName); - $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + } $response = $client->finalizeOrder($order, $csr); $this->output->writeln('Certificate requested successfully!'); - $repository->storeCertificateResponse($response); + $repository->storeCertificateResponse($domain, $response); return $response; } @@ -246,35 +249,36 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption) $client = $this->getClient(); $domains = array_unique(array_merge([$domain], $domainConfig['subject_alternative_names'])); + $csr = null; + if ($client->isCsrEager()) { + $domain = $domainConfig['domain']; + $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); + + $repository = $this->getRepository(); + $distinguishedName = new DistinguishedName( + $domainConfig['domain'], + $domainConfig['distinguished_name']['country'], + $domainConfig['distinguished_name']['state'], + $domainConfig['distinguished_name']['locality'], + $domainConfig['distinguished_name']['organization_name'], + $domainConfig['distinguished_name']['organization_unit_name'], + $domainConfig['distinguished_name']['email_address'], + $domainConfig['subject_alternative_names'] + ); - $domain = $domainConfig['domain']; - $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); - - $repository = $this->getRepository(); - $client = $this->getClient(); - $distinguishedName = new DistinguishedName( - $domainConfig['domain'], - $domainConfig['distinguished_name']['country'], - $domainConfig['distinguished_name']['state'], - $domainConfig['distinguished_name']['locality'], - $domainConfig['distinguished_name']['organization_name'], - $domainConfig['distinguished_name']['organization_unit_name'], - $domainConfig['distinguished_name']['email_address'], - $domainConfig['subject_alternative_names'] - ); - - if ($repository->hasDomainKeyPair($domain)) { - $domainKeyPair = $repository->loadDomainKeyPair($domain); - } else { - $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair($keyOption); - $repository->storeDomainKeyPair($domain, $domainKeyPair); - } + if ($repository->hasDomainKeyPair($domain)) { + $domainKeyPair = $repository->loadDomainKeyPair($domain); + } else { + $domainKeyPair = $this->getContainer()->get('ssl.key_pair_generator')->generateKeyPair($keyOption); + $repository->storeDomainKeyPair($domain, $domainKeyPair); + } - $repository->storeDomainDistinguishedName($domain, $distinguishedName); + $repository->storeDomainDistinguishedName($domain, $distinguishedName); - $csr = new CertificateRequest($distinguishedName, $domainKeyPair); + $csr = new CertificateRequest($distinguishedName, $domainKeyPair); - $this->output->writeln('Requesting certificate order...'); + $this->output->writeln('Requesting certificate order...'); + } $order = $client->requestOrder($domains, $csr); $authorizationChallengesToSolve = []; diff --git a/src/Cli/Repository/Repository.php b/src/Cli/Repository/Repository.php index 29a54c18..584227e3 100644 --- a/src/Cli/Repository/Repository.php +++ b/src/Cli/Repository/Repository.php @@ -81,13 +81,15 @@ public function __construct(SerializerInterface $serializer, FilesystemInterface /** * {@inheritdoc} */ - public function storeCertificateResponse(CertificateResponse $certificateResponse) + public function storeCertificateResponse($domain, CertificateResponse $certificateResponse) { - $distinguishedName = $certificateResponse->getCertificateRequest()->getDistinguishedName(); - $domain = $distinguishedName->getCommonName(); + if ($certificateResponse->getCertificateRequest()) { + $distinguishedName = $certificateResponse->getCertificateRequest()->getDistinguishedName(); + $domain = $distinguishedName->getCommonName(); - $this->storeDomainKeyPair($domain, $certificateResponse->getCertificateRequest()->getKeyPair()); - $this->storeDomainDistinguishedName($domain, $distinguishedName); + $this->storeDomainKeyPair($domain, $certificateResponse->getCertificateRequest()->getKeyPair()); + $this->storeDomainDistinguishedName($domain, $distinguishedName); + } $this->storeDomainCertificate($domain, $certificateResponse->getCertificate()); } diff --git a/src/Cli/Repository/RepositoryInterface.php b/src/Cli/Repository/RepositoryInterface.php index f42c5277..64657fab 100644 --- a/src/Cli/Repository/RepositoryInterface.php +++ b/src/Cli/Repository/RepositoryInterface.php @@ -35,11 +35,12 @@ interface RepositoryInterface * - the certificate request * - the certificate * + * @param string $domain * @param CertificateResponse $certificateResponse * * @throws AcmeCliException */ - public function storeCertificateResponse(CertificateResponse $certificateResponse); + public function storeCertificateResponse($domain, CertificateResponse $certificateResponse); /** * Store a given key pair as the account key pair (the global key pair used to diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 435d98c5..83f75f3e 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -135,10 +135,13 @@ public function requestOrder(array $domains, $csr = null) { Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s'); - $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; - $csrContent = $this->csrSigner->signCertificateRequest($csr); - $csrContent = trim(str_replace($humanText, '', $csrContent)); - $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + $csrContent = null; + if ($this->isCsrEager()) { + $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; + $csrContent = $this->csrSigner->signCertificateRequest($csr); + $csrContent = trim(str_replace($humanText, '', $csrContent)); + $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + } $payload = [ 'identifiers' => array_map( @@ -225,19 +228,21 @@ public function requestCertificate($domain, CertificateRequest $csr, $timeout = /** * {@inheritdoc} */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180) + public function finalizeOrder(CertificateOrder $order, $csr = null, $timeout = 180) { Assert::integer($timeout, 'finalizeOrder::$timeout expected an integer. Got: %s'); $endTime = time() + $timeout; $response = $this->getHttpClient()->signedKidRequest('GET', $order->getOrderEndpoint(), $this->getResourceAccount()); if (\in_array($response['status'], ['pending', 'ready'])) { - $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; - - $csrContent = $this->csrSigner->signCertificateRequest($csr); - $csrContent = trim(str_replace($humanText, '', $csrContent)); - $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + $csrContent = null; + if (!$this->isCsrEager()) { + $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; + $csrContent = $this->csrSigner->signCertificateRequest($csr); + $csrContent = trim(str_replace($humanText, '', $csrContent)); + $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + } $response = $this->getHttpClient()->signedKidRequest('POST', $response['finalize'], $this->getResourceAccount(), [ 'csr' => $csrContent, ]); @@ -314,6 +319,21 @@ public function getResourceUrl($resource) return $this->directory->getResourceUrl($resource); } + /** + * Find a resource URL. + * + * @return boolean + */ + public function isCsrEager() + { + if (!$this->directory) { + $this->directory = new ResourcesDirectory( + $this->getHttpClient()->unsignedRequest('GET', $this->directoryUrl, null, true) + ); + } + + return $this->directory->isCsrEager(); + } /** * Request a resource (URL is found using ACME server directory). * diff --git a/src/Core/AcmeClientV2Interface.php b/src/Core/AcmeClientV2Interface.php index 42eba089..1147649e 100644 --- a/src/Core/AcmeClientV2Interface.php +++ b/src/Core/AcmeClientV2Interface.php @@ -57,7 +57,7 @@ public function requestOrder(array $domains, $csr = null); * this operation could be long. * * @param CertificateOrder $order the Order returned by the Certificate Authority - * @param CertificateRequest $csr the Certificate Signing Request (informations for the certificate) + * @param CertificateRequest|null $csr the Certificate Signing Request (informations for the certificate) * @param int $timeout the timeout period * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code @@ -68,7 +68,7 @@ public function requestOrder(array $domains, $csr = null); * * @return CertificateResponse the certificate data to save it somewhere you want */ - public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, $timeout = 180); + public function finalizeOrder(CertificateOrder $order, $csr = null, $timeout = 180); /** * Request the current status of an authorization challenge. diff --git a/src/Core/Protocol/ResourcesDirectory.php b/src/Core/Protocol/ResourcesDirectory.php index 017616cb..79afadee 100644 --- a/src/Core/Protocol/ResourcesDirectory.php +++ b/src/Core/Protocol/ResourcesDirectory.php @@ -24,6 +24,7 @@ class ResourcesDirectory const NEW_ORDER = 'newOrder'; const NEW_NONCE = 'newNonce'; const REVOKE_CERT = 'revokeCert'; + const CSR_EAGER = 'csrEager'; /** * @var array @@ -48,6 +49,7 @@ public static function getResourcesNames() self::NEW_ORDER, self::NEW_NONCE, self::REVOKE_CERT, + self::CSR_EAGER, ]; } @@ -68,4 +70,18 @@ public function getResourceUrl($resource) return isset($this->serverResources[$resource]) ? $this->serverResources[$resource] : null; } + + /** + * Is this server requires CSR Eager + * + * @return boolean + */ + public function isCsrEager() + { + if (!isset($this->serverResources['meta']) || !isset($this->serverResources['meta'][self::CSR_EAGER])) { + return false; + } + + return $this->serverResources['meta'][self::CSR_EAGER]; + } } diff --git a/src/Ssl/CertificateResponse.php b/src/Ssl/CertificateResponse.php index fe48a7fa..1eff4ba6 100644 --- a/src/Ssl/CertificateResponse.php +++ b/src/Ssl/CertificateResponse.php @@ -25,11 +25,11 @@ class CertificateResponse private $certificate; /** - * @param CertificateRequest $certificateRequest + * @param CertificateRequest|null $certificateRequest * @param Certificate $certificate */ public function __construct( - CertificateRequest $certificateRequest, + $certificateRequest, Certificate $certificate ) { $this->certificateRequest = $certificateRequest; @@ -37,7 +37,7 @@ public function __construct( } /** - * @return CertificateRequest + * @return CertificateRequest|null */ public function getCertificateRequest() { From 51ce2fb9c52f4b6924348d2b8b01d1f9afd6b4a9 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 01:18:52 +0800 Subject: [PATCH 017/126] update change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e158f131..9c2e5f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +31/07/2019 01:16 1.1.2 Implement csrEager 18/01/2019 15:17 1.1.1 Several bug fixes 952b1a6 Merge pull request #148 from rokclimb15/patch-1 56df417 Correctly throw ChallengeTimedOutException From d522db72f339ff5c57ace3cf6bf5851662672600 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 02:22:07 +0800 Subject: [PATCH 018/126] stash --- composer.json | 3 +- src/Core/Challenge/Dns/AliyunSolver.php | 148 ++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/Core/Challenge/Dns/AliyunSolver.php diff --git a/composer.json b/composer.json index ef309996..e6dd8a54 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "symfony/serializer": "^3.0|^4.0", "symfony/yaml": "^3.0|^4.0", "webmozart/assert": "^1.0", - "webmozart/path-util": "^2.3" + "webmozart/path-util": "^2.3", + "alibabacloud/alidns": "^1.7" }, "suggest": { "daverandom/libdns": "^2.0" diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php new file mode 100644 index 00000000..0ffc922d --- /dev/null +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Dns; + +use AcmePhp\Core\Challenge\ConfigurableServiceInterface; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Webmozart\Assert\Assert; +use AlibabaCloud\Alidns\V20150109\AddDomainRecord; +use AlibabaCloud\Alidns\Alidns; + +/** + * ACME DNS solver with automate configuration of a Gandi.Net. + * + * @author Alexander Obuhovich + */ +class AliyunSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface +{ + use LoggerAwareTrait; + + /** + * @var DnsDataExtractor + */ + private $extractor; + + /** + * @var ClientInterface + */ + private $client; + + /** + * @var array + */ + private $cacheZones; + + /** + * @var string + */ + private $apiKey; + + /** + * @param DnsDataExtractor $extractor + * @param ClientInterface $client + */ + public function __construct( + DnsDataExtractor $extractor = null, + ClientInterface $client = null + ) { + $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; + $this->client = null === $client ? new Client() : $client; + $this->logger = new NullLogger(); + } + + /** + * Configure the service with a set of configuration. + * + * @param array $config + */ + public function configure(array $config) + { + $this->apiKey = $config['api_key']; + } + + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge) + { + return 'dns-01' === $authorizationChallenge->getType(); + } + + /** + * {@inheritdoc} + */ + public function solve(AuthorizationChallenge $authorizationChallenge) + { + return $this->solveAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function solveAll(array $authorizationChallenges) + { + + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + + $dns = new Alidns(); + $dns->v20150109()->addDomainRecord([ + 'DomainName' => $topLevelDomain, + 'Type' => isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT', + 'PR' => $subDomain, + 'Value' => $recordValue, + ]); + } + } + + /** + * {@inheritdoc} + */ + public function cleanup(AuthorizationChallenge $authorizationChallenge) + { + return $this->cleanupAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function cleanupAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); + + $dns = new Alidns(); + $dns->v20150109()->deleteDomainRecord(null); + } + } + + /** + * @param string $domain + * + * @return string + */ + protected function getTopLevelDomain($domain) + { + return \implode('.', \array_slice(\explode('.', $domain), -2)); + } +} From 0c6088d080eaa786f891ebc142a2eb49a4b87ab2 Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Wed, 31 Jul 2019 10:39:23 +0800 Subject: [PATCH 019/126] rename to trustocean/acme-client --- composer.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index d422eb1b..6ae69670 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,13 @@ { - "name": "trustocean/acmephp", - "description": "Let's Encrypt client written in PHP", + "name": "trustocean/acme-client", + "description": "ACME client written in PHP", "type": "project", "license": "MIT", - "homepage": "https://github.com/acmephp/acmephp", + "homepage": "https://github.com/trustocean/acme-client", "keywords": [ "acme", "acmephp", + "acme-client", "letsencrypt", "https", "encryption", @@ -39,6 +40,7 @@ "ext-openssl": "*", "lib-openssl": ">=0.9.8", "aws/aws-sdk-php": "^3.38", + "daverandom/libdns": "^2.0", "guzzlehttp/guzzle": "^6.0", "guzzlehttp/psr7": "^1.0", "league/flysystem": "^1.0.19", @@ -59,9 +61,6 @@ "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3" }, - "suggest": { - "daverandom/libdns": "^2.0" - }, "require-dev": { "phpspec/prophecy": "^1.8", "symfony/finder": "^3.4|^4.0", From d550946dc11b7a35bca785f1689688cf3c292b31 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:14:13 +0800 Subject: [PATCH 020/126] done support for Aliyun DNS service --- src/Core/Challenge/Dns/AliyunSolver.php | 54 +++++++++++++++++-------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index 0ffc922d..ab6d110f 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -18,13 +18,13 @@ use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use Webmozart\Assert\Assert; -use AlibabaCloud\Alidns\V20150109\AddDomainRecord; use AlibabaCloud\Alidns\Alidns; +use AlibabaCloud\Client\AlibabaCloud; /** - * ACME DNS solver with automate configuration of a Gandi.Net. + * ACME DNS solver with automate configuration of a Aliyun.com * - * @author Alexander Obuhovich + * @author Xiaohui Lam */ class AliyunSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface { @@ -48,7 +48,12 @@ class AliyunSolver implements MultipleChallengesSolverInterface, ConfigurableSer /** * @var string */ - private $apiKey; + private $accessKeyId; + + /** + * @var string + */ + private $accessKeySecret; /** * @param DnsDataExtractor $extractor @@ -70,7 +75,10 @@ public function __construct( */ public function configure(array $config) { - $this->apiKey = $config['api_key']; + $this->accessKeyId = $config['access_key_id']; + $this->accessKeySecret = $config['access_key_secret']; + + AlibabaCloud::accessKeyClient($this->accessKeyId, $this->accessKeySecret)->regionId('cn-hangzhou')->asDefaultClient(); } /** @@ -94,7 +102,6 @@ public function solve(AuthorizationChallenge $authorizationChallenge) */ public function solveAll(array $authorizationChallenges) { - Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); foreach ($authorizationChallenges as $authorizationChallenge) { $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); @@ -103,12 +110,27 @@ public function solveAll(array $authorizationChallenges) $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); $dns = new Alidns(); - $dns->v20150109()->addDomainRecord([ - 'DomainName' => $topLevelDomain, - 'Type' => isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT', - 'PR' => $subDomain, - 'Value' => $recordValue, - ]); + $do = $dns->v20150109()->addDomainRecord() + ->withDomainName('a.com') + ->withType('TXT') + ->withRR('bb') + ->withValue('aaa') + ->request(); + + /** + * @var \AlibabaCloud\Client\Result\Result $response + */ + $response = $dns->v20150109()->addDomainRecord() + ->withDomainName($topLevelDomain) + ->withType(isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT') + ->withRR($subDomain) + ->withValue($recordValue) + ->request(); + + /** + * Store to $authorizationChallenge, because when it requires recordId when clear + */ + $authorizationChallenge->recordId = $response->get('RecordId'); } } @@ -127,12 +149,10 @@ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); foreach ($authorizationChallenges as $authorizationChallenge) { - $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); - $recordName = $this->extractor->getRecordName($authorizationChallenge); - $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); - $dns = new Alidns(); - $dns->v20150109()->deleteDomainRecord(null); + $dns->v20150109()->deleteDomainRecord() + ->withRecordId($authorizationChallenge->recordId) + ->request(); } } From 1675a73874106d9adf464362f1d4e8b39216b9be Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:21:35 +0800 Subject: [PATCH 021/126] enhancement --- src/Cli/Command/AuthorizeCommand.php | 4 ++-- src/Cli/Command/CheckCommand.php | 2 +- src/Cli/Resources/services.xml | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 4a912891..b1a186c3 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -32,7 +32,7 @@ protected function configure() { $this->setName('authorize') ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), + new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi, aliyun)', 'http'), new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'List of domains to ask an authorization for'), ]) ->setDescription('Ask the ACME server for an authorization token to check you are the owner of a domain') @@ -44,7 +44,7 @@ protected function configure() Ask the server for an authorization token: php %command.full_name% example.com www.exemple.org *.example.io - + Follow the instructions to expose your token on the specific URL, and then run the check command to tell the server to check your token. EOF diff --git a/src/Cli/Command/CheckCommand.php b/src/Cli/Command/CheckCommand.php index 72c63f8d..6b9af5ee 100644 --- a/src/Cli/Command/CheckCommand.php +++ b/src/Cli/Command/CheckCommand.php @@ -34,7 +34,7 @@ protected function configure() { $this->setName('check') ->setDefinition([ - new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi)', 'http'), + new InputOption('solver', 's', InputOption::VALUE_REQUIRED, 'The type of challenge solver to use (available: http, dns, route53, gandi, aliyun)', 'http'), new InputOption('no-test', 't', InputOption::VALUE_NONE, 'Whether or not internal tests should be disabled'), new InputArgument('domains', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The list of domains to check the authorization for'), ]) diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 72e9953d..2e11c735 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -188,6 +188,12 @@ + + + + + + From 26b54bb2fe639cb47ba7e8920e73f19252a6e9f3 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:43:02 +0800 Subject: [PATCH 022/126] update travis to build phar --- .travis.yml | 4 ++++ script/build_phar.php | 6 ++++++ src/Cli/Application.php | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 script/build_phar.php diff --git a/.travis.yml b/.travis.yml index a434d871..8334c398 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,10 @@ jobs: COMMENT: split repo script: - docker run --rm -t -e GH_TOKEN -v "$HOME/.gitsplit/cache":/cache/gitsplit -v ${PWD}:/srv jderusse/gitsplit:2.0 gitsplit --ref "${TRAVIS_BRANCH}"; + - composer config platform.php 5.6.3 + - composer update --no-dev + - php -d phar.readonly=0 script/build_phar.php + - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn env: global: secure: "guVs6Ym+RKa+4l3UAR/LbS360VziKilA+FHn89R9vszDuOm7Yj0+KvuE+c6nCSNL6JgVsBN6X14naDuDPrb/61HMCOZZhehL3lHu5j76sF7dxRgkftuT6vdn7z0Zz1Kaw7iH1oEgSsVpL9zKXEC9acgWHNb894ecBn80lhLiI5GfGzEcTEpfuowT0MOaFe0Oa5mFIFux4p4DWEZSkEmc21GYZI/CQe5VhRUGjApJNNiKtcHw1Qvdbu1Ubaq9W0issjXjEeBibSeIfjN6++L+k6QH1AITTTTJRRsz1O2T+8N9PxGjHR0o3+IfCC1oEQ6Ezlx10uS8TL85ytlRtZg+NNTJ1FDHQ39/M5ZcOpkrL9ohlfbVq3bcxp5i/Iu2UnO4ZvpMxRQLT1sH735gEt5NeNn9vsNm2M7ke/7oEi8rksixI1bakEFkOrsbGBDMg797wE9DtUFoY3JOKzKkCQjXiUkCsT2UalDoUauF7CkM3+QrEyhPOeNFK5lgRBsVJfeD6idNCmIvONoN8F8FCBlYvhrsLkTtwpO1uFyMboJ3/CIT8S4l1C3JPcTnMt2DUMYXxOv/tl/1x1MtL785tOb+OWs1Sta2H/Bllxp9qUPmNM1+uj71aTPqBLpWCwF2skRywW7Jqb9qGsROiOVUa6to1hMbbKSOoL59KqZF6e2EDpo=" diff --git a/script/build_phar.php b/script/build_phar.php new file mode 100644 index 00000000..8728c7c1 --- /dev/null +++ b/script/build_phar.php @@ -0,0 +1,6 @@ +buildFromDirectory(__DIR__ . '/../'); +$phar->compressFiles(Phar::GZ); +$phar->setDefaultStub('bin/acme'); \ No newline at end of file diff --git a/src/Cli/Application.php b/src/Cli/Application.php index a057715a..214d7ec9 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -30,7 +30,7 @@ */ class Application extends BaseApplication { - const VERSION = '1.1.1'; + const VERSION = '1.1.2'; /** * {@inheritdoc} From a28ba03b1cbad5c4d8eb27afd5addb54d321d72b Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:47:23 +0800 Subject: [PATCH 023/126] remove php5.5 --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8334c398..377ac7b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,6 @@ notifications: jobs: include: - - <<: *phpunit - php: 5.5 - <<: *phpunit php: 5.6 - <<: *phpunit_low From d9e446f3da79a5db366e048df1d479d2d2cc4997 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:53:40 +0800 Subject: [PATCH 024/126] update --- .travis.yml | 8 ++++---- composer.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 377ac7b0..0b95d314 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,10 @@ jobs: - $HOME/.gitsplit/cache if: branch = master AND fork = false install: + - composer config platform.php 5.6.3 + - composer update --no-dev + - php -d phar.readonly=0 script/build_phar.php + - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn - docker pull jderusse/gitsplit:2.0 - git config remote.origin.fetch "+refs/*:refs/*" - git config remote.origin.mirror true @@ -54,10 +58,6 @@ jobs: COMMENT: split repo script: - docker run --rm -t -e GH_TOKEN -v "$HOME/.gitsplit/cache":/cache/gitsplit -v ${PWD}:/srv jderusse/gitsplit:2.0 gitsplit --ref "${TRAVIS_BRANCH}"; - - composer config platform.php 5.6.3 - - composer update --no-dev - - php -d phar.readonly=0 script/build_phar.php - - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn env: global: secure: "guVs6Ym+RKa+4l3UAR/LbS360VziKilA+FHn89R9vszDuOm7Yj0+KvuE+c6nCSNL6JgVsBN6X14naDuDPrb/61HMCOZZhehL3lHu5j76sF7dxRgkftuT6vdn7z0Zz1Kaw7iH1oEgSsVpL9zKXEC9acgWHNb894ecBn80lhLiI5GfGzEcTEpfuowT0MOaFe0Oa5mFIFux4p4DWEZSkEmc21GYZI/CQe5VhRUGjApJNNiKtcHw1Qvdbu1Ubaq9W0issjXjEeBibSeIfjN6++L+k6QH1AITTTTJRRsz1O2T+8N9PxGjHR0o3+IfCC1oEQ6Ezlx10uS8TL85ytlRtZg+NNTJ1FDHQ39/M5ZcOpkrL9ohlfbVq3bcxp5i/Iu2UnO4ZvpMxRQLT1sH735gEt5NeNn9vsNm2M7ke/7oEi8rksixI1bakEFkOrsbGBDMg797wE9DtUFoY3JOKzKkCQjXiUkCsT2UalDoUauF7CkM3+QrEyhPOeNFK5lgRBsVJfeD6idNCmIvONoN8F8FCBlYvhrsLkTtwpO1uFyMboJ3/CIT8S4l1C3JPcTnMt2DUMYXxOv/tl/1x1MtL785tOb+OWs1Sta2H/Bllxp9qUPmNM1+uj71aTPqBLpWCwF2skRywW7Jqb9qGsROiOVUa6to1hMbbKSOoL59KqZF6e2EDpo=" diff --git a/composer.json b/composer.json index 9975573d..e1325804 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "ext-openssl": "*", "lib-openssl": ">=0.9.8", "aws/aws-sdk-php": "^3.38", - "daverandom/libdns": "^2.0", + "daverandom/libdns": ">=1.0", "guzzlehttp/guzzle": "^6.0", "guzzlehttp/psr7": "^1.0", "league/flysystem": "^1.0.19", From 898534d8230d8e6db9731b73c0b034f57711c4ff Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 11:56:50 +0800 Subject: [PATCH 025/126] update --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0b95d314..9d1d20a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,10 @@ notifications: services: - docker before_install: + - composer config platform.php 5.6.3 + - composer update --no-dev + - php -d phar.readonly=0 script/build_phar.php + - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn - stty cols 120 - phpenv config-rm xdebug.ini || echo "xdebug not available" - ./tests/setup.sh @@ -46,10 +50,6 @@ jobs: - $HOME/.gitsplit/cache if: branch = master AND fork = false install: - - composer config platform.php 5.6.3 - - composer update --no-dev - - php -d phar.readonly=0 script/build_phar.php - - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn - docker pull jderusse/gitsplit:2.0 - git config remote.origin.fetch "+refs/*:refs/*" - git config remote.origin.mirror true From bbdc83654cf9860c89b4c218f6bb4267fe1cf97a Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 12:24:39 +0800 Subject: [PATCH 026/126] fix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9d1d20a7..998eb201 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ notifications: services: - docker before_install: + - apt-get install mutt - composer config platform.php 5.6.3 - composer update --no-dev - php -d phar.readonly=0 script/build_phar.php From 97e512c068f5ea2d4157867f5da21afc874f98b6 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 13:44:08 +0800 Subject: [PATCH 027/126] enhancement --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 998eb201..1bc5a5df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,6 @@ notifications: services: - docker before_install: - - apt-get install mutt - - composer config platform.php 5.6.3 - - composer update --no-dev - - php -d phar.readonly=0 script/build_phar.php - - echo "Latest acmephp.phar" | mutt -a "/tmp/acmephp.phar" -s "see it in attachment" -- xiaohui.lam@e.hexdata.cn - stty cols 120 - phpenv config-rm xdebug.ini || echo "xdebug not available" - ./tests/setup.sh @@ -51,6 +46,11 @@ jobs: - $HOME/.gitsplit/cache if: branch = master AND fork = false install: + - composer config platform.php 5.6.3 + - composer update --no-dev + - php -d phar.readonly=0 script/build_phar.php + - curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" + - curl -F 'file=@/tmp/acmephp.phar' https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets\?name\=acmephp.phar -H 'Authorization: token $GITHUB_TOKEN" - docker pull jderusse/gitsplit:2.0 - git config remote.origin.fetch "+refs/*:refs/*" - git config remote.origin.mirror true From eb425e6362328f6d0cdca5147e6684ed9acc2603 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 13:57:42 +0800 Subject: [PATCH 028/126] test --- .travis.yml | 3 +-- script/upload_phar.sh | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 script/upload_phar.sh diff --git a/.travis.yml b/.travis.yml index 1bc5a5df..729878ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,8 +49,7 @@ jobs: - composer config platform.php 5.6.3 - composer update --no-dev - php -d phar.readonly=0 script/build_phar.php - - curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" - - curl -F 'file=@/tmp/acmephp.phar' https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets\?name\=acmephp.phar -H 'Authorization: token $GITHUB_TOKEN" + - sh script/upload_phar.sh - docker pull jderusse/gitsplit:2.0 - git config remote.origin.fetch "+refs/*:refs/*" - git config remote.origin.mirror true diff --git a/script/upload_phar.sh b/script/upload_phar.sh new file mode 100644 index 00000000..f5884ce5 --- /dev/null +++ b/script/upload_phar.sh @@ -0,0 +1,2 @@ +curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" +curl -F 'file=@/tmp/acmephp.phar' https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets\?name\=acmephp.phar -H 'Authorization: token $GITHUB_TOKEN" \ No newline at end of file From d84a112a369335eec25e41630ac81c2e4f8b2abb Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 14:09:43 +0800 Subject: [PATCH 029/126] fix --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index f5884ce5..7503b8fe 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -curl -F 'file=@/tmp/acmephp.phar' https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets\?name\=acmephp.phar -H 'Authorization: token $GITHUB_TOKEN" \ No newline at end of file +curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H 'Authorization: token $GITHUB_TOKEN" \ No newline at end of file From 6d113ee0a705d7ee224a3e6e395d0e63bd982630 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 14:10:05 +0800 Subject: [PATCH 030/126] fix --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index 7503b8fe..cdee8536 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H 'Authorization: token $GITHUB_TOKEN" \ No newline at end of file +curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" \ No newline at end of file From 985d91826576f942adb068f9d555f6e447d1f932 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 14:23:21 +0800 Subject: [PATCH 031/126] fix --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index cdee8536..0ad2e60a 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ -curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" +curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" || echo failed curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" \ No newline at end of file From 6c7969095dc627b4ba4461db7598ba41f16c1820 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 14:36:12 +0800 Subject: [PATCH 032/126] fix --- .travis.yml | 47 ------------------------------------------- script/upload_phar.sh | 4 ++-- 2 files changed, 2 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index 729878ee..78dfe950 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,58 +6,11 @@ branches: notifications: email: false -.template_phpunit: &phpunit - services: - - docker - before_install: - - stty cols 120 - - phpenv config-rm xdebug.ini || echo "xdebug not available" - - ./tests/setup.sh - install: composer install --no-interaction --no-progress --ansi - script: ./tests/run.sh - cache: - directories: - - $HOME/.composer/cache/files -.template_phpunit_low: &phpunit_low - <<: *phpunit - install: composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable - env: - COMMENT: low deps -.template_phpunit_high: &phpunit_high - <<: *phpunit - install: composer update --no-interaction --no-progress --ansi - env: - COMMENT: high deps - jobs: include: - <<: *phpunit php: 5.6 - - <<: *phpunit_low - php: 7.0 - - <<: *phpunit - php: 7.1 - - <<: *phpunit_high - php: 7.2 - - stage: split - php: 7.2 - cache: - directories: - - $HOME/.gitsplit/cache - if: branch = master AND fork = false install: - - composer config platform.php 5.6.3 - composer update --no-dev - php -d phar.readonly=0 script/build_phar.php - sh script/upload_phar.sh - - docker pull jderusse/gitsplit:2.0 - - git config remote.origin.fetch "+refs/*:refs/*" - - git config remote.origin.mirror true - - git fetch --unshallow || true - env: - COMMENT: split repo - script: - - docker run --rm -t -e GH_TOKEN -v "$HOME/.gitsplit/cache":/cache/gitsplit -v ${PWD}:/srv jderusse/gitsplit:2.0 gitsplit --ref "${TRAVIS_BRANCH}"; -env: - global: - secure: "guVs6Ym+RKa+4l3UAR/LbS360VziKilA+FHn89R9vszDuOm7Yj0+KvuE+c6nCSNL6JgVsBN6X14naDuDPrb/61HMCOZZhehL3lHu5j76sF7dxRgkftuT6vdn7z0Zz1Kaw7iH1oEgSsVpL9zKXEC9acgWHNb894ecBn80lhLiI5GfGzEcTEpfuowT0MOaFe0Oa5mFIFux4p4DWEZSkEmc21GYZI/CQe5VhRUGjApJNNiKtcHw1Qvdbu1Ubaq9W0issjXjEeBibSeIfjN6++L+k6QH1AITTTTJRRsz1O2T+8N9PxGjHR0o3+IfCC1oEQ6Ezlx10uS8TL85ytlRtZg+NNTJ1FDHQ39/M5ZcOpkrL9ohlfbVq3bcxp5i/Iu2UnO4ZvpMxRQLT1sH735gEt5NeNn9vsNm2M7ke/7oEi8rksixI1bakEFkOrsbGBDMg797wE9DtUFoY3JOKzKkCQjXiUkCsT2UalDoUauF7CkM3+QrEyhPOeNFK5lgRBsVJfeD6idNCmIvONoN8F8FCBlYvhrsLkTtwpO1uFyMboJ3/CIT8S4l1C3JPcTnMt2DUMYXxOv/tl/1x1MtL785tOb+OWs1Sta2H/Bllxp9qUPmNM1+uj71aTPqBLpWCwF2skRywW7Jqb9qGsROiOVUa6to1hMbbKSOoL59KqZF6e2EDpo=" diff --git a/script/upload_phar.sh b/script/upload_phar.sh index 0ad2e60a..bf9cf284 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ -curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" || echo failed -curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" \ No newline at end of file +curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed +curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" -s \ No newline at end of file From 040b5b52987647a6f7b1f0c1b515ff6ae3c6ab61 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 14:40:24 +0800 Subject: [PATCH 033/126] fix --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 78dfe950..bf2845df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ -sudo: false language: php branches: only: @@ -6,11 +5,12 @@ branches: notifications: email: false -jobs: +matrix: include: - - <<: *phpunit - php: 5.6 - install: - - composer update --no-dev - - php -d phar.readonly=0 script/build_phar.php - - sh script/upload_phar.sh + - php: 5.6 + env: ENV=building + +install: + - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest + - php -d phar.readonly=0 script/build_phar.php + - sh script/upload_phar.sh From 856526d53e04aa14fa0321ecc7e26873c5d44055 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 16:08:14 +0800 Subject: [PATCH 034/126] fix phar build --- .travis.yml | 10 ++++++++-- box.json.dist | 3 +-- script/build_phar.php | 5 ++++- script/upload_phar.sh | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf2845df..3bbed8db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,16 @@ notifications: matrix: include: - - php: 5.6 + - php: 7.2 env: ENV=building install: + - composer global require bamarni/composer-bin-plugin + - composer global require humbug/box + - ln -s ~/.composer/vendor ~/vendor + - composer config platform.php 5.6.3 - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest - - php -d phar.readonly=0 script/build_phar.php + - mv box.json.dist box.json + - php -d phar.readonly=0 ~/.composer/vendor/humbug/box/bin/box build - sh script/upload_phar.sh + - exit 0 diff --git a/box.json.dist b/box.json.dist index c3d40d75..c92a2fca 100644 --- a/box.json.dist +++ b/box.json.dist @@ -22,6 +22,5 @@ "main": "bin/acme", "output": "build/acmephp.phar", "chmod": "0755", - "stub": true, - "key": "../private.key" + "stub": true } diff --git a/script/build_phar.php b/script/build_phar.php index 8728c7c1..83dba671 100644 --- a/script/build_phar.php +++ b/script/build_phar.php @@ -1,6 +1,9 @@ startBuffering(); $phar->buildFromDirectory(__DIR__ . '/../'); $phar->compressFiles(Phar::GZ); -$phar->setDefaultStub('bin/acme'); \ No newline at end of file +$phar->setDefaultStub('bin/acme'); +$phar->setSignatureAlgorithm(Phar::SHA256); +$phar->stopBuffering(); diff --git a/script/upload_phar.sh b/script/upload_phar.sh index bf9cf284..29367e1b 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed -curl -F 'file=@/tmp/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" -s \ No newline at end of file +curl -F 'file=@build/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" -s \ No newline at end of file From 62a5bd47664eac02bbfa60e01ad1072cd344653e Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 16:09:31 +0800 Subject: [PATCH 035/126] fix --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3bbed8db..b0a58d66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,11 @@ matrix: install: - composer global require bamarni/composer-bin-plugin - - composer global require humbug/box - - ln -s ~/.composer/vendor ~/vendor + - composer require --dev humbug/box + - ln -s $(pwd)/vendor ~/vendor - composer config platform.php 5.6.3 - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest - mv box.json.dist box.json - - php -d phar.readonly=0 ~/.composer/vendor/humbug/box/bin/box build + - php -d phar.readonly=0 vendor/humbug/box/bin/box build - sh script/upload_phar.sh - exit 0 From d8ca2dcdb2b360a7b33c0e33739e5d8dd2c59092 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 16:18:45 +0800 Subject: [PATCH 036/126] stash --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b0a58d66..62036d47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,11 @@ matrix: install: - composer global require bamarni/composer-bin-plugin - - composer require --dev humbug/box - - ln -s $(pwd)/vendor ~/vendor - composer config platform.php 5.6.3 - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest + - composer global require humbug/box - mv box.json.dist box.json + - ln -s ~/.composer/vendor ~/vendor - php -d phar.readonly=0 vendor/humbug/box/bin/box build - sh script/upload_phar.sh - exit 0 From 1e07b367edd869616bceeaeb7df1091f65dbbe1d Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 16:29:09 +0800 Subject: [PATCH 037/126] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=89=93=E5=8C=85=20?= =?UTF-8?q?Phar=20=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 62036d47..d03378f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ install: - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest - composer global require humbug/box - mv box.json.dist box.json - - ln -s ~/.composer/vendor ~/vendor - - php -d phar.readonly=0 vendor/humbug/box/bin/box build + - ln -s ~/.config/composer/vendor ~/vendor + - php -d phar.readonly=0 ~/.config/composer/vendor/humbug/box/bin/box build - sh script/upload_phar.sh - exit 0 From 5cad995c0d36f0d3cb3275acae314fdfe65cc9ac Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 31 Jul 2019 16:52:41 +0800 Subject: [PATCH 038/126] done phar auto compile --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index 29367e1b..e3ac8791 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed -curl -F 'file=@build/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -H "Authorization: token $GITHUB_TOKEN" -s \ No newline at end of file +curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -s \ No newline at end of file From 8541342573fe44e4fd80d4db2ef81b92bef910f3 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 14:49:46 +0800 Subject: [PATCH 039/126] statsh --- composer.json | 3 +- src/Cli/Action/InstallAwsElbv2Action copy.php | 37 +++++++++++++++++++ src/Cli/Resources/services.xml | 4 ++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/Cli/Action/InstallAwsElbv2Action copy.php diff --git a/composer.json b/composer.json index ef309996..9dc27e21 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "symfony/serializer": "^3.0|^4.0", "symfony/yaml": "^3.0|^4.0", "webmozart/assert": "^1.0", - "webmozart/path-util": "^2.3" + "webmozart/path-util": "^2.3", + "alibabacloud/wafopenapi": "^1.7" }, "suggest": { "daverandom/libdns": "^2.0" diff --git a/src/Cli/Action/InstallAwsElbv2Action copy.php b/src/Cli/Action/InstallAwsElbv2Action copy.php new file mode 100644 index 00000000..986d86d9 --- /dev/null +++ b/src/Cli/Action/InstallAwsElbv2Action copy.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Cli\Action; + +use AlibabaCloud\Client\AlibabaCloud; +use AlibabaCloud\WafOpenapi\WafOpenapi; + +/** + * Action to install certificate in an AWS ELBv2. + * + * @author Xiaohui Lam + */ +class InstallAliyunWafAction extends AbstractAction +{ + protected function installCertificate($certificateArn, $region, $loadBalancerName, $loadBalancerPort) + { + print_r($certificateArn); + exit; + + AlibabaCloud::accessKeyClient('', '')->regionId('cn')->asDefaultClient(); + WafOpenapi::v20180117()->createCertAndKey() + ->withCert($certificateArn) + ->withKey() + ->withDomain() + ->withHttpsCertName() + ->withInstanceId(); + } +} diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index fa582e3f..4b752e88 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -122,6 +122,10 @@ + + + + From 05a2f399f43494b77b8f02a0dcec531973b1bdc8 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 14:56:11 +0800 Subject: [PATCH 040/126] fix --- ...on copy.php => InstallAliyunWafAction.php} | 21 ++++++++++++++----- src/Cli/Resources/services.xml | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) rename src/Cli/Action/{InstallAwsElbv2Action copy.php => InstallAliyunWafAction.php} (54%) diff --git a/src/Cli/Action/InstallAwsElbv2Action copy.php b/src/Cli/Action/InstallAliyunWafAction.php similarity index 54% rename from src/Cli/Action/InstallAwsElbv2Action copy.php rename to src/Cli/Action/InstallAliyunWafAction.php index 986d86d9..17de9770 100644 --- a/src/Cli/Action/InstallAwsElbv2Action copy.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -13,6 +13,7 @@ use AlibabaCloud\Client\AlibabaCloud; use AlibabaCloud\WafOpenapi\WafOpenapi; +use AcmePhp\Ssl\CertificateResponse; /** * Action to install certificate in an AWS ELBv2. @@ -21,15 +22,25 @@ */ class InstallAliyunWafAction extends AbstractAction { - protected function installCertificate($certificateArn, $region, $loadBalancerName, $loadBalancerPort) + /** + * {@inheritdoc} + */ + public function handle($config, CertificateResponse $response) { - print_r($certificateArn); - exit; + $issuerChain = []; + $issuerCertificate = $response->getCertificate()->getIssuerCertificate(); + while (null !== $issuerCertificate) { + $issuerChain[] = $issuerCertificate->getPEM(); + $issuerCertificate = $issuerCertificate->getIssuerCertificate(); + } + $cert = implode("\n", $issuerChain); + + $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); AlibabaCloud::accessKeyClient('', '')->regionId('cn')->asDefaultClient(); WafOpenapi::v20180117()->createCertAndKey() - ->withCert($certificateArn) - ->withKey() + ->withCert($cert) + ->withKey($key) ->withDomain() ->withHttpsCertName() ->withInstanceId(); diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 4b752e88..4e9e0e4d 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -122,7 +122,7 @@ - + From 40558ea42851bf9a0e321e161cfa202d6aab41fa Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 15:36:34 +0800 Subject: [PATCH 041/126] close #17 --- src/Cli/Action/InstallAliyunWafAction.php | 29 ++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 17de9770..9ae180fa 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -14,9 +14,11 @@ use AlibabaCloud\Client\AlibabaCloud; use AlibabaCloud\WafOpenapi\WafOpenapi; use AcmePhp\Ssl\CertificateResponse; +use AlibabaCloud\Client\Exception\ServerException; +use AlibabaCloud\Client\Exception\ClientException; /** - * Action to install certificate in an AWS ELBv2. + * Action to install certificate in an Aliyun Waf. * * @author Xiaohui Lam */ @@ -37,12 +39,23 @@ public function handle($config, CertificateResponse $response) $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); - AlibabaCloud::accessKeyClient('', '')->regionId('cn')->asDefaultClient(); - WafOpenapi::v20180117()->createCertAndKey() - ->withCert($cert) - ->withKey($key) - ->withDomain() - ->withHttpsCertName() - ->withInstanceId(); + try { + AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); + $response = WafOpenapi::v20161111()->upgradeInstance() + ->host('wafopenapi.cn-hangzhou.aliyuncs.com') + ->action('CreateCertAndKey') + ->setProtocol('https') + ->version('2018-01-17') + ->withCert($cert) + ->withKey($key) + ->withDomain($config['domain']) + ->withHttpsCertName($config['domain']) + ->withInstanceId($config['instanceId']) + ->request(); + } catch (ServerException $e) { + throw $e; + } catch (ClientException $e) { + throw $e; + } } } From 1a17f6e6b1903c15428860d922f9974e54332911 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 15:44:18 +0800 Subject: [PATCH 042/126] fix style ci --- src/Cli/Action/InstallAliyunWafAction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 9ae180fa..8998442d 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -11,11 +11,11 @@ namespace AcmePhp\Cli\Action; +use AcmePhp\Ssl\CertificateResponse; use AlibabaCloud\Client\AlibabaCloud; use AlibabaCloud\WafOpenapi\WafOpenapi; -use AcmePhp\Ssl\CertificateResponse; -use AlibabaCloud\Client\Exception\ServerException; use AlibabaCloud\Client\Exception\ClientException; +use AlibabaCloud\Client\Exception\ServerException; /** * Action to install certificate in an Aliyun Waf. From 723de09731de8b240ece2863a9470952e6137dee Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Thu, 1 Aug 2019 15:57:11 +0800 Subject: [PATCH 043/126] style ci --- src/Cli/Action/InstallAliyunWafAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 8998442d..420c46d8 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -13,9 +13,9 @@ use AcmePhp\Ssl\CertificateResponse; use AlibabaCloud\Client\AlibabaCloud; -use AlibabaCloud\WafOpenapi\WafOpenapi; use AlibabaCloud\Client\Exception\ClientException; use AlibabaCloud\Client\Exception\ServerException; +use AlibabaCloud\WafOpenapi\WafOpenapi; /** * Action to install certificate in an Aliyun Waf. From e9fefc3b9821f8a6316b88b0b8a30eb3d6a33653 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 17:21:25 +0800 Subject: [PATCH 044/126] stash --- composer.json | 3 +- src/Cli/Action/InstallAliyunCdnAction.php | 60 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/Cli/Action/InstallAliyunCdnAction.php diff --git a/composer.json b/composer.json index ef309996..f9ece7f8 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,8 @@ "symfony/serializer": "^3.0|^4.0", "symfony/yaml": "^3.0|^4.0", "webmozart/assert": "^1.0", - "webmozart/path-util": "^2.3" + "webmozart/path-util": "^2.3", + "alibabacloud/cdn": "^1.7" }, "suggest": { "daverandom/libdns": "^2.0" diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php new file mode 100644 index 00000000..f3680b77 --- /dev/null +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Cli\Action; + +use AcmePhp\Ssl\CertificateResponse; +use AlibabaCloud\Client\AlibabaCloud; +use AlibabaCloud\Client\Exception\ClientException; +use AlibabaCloud\Client\Exception\ServerException; + +/** + * Action to install certificate in an Aliyun Waf. + * + * @author Xiaohui Lam + */ +class InstallAliyunCdnAction extends AbstractAction +{ + /** + * {@inheritdoc} + */ + public function handle($config, CertificateResponse $response) + { + $issuerChain = []; + $issuerCertificate = $response->getCertificate()->getIssuerCertificate(); + while (null !== $issuerCertificate) { + $issuerChain[] = $issuerCertificate->getPEM(); + $issuerCertificate = $issuerCertificate->getIssuerCertificate(); + } + $cert = implode("\n", $issuerChain); + + $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); + + try { + AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); + $response = WafOpenapi::v20161111()->upgradeInstance() + ->host('wafopenapi.cn-hangzhou.aliyuncs.com') + ->action('CreateCertAndKey') + ->setProtocol('https') + ->version('2018-01-17') + ->withCert($cert) + ->withKey($key) + ->withDomain($config['domain']) + ->withHttpsCertName($config['domain']) + ->withInstanceId($config['instanceId']) + ->request(); + } catch (ServerException $e) { + throw $e; + } catch (ClientException $e) { + throw $e; + } + } +} From 91049e3532fc905dde3b90406c968dff0abb7de4 Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Thu, 1 Aug 2019 17:44:15 +0800 Subject: [PATCH 045/126] Remove try...catch wrapper --- src/Cli/Action/InstallAliyunWafAction.php | 30 +++++++++-------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 420c46d8..698df57e 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -39,23 +39,17 @@ public function handle($config, CertificateResponse $response) $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); - try { - AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); - $response = WafOpenapi::v20161111()->upgradeInstance() - ->host('wafopenapi.cn-hangzhou.aliyuncs.com') - ->action('CreateCertAndKey') - ->setProtocol('https') - ->version('2018-01-17') - ->withCert($cert) - ->withKey($key) - ->withDomain($config['domain']) - ->withHttpsCertName($config['domain']) - ->withInstanceId($config['instanceId']) - ->request(); - } catch (ServerException $e) { - throw $e; - } catch (ClientException $e) { - throw $e; - } + AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); + $response = WafOpenapi::v20161111()->upgradeInstance() + ->host('wafopenapi.cn-hangzhou.aliyuncs.com') + ->action('CreateCertAndKey') + ->setProtocol('https') + ->version('2018-01-17') + ->withCert($cert) + ->withKey($key) + ->withDomain($config['domain']) + ->withHttpsCertName($config['domain']) + ->withInstanceId($config['instanceId']) + ->request(); } } From 0d522f778c3027add58b817b79c543ff6cfc673a Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Thu, 1 Aug 2019 17:45:19 +0800 Subject: [PATCH 046/126] Update InstallAliyunWafAction.php --- src/Cli/Action/InstallAliyunWafAction.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 698df57e..3b3359f9 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -13,8 +13,6 @@ use AcmePhp\Ssl\CertificateResponse; use AlibabaCloud\Client\AlibabaCloud; -use AlibabaCloud\Client\Exception\ClientException; -use AlibabaCloud\Client\Exception\ServerException; use AlibabaCloud\WafOpenapi\WafOpenapi; /** From 505d4c2d60229e89103f49e396b792cefc5c2a86 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:00:30 +0800 Subject: [PATCH 047/126] stash --- src/Cli/Action/InstallAliyunCdnAction.php | 30 ++++++++--------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index f3680b77..a767bac5 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -13,8 +13,7 @@ use AcmePhp\Ssl\CertificateResponse; use AlibabaCloud\Client\AlibabaCloud; -use AlibabaCloud\Client\Exception\ClientException; -use AlibabaCloud\Client\Exception\ServerException; +use AlibabaCloud\Cdn\Cdn; /** * Action to install certificate in an Aliyun Waf. @@ -38,23 +37,14 @@ public function handle($config, CertificateResponse $response) $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); - try { - AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); - $response = WafOpenapi::v20161111()->upgradeInstance() - ->host('wafopenapi.cn-hangzhou.aliyuncs.com') - ->action('CreateCertAndKey') - ->setProtocol('https') - ->version('2018-01-17') - ->withCert($cert) - ->withKey($key) - ->withDomain($config['domain']) - ->withHttpsCertName($config['domain']) - ->withInstanceId($config['instanceId']) - ->request(); - } catch (ServerException $e) { - throw $e; - } catch (ClientException $e) { - throw $e; - } + AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); + Cdn::v20180510()->setDomainServerCertificate() + ->withDomainName($config['domain']) + ->withCertName($config['domain']) + ->withCertType('upload') + ->withForceSet(1) + ->withServerCertificate($cert) + ->withPrivateKey($key) + ->withServerCertificateStatus('on'); } } From 0501e56b6dedef00298394a6ef913ce731c3ebfa Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:01:24 +0800 Subject: [PATCH 048/126] register in services --- src/Cli/Resources/services.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index fa582e3f..50872554 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -122,6 +122,9 @@ + + + From 2400302f34e5f50ac3fcbe707461e7d9bfee8518 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:04:39 +0800 Subject: [PATCH 049/126] remove wrong conf --- src/Cli/Resources/services.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 4e9e0e4d..49e7985f 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -123,7 +123,6 @@ - From 3292b41a504d3a1d711c73efb15a9b2f4538c2dd Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:09:00 +0800 Subject: [PATCH 050/126] fix --- src/Cli/Action/InstallAliyunCdnAction.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index a767bac5..58cc45f3 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -45,6 +45,7 @@ public function handle($config, CertificateResponse $response) ->withForceSet(1) ->withServerCertificate($cert) ->withPrivateKey($key) - ->withServerCertificateStatus('on'); + ->withServerCertificateStatus('on') + ->request(); } } From 634ef1bf236bcf8c66b529b847fdebf5789b6c35 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:21:55 +0800 Subject: [PATCH 051/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20requestcert?= =?UTF-8?q?=E5=90=8E=20installcert=20=E6=8A=A5=E9=94=99=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Command/RunCommand.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index c85249b7..2cf9c9aa 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -80,9 +80,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->register($config['contact_email'], $keyOption); foreach ($config['certificates'] as $domainConfig) { $domain = $domainConfig['domain']; + $repository = $this->getRepository(); if ($this->isUpToDate($domain, $domainConfig, (int) $input->getOption('delay'))) { - $repository = $this->getRepository(); $certificate = $this->getRepository()->loadDomainCertificate($domain); /** @var ParsedCertificate $parsedCertificate */ $parsedCertificate = $this->getContainer()->get('ssl.certificate_parser')->parse($certificate); @@ -97,7 +97,16 @@ protected function execute(InputInterface $input, OutputInterface $output) ); } else { $order = $this->challengeDomains($domainConfig, $keyOption); - $response = $this->requestCertificate($order, $domainConfig, $keyOption); + $this->requestCertificate($order, $domainConfig, $keyOption); + + $certificate = $this->getRepository()->loadDomainCertificate($domain); + $response = new CertificateResponse( + new CertificateRequest( + $repository->loadDomainDistinguishedName($domain), + $repository->loadDomainKeyPair($domain) + ), + $certificate + ); } $this->installCertificate($domain, $response, $domainConfig['install']); From 6d17d7b91a3d6e890487279c22cb46fb65e0c8b2 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 18:30:10 +0800 Subject: [PATCH 052/126] remove useless sh --- script/build_phar.php | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 script/build_phar.php diff --git a/script/build_phar.php b/script/build_phar.php deleted file mode 100644 index 83dba671..00000000 --- a/script/build_phar.php +++ /dev/null @@ -1,9 +0,0 @@ -startBuffering(); -$phar->buildFromDirectory(__DIR__ . '/../'); -$phar->compressFiles(Phar::GZ); -$phar->setDefaultStub('bin/acme'); -$phar->setSignatureAlgorithm(Phar::SHA256); -$phar->stopBuffering(); From bce782975bad402c51068679248ba6fe69095a87 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 21:17:59 +0800 Subject: [PATCH 053/126] fixbug --- src/Cli/Action/InstallAliyunCdnAction.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index 58cc45f3..b61e9e20 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -28,6 +28,8 @@ class InstallAliyunCdnAction extends AbstractAction public function handle($config, CertificateResponse $response) { $issuerChain = []; + $issuerChain[] = $response->getCertificate()->getPEM(); + $issuerCertificate = $response->getCertificate()->getIssuerCertificate(); while (null !== $issuerCertificate) { $issuerChain[] = $issuerCertificate->getPEM(); From dcd1ad771389fa738fd759cddebf8cb480d0af3c Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 21:18:23 +0800 Subject: [PATCH 054/126] fixbug --- src/Cli/Action/InstallAliyunWafAction.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 3b3359f9..678e883a 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -28,6 +28,8 @@ class InstallAliyunWafAction extends AbstractAction public function handle($config, CertificateResponse $response) { $issuerChain = []; + $issuerChain[] = $response->getCertificate()->getPEM(); + $issuerCertificate = $response->getCertificate()->getIssuerCertificate(); while (null !== $issuerCertificate) { $issuerChain[] = $issuerCertificate->getPEM(); From ce4bb922659c8f3ed4b754b6c87c3f9334452c98 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 1 Aug 2019 21:37:14 +0800 Subject: [PATCH 055/126] style ci --- src/Cli/Action/InstallAliyunCdnAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index b61e9e20..ad3171fb 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -12,8 +12,8 @@ namespace AcmePhp\Cli\Action; use AcmePhp\Ssl\CertificateResponse; -use AlibabaCloud\Client\AlibabaCloud; use AlibabaCloud\Cdn\Cdn; +use AlibabaCloud\Client\AlibabaCloud; /** * Action to install certificate in an Aliyun Waf. From c32984657a24bfd5565528dd2174c841b499e533 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 12:20:51 +0800 Subject: [PATCH 056/126] =?UTF-8?q?=E9=87=8D=E5=90=8D=E6=97=B6=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Action/InstallAliyunWafAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index 3b3359f9..cd503596 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -46,7 +46,7 @@ public function handle($config, CertificateResponse $response) ->withCert($cert) ->withKey($key) ->withDomain($config['domain']) - ->withHttpsCertName($config['domain']) + ->withHttpsCertName($config['domain'] . '_' . date('Y_m_d_H_i_s')) ->withInstanceId($config['instanceId']) ->request(); } From 6928dbf38293592228f4d5d074e15804a2da3ed4 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 12:21:31 +0800 Subject: [PATCH 057/126] =?UTF-8?q?=E9=87=8D=E5=90=8D=E6=97=B6=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Action/InstallAliyunCdnAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index 58cc45f3..c014e5f2 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -40,7 +40,7 @@ public function handle($config, CertificateResponse $response) AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); Cdn::v20180510()->setDomainServerCertificate() ->withDomainName($config['domain']) - ->withCertName($config['domain']) + ->withCertName($config['domain'] . '_' . date('Y_m_d_H_i_s')) ->withCertType('upload') ->withForceSet(1) ->withServerCertificate($cert) From bddfd663fb4cfb6f9e77cbae80f435dfce0728d8 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 13:48:23 +0800 Subject: [PATCH 058/126] done dnspod(Tencentcloud NS) support --- composer.json | 3 +- src/Cli/Resources/services.xml | 6 + src/Core/Challenge/Dns/DnspodSolver.php | 185 ++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 src/Core/Challenge/Dns/DnspodSolver.php diff --git a/composer.json b/composer.json index 9dc27e21..277dea1d 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,8 @@ "symfony/yaml": "^3.0|^4.0", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", - "alibabacloud/wafopenapi": "^1.7" + "alibabacloud/wafopenapi": "^1.7", + "tencentyun-api/qcloudapi-sdk-php": "^2.0" }, "suggest": { "daverandom/libdns": "^2.0" diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 7e3b8d64..60b6cc38 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -191,6 +191,12 @@ + + + + + + diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php new file mode 100644 index 00000000..8eb47db9 --- /dev/null +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Dns; + +use AcmePhp\Core\Challenge\ConfigurableServiceInterface; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Webmozart\Assert\Assert; +use QcloudApi; +use function GuzzleHttp\json_decode; + +/** + * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS) + * + * @author Xiaohui Lam + */ +class DnspodSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface +{ + use LoggerAwareTrait; + /** + * @var DnsDataExtractor + */ + private $extractor; + + /** + * @var ClientInterface + */ + private $client; + + /** + * @var array + */ + private $cacheZones; + + /** + * @var string + */ + private $secretId; + + /** + * @var string + */ + private $secretKey; + + /** + * @param DnsDataExtractor $extractor + * @param ClientInterface $client + */ + public function __construct( + DnsDataExtractor $extractor = null, + ClientInterface $client = null + ) { + $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; + $this->client = null === $client ? new Client() : $client; + $this->logger = new NullLogger(); + } + + /** + * Configure the service with a set of configuration. + * + * @param array $config + */ + public function configure(array $config) + { + $this->secretId = $config['secret_id']; + $this->secretKey = $config['secret_key']; + } + + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge) + { + return 'dns-01' === $authorizationChallenge->getType(); + } + + /** + * {@inheritdoc} + */ + public function solve(AuthorizationChallenge $authorizationChallenge) + { + return $this->solveAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function solveAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + $config = array( + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz' + ); + /** + * @var \QcloudApi_Module_Cns $cns + */ + $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + + $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); + + $solve = $cns->RecordCreate([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => 'TXT', + 'recordLine' => '默认', + 'value' => $recordValue, + ]); + + if ($solve === false) { + throw $cns->getError(); + } + + $data = json_decode($cns->getLastResponse(), true); + $authorizationChallenge->recordId = $data['data']['record']['id']; + } + } + + /** + * {@inheritdoc} + */ + public function cleanup(AuthorizationChallenge $authorizationChallenge) + { + return $this->cleanupAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function cleanupAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + $config = array( + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz' + ); + /** + * @var \QcloudApi_Module_Cns $cns + */ + $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $authorizationChallenge->recordId, + ]); + } + } + + /** + * @param string $domain + * + * @return string + */ + protected function getTopLevelDomain($domain) + { + return \implode('.', \array_slice(\explode('.', $domain), -2)); + } +} From 771042030b464f0578fac2a28b49d966c397e420 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 13:54:37 +0800 Subject: [PATCH 059/126] style ci --- src/Core/Challenge/Dns/DnspodSolver.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 8eb47db9..335b2399 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -15,12 +15,12 @@ use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; +use function GuzzleHttp\json_decode; use GuzzleHttp\ClientInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; -use Webmozart\Assert\Assert; use QcloudApi; -use function GuzzleHttp\json_decode; +use Webmozart\Assert\Assert; /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS) @@ -103,13 +103,13 @@ public function solveAll(array $authorizationChallenges) Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); $config = array( - 'SecretId' => $this->secretId, - 'SecretKey' => $this->secretKey, - 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz' + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz' ); /** - * @var \QcloudApi_Module_Cns $cns + * @var \QcloudApi_Module_Cns */ $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); @@ -153,13 +153,13 @@ public function cleanupAll(array $authorizationChallenges) Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); $config = array( - 'SecretId' => $this->secretId, - 'SecretKey' => $this->secretKey, - 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz' + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz' ); /** - * @var \QcloudApi_Module_Cns $cns + * @var \QcloudApi_Module_Cns */ $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); From 5781c349ae39b1b4cf89ac655a63a384462b16a3 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 14:50:59 +0800 Subject: [PATCH 060/126] style ci --- src/Core/Challenge/Dns/DnspodSolver.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 335b2399..91093aab 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -102,12 +102,12 @@ public function solveAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); - $config = array( + $config = [ 'SecretId' => $this->secretId, 'SecretKey' => $this->secretKey, 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz' - ); + 'DefaultRegion' => 'gz', + ]; /** * @var \QcloudApi_Module_Cns */ @@ -152,12 +152,12 @@ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); - $config = array( + $config = [ 'SecretId' => $this->secretId, 'SecretKey' => $this->secretKey, 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz' - ); + 'DefaultRegion' => 'gz', + ]; /** * @var \QcloudApi_Module_Cns */ From 6a09ffb69431496f382de9b13bf5dac09eb0b4d3 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 14:52:41 +0800 Subject: [PATCH 061/126] style ci --- src/Core/Challenge/Dns/DnspodSolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 91093aab..a637c969 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -23,7 +23,7 @@ use Webmozart\Assert\Assert; /** - * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS) + * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). * * @author Xiaohui Lam */ @@ -128,7 +128,7 @@ public function solveAll(array $authorizationChallenges) 'value' => $recordValue, ]); - if ($solve === false) { + if (false === $solve) { throw $cns->getError(); } From aaf359b1127189ecda09edf0c998947d3f405a10 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 15:30:35 +0800 Subject: [PATCH 062/126] style ci --- src/Core/Challenge/Dns/DnspodSolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index a637c969..da1c578a 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -15,8 +15,8 @@ use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; -use function GuzzleHttp\json_decode; use GuzzleHttp\ClientInterface; +use function GuzzleHttp\json_decode; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use QcloudApi; From 6ec120eaccad8209715ff51b56104366ad50ab3d Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 17:59:52 +0800 Subject: [PATCH 063/126] add checkSign method --- .../Exception/DataCheckingSignException.php | 19 ++++++++++++++ src/Ssl/Signer/DataSigner.php | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/Ssl/Exception/DataCheckingSignException.php diff --git a/src/Ssl/Exception/DataCheckingSignException.php b/src/Ssl/Exception/DataCheckingSignException.php new file mode 100644 index 00000000..9dc5e4af --- /dev/null +++ b/src/Ssl/Exception/DataCheckingSignException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Ssl\Exception; + +/** + * @author Xiaohui Lam + */ +class DataCheckingSignException extends SigningException +{ +} diff --git a/src/Ssl/Signer/DataSigner.php b/src/Ssl/Signer/DataSigner.php index dbce6aca..7c18e533 100644 --- a/src/Ssl/Signer/DataSigner.php +++ b/src/Ssl/Signer/DataSigner.php @@ -14,6 +14,8 @@ use AcmePhp\Ssl\Exception\DataSigningException; use AcmePhp\Ssl\PrivateKey; use Webmozart\Assert\Assert; +use AcmePhp\Ssl\PublicKey; +use AcmePhp\Ssl\Exception\DataCheckingSignException; /** * Provide tools to sign data using a private key. @@ -66,6 +68,30 @@ public function signData($data, PrivateKey $privateKey, $algorithm = OPENSSL_ALG } } + /** + * Check sign + * + * @param string $signature + * @param string $data + * @param PublicKey $publicKey + * @param int $algorithm + * @param string $format + * @return void + */ + public function checkSign($signature, $data, PublicKey $publicKey, $algorithm = OPENSSL_ALGO_SHA256, $format = self::FORMAT_DER) + { + Assert::oneOf($format, [self::FORMAT_ECDSA, self::FORMAT_DER], 'The format %s to sign request does not exists. Available format: %s'); + + $resource = $publicKey->getResource(); + if (1 != openssl_verify($data, $signature, $resource, $algorithm)) { + throw new DataCheckingSignException( + sprintf('OpenSSL data checking sign failed with error: %s', openssl_error_string()) + ); + } + + openssl_free_key($resource); + } + /** * Convert a DER signature into ECDSA. * From 222f1848627c1ef73f559575e46c71d3ff351199 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 12:21:31 +0800 Subject: [PATCH 064/126] =?UTF-8?q?=E9=87=8D=E5=90=8D=E6=97=B6=E9=98=BF?= =?UTF-8?q?=E9=87=8C=E4=BA=91=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Action/InstallAliyunCdnAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index ad3171fb..b3eb8205 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -42,7 +42,7 @@ public function handle($config, CertificateResponse $response) AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); Cdn::v20180510()->setDomainServerCertificate() ->withDomainName($config['domain']) - ->withCertName($config['domain']) + ->withCertName($config['domain'] . '_' . date('Y_m_d_H_i_s')) ->withCertType('upload') ->withForceSet(1) ->withServerCertificate($cert) From d060f0c7f79b98051192d6abb9de47bfbf460147 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 20:04:38 +0800 Subject: [PATCH 065/126] style ci, no spacing near dot --- src/Cli/Action/InstallAliyunCdnAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunCdnAction.php b/src/Cli/Action/InstallAliyunCdnAction.php index b3eb8205..6d50a13b 100644 --- a/src/Cli/Action/InstallAliyunCdnAction.php +++ b/src/Cli/Action/InstallAliyunCdnAction.php @@ -42,7 +42,7 @@ public function handle($config, CertificateResponse $response) AlibabaCloud::accessKeyClient($config['accessKeyId'], $config['accessKeySecret'])->regionId('cn-hangzhou')->asDefaultClient(); Cdn::v20180510()->setDomainServerCertificate() ->withDomainName($config['domain']) - ->withCertName($config['domain'] . '_' . date('Y_m_d_H_i_s')) + ->withCertName($config['domain'].'_'.date('Y_m_d_H_i_s')) ->withCertType('upload') ->withForceSet(1) ->withServerCertificate($cert) From 6eacee78488be03a51af5f9a4bd27e0839731ac6 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 2 Aug 2019 20:07:47 +0800 Subject: [PATCH 066/126] style ci --- src/Cli/Action/InstallAliyunWafAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Action/InstallAliyunWafAction.php b/src/Cli/Action/InstallAliyunWafAction.php index b9496702..d71e7ef1 100644 --- a/src/Cli/Action/InstallAliyunWafAction.php +++ b/src/Cli/Action/InstallAliyunWafAction.php @@ -48,7 +48,7 @@ public function handle($config, CertificateResponse $response) ->withCert($cert) ->withKey($key) ->withDomain($config['domain']) - ->withHttpsCertName($config['domain'] . '_' . date('Y_m_d_H_i_s')) + ->withHttpsCertName($config['domain'].'_'.date('Y_m_d_H_i_s')) ->withInstanceId($config['instanceId']) ->request(); } From 1e7bbc1289c4b57ab5711d0fb835aeed7797aa8c Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 11:47:13 +0800 Subject: [PATCH 067/126] add the challenge type parameter when initOrder --- src/Cli/Command/RunCommand.php | 8 +++++++- src/Core/AcmeClient.php | 9 +++++++-- src/Core/AcmeClientV2Interface.php | 4 +++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 2cf9c9aa..645f17c5 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -259,7 +259,13 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption) $domains = array_unique(array_merge([$domain], $domainConfig['subject_alternative_names'])); $csr = null; + $challenge_type = null; if ($client->isCsrEager()) { + $challenge_type = 'http-01'; + if (substr(get_class($solver), 0, 26) == 'AcmePhp\Core\Challenge\Dns') { + $challenge_type = 'dns-01'; + } + $domain = $domainConfig['domain']; $this->output->writeln(sprintf('Requesting certificate for domain %s...', $domain)); @@ -288,7 +294,7 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption) $this->output->writeln('Requesting certificate order...'); } - $order = $client->requestOrder($domains, $csr); + $order = $client->requestOrder($domains, $csr, $challenge_type); $authorizationChallengesToSolve = []; foreach ($order->getAuthorizationsChallenges() as $domain => $authorizationChallenges) { diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 83f75f3e..72031d51 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -131,7 +131,7 @@ public function requestAuthorization($domain) /** * {@inheritdoc} */ - public function requestOrder(array $domains, $csr = null) + public function requestOrder(array $domains, $csr = null, $challenge_type = null) { Assert::allStringNotEmpty($domains, 'requestOrder::$domains expected a list of strings. Got: %s'); @@ -153,8 +153,13 @@ function ($domain) { }, array_values($domains) ), - 'csr' => $csrContent, ]; + if ($csrContent) { + $payload['csr'] = $csrContent; + } + if ($challenge_type) { + $payload['challenge_type'] = $challenge_type; + } $response = $this->getHttpClient()->signedKidRequest('POST', $this->getResourceUrl(ResourcesDirectory::NEW_ORDER), $this->getResourceAccount(), $payload); if (!isset($response['authorizations']) || !$response['authorizations']) { diff --git a/src/Core/AcmeClientV2Interface.php b/src/Core/AcmeClientV2Interface.php index 1147649e..f94d3fcc 100644 --- a/src/Core/AcmeClientV2Interface.php +++ b/src/Core/AcmeClientV2Interface.php @@ -36,6 +36,8 @@ interface AcmeClientV2Interface extends AcmeClientInterface * to expose the payload for the verification (see challengeAuthorization). * * @param string[] $domains the domains to challenge + * @param CertificateRequest|null $csr + * @param string|null $challenge_type * * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * (the exception will be more specific if detail is provided) @@ -44,7 +46,7 @@ interface AcmeClientV2Interface extends AcmeClientInterface * * @return CertificateOrder the Order returned by the Certificate Authority */ - public function requestOrder(array $domains, $csr = null); + public function requestOrder(array $domains, $csr = null, $challenge_type = null); /** * Request a certificate for the given domain. From 697a6ec7087d9c76411ca794c12721074c5957ed Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 12:40:16 +0800 Subject: [PATCH 068/126] update --- src/Core/Challenge/Dns/DnsDataExtractor.php | 21 +++++++++++++++++++++ src/Core/Challenge/Dns/DnspodSolver.php | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/DnsDataExtractor.php b/src/Core/Challenge/Dns/DnsDataExtractor.php index 6ce2ea5a..e694be8b 100644 --- a/src/Core/Challenge/Dns/DnsDataExtractor.php +++ b/src/Core/Challenge/Dns/DnsDataExtractor.php @@ -43,6 +43,9 @@ public function __construct(Base64SafeEncoder $encoder = null) */ public function getRecordName(AuthorizationChallenge $authorizationChallenge) { + if ($authorizationChallenge->getToken()) { + return $authorizationChallenge->getToken(); + } return sprintf('_acme-challenge.%s.', $authorizationChallenge->getDomain()); } @@ -55,6 +58,24 @@ public function getRecordName(AuthorizationChallenge $authorizationChallenge) */ public function getRecordValue(AuthorizationChallenge $authorizationChallenge) { + if ($authorizationChallenge->getFilecontent()) { + return $authorizationChallenge->getFilecontent(); + } return $this->encoder->encode(hash('sha256', $authorizationChallenge->getPayload(), true)); } + + /** + * Retrieves the value of the NS Type + * + * @param AuthorizationChallenge $authorizationChallenge + * + * @return string + */ + public function getRecordType(AuthorizationChallenge $authorizationChallenge) + { + if ($authorizationChallenge->getPath()) { + return $authorizationChallenge->getPath(); + } + return 'TXT'; + } } diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index da1c578a..d2644e2f 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -123,7 +123,7 @@ public function solveAll(array $authorizationChallenges) $solve = $cns->RecordCreate([ 'domain' => $topLevelDomain, 'subDomain' => $subDomain, - 'recordType' => 'TXT', + 'recordType' => $this->extractor->getRecordType($authorizationChallenge), 'recordLine' => '默认', 'value' => $recordValue, ]); From ce403661bd8adb730101b0f514f8f45b7ea628d3 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 12:41:45 +0800 Subject: [PATCH 069/126] fixbug --- src/Core/Challenge/Dns/DnspodSolver.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index da1c578a..fe0cc787 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -21,6 +21,7 @@ use Psr\Log\NullLogger; use QcloudApi; use Webmozart\Assert\Assert; +use Exception; /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). @@ -129,7 +130,11 @@ public function solveAll(array $authorizationChallenges) ]); if (false === $solve) { - throw $cns->getError(); + /** + * @var \QcloudApi_Common_Error + */ + $err = $cns->getError(); + throw new Exception($err->getMessage(), $err->getCode()); } $data = json_decode($cns->getLastResponse(), true); From 144c49b11efc3c4f74d10d0aeab8904e977c60b4 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 14:17:06 +0800 Subject: [PATCH 070/126] fix --- src/Core/Challenge/Dns/DnsDataExtractor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Challenge/Dns/DnsDataExtractor.php b/src/Core/Challenge/Dns/DnsDataExtractor.php index e694be8b..c04e1786 100644 --- a/src/Core/Challenge/Dns/DnsDataExtractor.php +++ b/src/Core/Challenge/Dns/DnsDataExtractor.php @@ -43,7 +43,7 @@ public function __construct(Base64SafeEncoder $encoder = null) */ public function getRecordName(AuthorizationChallenge $authorizationChallenge) { - if ($authorizationChallenge->getToken()) { + if (trim($authorizationChallenge->getFilecontent())) { return $authorizationChallenge->getToken(); } return sprintf('_acme-challenge.%s.', $authorizationChallenge->getDomain()); @@ -58,7 +58,7 @@ public function getRecordName(AuthorizationChallenge $authorizationChallenge) */ public function getRecordValue(AuthorizationChallenge $authorizationChallenge) { - if ($authorizationChallenge->getFilecontent()) { + if (trim($authorizationChallenge->getFilecontent())) { return $authorizationChallenge->getFilecontent(); } return $this->encoder->encode(hash('sha256', $authorizationChallenge->getPayload(), true)); From 5307d8465bfc28dbd62c3b11b7a6ec54f7844094 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 14:39:04 +0800 Subject: [PATCH 071/126] getTopLevelDomain func born from GandiSolver isn't reliable, should extract from public suffix list instead --- composer.json | 3 +- src/Core/Challenge/Dns/DnspodSolver.php | 16 +++------- src/Core/Challenge/Dns/GandiSolver.php | 13 ++------ .../Dns/Traits/TopLevelDomainTrait.php | 30 +++++++++++++++++++ 4 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php diff --git a/composer.json b/composer.json index 277dea1d..d7e5f50f 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,8 @@ "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", "alibabacloud/wafopenapi": "^1.7", - "tencentyun-api/qcloudapi-sdk-php": "^2.0" + "tencentyun-api/qcloudapi-sdk-php": "^2.0", + "layershifter/tld-extract": "^2.0" }, "suggest": { "daverandom/libdns": "^2.0" diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index fe0cc787..efae9e3f 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -13,7 +13,9 @@ use AcmePhp\Core\Challenge\ConfigurableServiceInterface; use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; use AcmePhp\Core\Protocol\AuthorizationChallenge; +use Exception; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use function GuzzleHttp\json_decode; @@ -21,7 +23,6 @@ use Psr\Log\NullLogger; use QcloudApi; use Webmozart\Assert\Assert; -use Exception; /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). @@ -30,7 +31,8 @@ */ class DnspodSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface { - use LoggerAwareTrait; + use LoggerAwareTrait, TopLevelDomainTrait; + /** * @var DnsDataExtractor */ @@ -177,14 +179,4 @@ public function cleanupAll(array $authorizationChallenges) ]); } } - - /** - * @param string $domain - * - * @return string - */ - protected function getTopLevelDomain($domain) - { - return \implode('.', \array_slice(\explode('.', $domain), -2)); - } } diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index f1e98a78..a5b070c3 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -13,6 +13,7 @@ use AcmePhp\Core\Challenge\ConfigurableServiceInterface; use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; @@ -27,7 +28,7 @@ */ class GandiSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface { - use LoggerAwareTrait; + use LoggerAwareTrait, TopLevelDomainTrait; /** * @var DnsDataExtractor */ @@ -151,14 +152,4 @@ public function cleanupAll(array $authorizationChallenges) ); } } - - /** - * @param string $domain - * - * @return string - */ - protected function getTopLevelDomain($domain) - { - return \implode('.', \array_slice(\explode('.', $domain), -2)); - } } diff --git a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php new file mode 100644 index 00000000..046708a4 --- /dev/null +++ b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Dns\Traits; + +use LayerShifter\TLDExtract\Extract as TLDExtract; + +trait TopLevelDomainTrait +{ + /** + * @param string $domain + * + * @return string + */ + protected function getTopLevelDomain($domain) + { + $extract = new TLDExtract(); + $parse = $extract->parse($domain); + + return $parse->getRegistrableDomain(); + } +} From 2e6f3ca69062f2b02222aa6bf24ec5a085911acf Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 14:41:31 +0800 Subject: [PATCH 072/126] style ci --- src/Core/Challenge/Dns/DnspodSolver.php | 2 +- src/Core/Challenge/Dns/GandiSolver.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index efae9e3f..968c118a 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -12,8 +12,8 @@ namespace AcmePhp\Core\Challenge\Dns; use AcmePhp\Core\Challenge\ConfigurableServiceInterface; -use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use Exception; use GuzzleHttp\Client; diff --git a/src/Core/Challenge/Dns/GandiSolver.php b/src/Core/Challenge/Dns/GandiSolver.php index a5b070c3..70564755 100644 --- a/src/Core/Challenge/Dns/GandiSolver.php +++ b/src/Core/Challenge/Dns/GandiSolver.php @@ -12,8 +12,8 @@ namespace AcmePhp\Core\Challenge\Dns; use AcmePhp\Core\Challenge\ConfigurableServiceInterface; -use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; use AcmePhp\Core\Protocol\AuthorizationChallenge; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; From e341ca7a929154d113a1723093d9c7f415bb7129 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 15:47:43 +0800 Subject: [PATCH 073/126] fix wildcard small pattern problem --- src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php index 046708a4..156a6b38 100644 --- a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php +++ b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php @@ -11,6 +11,7 @@ namespace AcmePhp\Core\Challenge\Dns\Traits; +use LayerShifter\TLDDatabase\Exceptions\ParserException; use LayerShifter\TLDExtract\Extract as TLDExtract; trait TopLevelDomainTrait @@ -23,7 +24,10 @@ trait TopLevelDomainTrait protected function getTopLevelDomain($domain) { $extract = new TLDExtract(); - $parse = $extract->parse($domain); + $parse = $extract->parse(str_replace('*.', '', $domain)); + if (!$parse->isValidDomain()) { + throw new ParserException($domain . ' is not a valid domain', 1); + } return $parse->getRegistrableDomain(); } From 8b1597ccf9172b57338452eb1229893e8973a14e Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 3 Aug 2019 15:50:11 +0800 Subject: [PATCH 074/126] style ci --- src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php index 156a6b38..f9178c50 100644 --- a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php +++ b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php @@ -26,7 +26,7 @@ protected function getTopLevelDomain($domain) $extract = new TLDExtract(); $parse = $extract->parse(str_replace('*.', '', $domain)); if (!$parse->isValidDomain()) { - throw new ParserException($domain . ' is not a valid domain', 1); + throw new ParserException($domain.' is not a valid domain', 1); } return $parse->getRegistrableDomain(); From ce561b0b951b7ea954df14e4fcc1e5911d450bc9 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 6 Aug 2019 17:47:08 +0800 Subject: [PATCH 075/126] enhancement --- src/Core/Http/SecureHttpClient.php | 30 ++------------------------- src/Ssl/Signer/DataSigner.php | 33 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 07f35d13..5252be46 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -119,7 +119,7 @@ public function signedRequest($method, $endpoint, array $payload = [], $returnJs 'nonce' => $this->getNonce(), 'url' => $endpoint, ]; - list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); + list($algorithm, $format) = $this->dataSigner->extractSignOptionFromJWSAlg($alg); $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); @@ -159,32 +159,6 @@ private function getAlg() } } - private function extractSignOptionFromJWSAlg($alg) - { - if (!preg_match('/^([A-Z]+)(\d+)$/', $alg, $match)) { - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - if (!\defined('OPENSSL_ALGO_SHA'.$match[2])) { - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - $algorithm = \constant('OPENSSL_ALGO_SHA'.$match[2]); - - switch ($match[1]) { - case 'RS': - $format = DataSigner::FORMAT_DER; - break; - case 'ES': - $format = DataSigner::FORMAT_ECDSA; - break; - default: - throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); - } - - return [$algorithm, $format]; - } - public function getJWK() { $privateKey = $this->accountKeyPair->getPrivateKey(); @@ -241,7 +215,7 @@ public function signedKidRequest($method, $endpoint, $account, array $payload = 'nonce' => $this->getNonce(), 'url' => $endpoint, ]; - list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); + list($algorithm, $format) = $this->dataSigner->extractSignOptionFromJWSAlg($alg); $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); if ($payload === []) { diff --git a/src/Ssl/Signer/DataSigner.php b/src/Ssl/Signer/DataSigner.php index 7c18e533..56db1d76 100644 --- a/src/Ssl/Signer/DataSigner.php +++ b/src/Ssl/Signer/DataSigner.php @@ -16,6 +16,7 @@ use Webmozart\Assert\Assert; use AcmePhp\Ssl\PublicKey; use AcmePhp\Ssl\Exception\DataCheckingSignException; +use AcmePhp\Core\Exception\AcmeCoreClientException; /** * Provide tools to sign data using a private key. @@ -159,4 +160,36 @@ private function retrievePositiveInteger($data) return $data; } + + /** + * Extract Sign Option From Jws Alg + * + * @param string $alg + * @return array + */ + public function extractSignOptionFromJWSAlg($alg) + { + if (!preg_match('/^([A-Z]+)(\d+)$/', $alg, $match)) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + if (!\defined('OPENSSL_ALGO_SHA' . $match[2])) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + $algorithm = \constant('OPENSSL_ALGO_SHA' . $match[2]); + + switch ($match[1]) { + case 'RS': + $format = static::FORMAT_DER; + break; + case 'ES': + $format = static::FORMAT_ECDSA; + break; + default: + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + return [$algorithm, $format]; + } } From fe15f153703b441f10c261ae9868f952f71b4401 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 7 Aug 2019 14:17:08 +0800 Subject: [PATCH 076/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDNSPod=E5=AF=B9?= =?UTF-8?q?=E4=BA=8E=E5=90=8C=E4=B8=80=E4=B8=BB=E6=9C=BA=E5=90=8DCNAME?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E5=85=B1=E5=AD=98=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/DnspodSolver.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 1e0aa4dc..2942bb23 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -123,10 +123,32 @@ public function solveAll(array $authorizationChallenges) $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); + $recordType = $this->extractor->getRecordType($authorizationChallenge); + + if (strtolower($recordType) == 'cname') { + // 因为 DNSPod 免费版套餐 同一名称 cname 不能并存 + // 所以要删除旧的 + + $cns->RecordList([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => $recordType, + ]); + $data = json_decode($cns->getLastResponse(), true); + if ($data && isset($data['data']) && isset($data['data']['records']) && is_array($data['data']['records']) && count($data['data']['records'])) { + $existedRecord = $data['data']['records'][0]; + if (isset($existedRecord['id'])) { + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $existedRecord['id'], + ]); + } + } + } $solve = $cns->RecordCreate([ 'domain' => $topLevelDomain, 'subDomain' => $subDomain, - 'recordType' => $this->extractor->getRecordType($authorizationChallenge), + 'recordType' => $recordType, 'recordLine' => '默认', 'value' => $recordValue, ]); From dcf3aebf86f8dab2ad60709f38bbf233708c0010 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 7 Aug 2019 21:33:24 +0800 Subject: [PATCH 077/126] fixbug --- src/Core/Challenge/Dns/AliyunSolver.php | 34 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index ab6d110f..bf8beb38 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -107,22 +107,42 @@ public function solveAll(array $authorizationChallenges) $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + $recordType = isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT'; + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); $dns = new Alidns(); - $do = $dns->v20150109()->addDomainRecord() - ->withDomainName('a.com') - ->withType('TXT') - ->withRR('bb') - ->withValue('aaa') - ->request(); + + if (strtolower($recordType) == 'cname') { + /** + * @var \AlibabaCloud\Client\Result\Result $list + */ + $list = $dns->v20150109()->describeSubDomainRecords() + ->withSubDomain($subDomain) + ->withType($recordType) + ->withPageSize(100) + ->request(); + + $records = $list->get('DomainRecords'); + $records = isset($records['Record']) ? $records['Record'] : $records; + + foreach ($records as $record) { + try { + $recordId = $record['RecordId']; + $dns->v20150109()->deleteDomainRecord() + ->withRecordId($recordId) + ->request(); + } catch (\Exception $e) { + } + } + } /** * @var \AlibabaCloud\Client\Result\Result $response */ $response = $dns->v20150109()->addDomainRecord() ->withDomainName($topLevelDomain) - ->withType(isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT') + ->withType($recordType) ->withRR($subDomain) ->withValue($recordValue) ->request(); From 06dcff782cdd30db694211496e9e5cdc6c432357 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 15 Aug 2019 15:04:18 +0800 Subject: [PATCH 078/126] resolved #19 --- .../Action/InstallTencentcloudCdnAction.php | 123 ++++++++++++++++++ src/Cli/Resources/services.xml | 3 + 2 files changed, 126 insertions(+) create mode 100644 src/Cli/Action/InstallTencentcloudCdnAction.php diff --git a/src/Cli/Action/InstallTencentcloudCdnAction.php b/src/Cli/Action/InstallTencentcloudCdnAction.php new file mode 100644 index 00000000..10cfa409 --- /dev/null +++ b/src/Cli/Action/InstallTencentcloudCdnAction.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Cli\Action; + +use AcmePhp\Ssl\CertificateResponse; +use Exception; +use function GuzzleHttp\json_decode; + +/** + * Action to install certificate in an Aliyun Waf. + * + * @author Xiaohui Lam + */ +class InstallTencentcloudCdnAction extends AbstractAction +{ + /** + * {@inheritdoc} + */ + public function handle($config, CertificateResponse $response) + { + $issuerChain = []; + $issuerChain[] = $response->getCertificate()->getPEM(); + + $issuerCertificate = $response->getCertificate()->getIssuerCertificate(); + while (null !== $issuerCertificate) { + $issuerChain[] = $issuerCertificate->getPEM(); + $issuerCertificate = $issuerCertificate->getIssuerCertificate(); + } + $cert = implode("\n", $issuerChain); + + $key = $response->getCertificateRequest()->getKeyPair()->getPrivateKey()->getPEM(); + + $data = [ + 'host' => $config['host'], + 'httpsType' => $config['https_type'], + 'cert' => $cert, + 'privateKey' => $key, + ]; + + $response = $this->_createRequest($config['secret_id'], $config['secret_key'], $data, true); + if (!isset($response['code'])) { + throw new Exception('Install fail!', 1); + } + if ($response['code'] != 0) { + throw new Exception((isset($response['codeDesc']) ? $response['codeDesc'] : '') . (isset($response['message']) ? $response['message'] : ''), $response['code']); + } + } + + protected function _createRequest($secretId, $secretKey, $privateParams, $isHttps) + { + $HttpMethod = 'POST'; + + $commonParams = [ + 'Nonce' => rand(), + 'Timestamp' => time(NULL), + 'Action' => 'SetHttpsInfo', + 'SecretId' => $secretId, + ]; + + $HttpUrl = 'cdn.api.qcloud.com'; + $FullHttpUrl = $HttpUrl . "/v2/index.php"; + $ReqParaArray = array_merge($commonParams, $privateParams); + ksort($ReqParaArray); + + $SigTxt = $HttpMethod . $FullHttpUrl . "?"; + $isFirst = true; + foreach ($ReqParaArray as $key => $value) { + if (!$isFirst) { + $SigTxt = $SigTxt . "&"; + } + $isFirst = false; + if (strpos($key, '_')) { + $key = str_replace('_', '.', $key); + } + $SigTxt = $SigTxt . $key . "=" . $value; + } + + $Signature = base64_encode(hash_hmac('sha1', $SigTxt, $secretKey, true)); + + $Req = "Signature=" . urlencode($Signature); + foreach ($ReqParaArray as $key => $value) { + $Req = $Req . "&" . $key . "=" . urlencode($value); + } + + if ($HttpMethod === 'GET') { + if ($isHttps) { + $Req = "https://" . $FullHttpUrl . "?" . $Req; + } else { + $Req = "http://" . $FullHttpUrl . "?" . $Req; + } + $Rsp = file_get_contents($Req); + } else { + if ($isHttps) { + $Rsp = $this->_sendPost("https://" . $FullHttpUrl, $Req); + } else { + $Rsp = $this->_sendPost("http://" . $FullHttpUrl, $Req); + } + } + + return json_decode($Rsp, true); + } + + protected function _sendPost($FullHttpUrl, $Req) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $Req); + curl_setopt($ch, CURLOPT_URL, $FullHttpUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $result = curl_exec($ch); + return $result; + } +} diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 02c76a17..31cab43b 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -128,6 +128,9 @@ + + + From 44a6aee02200ef6a323e5c876d38f02f142b8a55 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 28 Aug 2019 10:36:36 +0800 Subject: [PATCH 079/126] fixbug --- bin/acme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/acme b/bin/acme index b9c0dd24..52556aa8 100755 --- a/bin/acme +++ b/bin/acme @@ -26,8 +26,8 @@ if (!class_exists('DOMDocument')) { } $autoload = [ - __DIR__.'/../../../autoload.php', __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', __DIR__.'/vendor/autoload.php', ]; From bdc1e9e37462e318139b8c3c375a86dfb8c1ad1f Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sun, 6 Oct 2019 18:18:02 +0800 Subject: [PATCH 080/126] fixbug --- src/Core/Challenge/Dns/AliyunSolver.php | 46 ++++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index bf8beb38..70be3842 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -107,33 +107,39 @@ public function solveAll(array $authorizationChallenges) $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); - $recordType = isset($authorizationChallenge['dnsType']) ? $authorizationChallenge['dnsType'] : 'TXT'; + $recordType = $authorizationChallenge->getPath(); + if (!$recordType) { + $recordType = 'TXT'; + } $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); $dns = new Alidns(); if (strtolower($recordType) == 'cname') { - /** - * @var \AlibabaCloud\Client\Result\Result $list - */ - $list = $dns->v20150109()->describeSubDomainRecords() - ->withSubDomain($subDomain) - ->withType($recordType) - ->withPageSize(100) - ->request(); - - $records = $list->get('DomainRecords'); - $records = isset($records['Record']) ? $records['Record'] : $records; - - foreach ($records as $record) { - try { - $recordId = $record['RecordId']; - $dns->v20150109()->deleteDomainRecord() - ->withRecordId($recordId) - ->request(); - } catch (\Exception $e) { + try { + /** + * @var \AlibabaCloud\Client\Result\Result $list + */ + $list = $dns->v20150109()->describeSubDomainRecords() + ->withSubDomain($subDomain) + ->withType($recordType) + ->withPageSize(100) + ->request(); + + $records = $list->get('DomainRecords'); + $records = isset($records['Record']) ? $records['Record'] : $records; + + foreach ($records as $record) { + try { + $recordId = $record['RecordId']; + $dns->v20150109()->deleteDomainRecord() + ->withRecordId($recordId) + ->request(); + } catch (\Exception $e) { + } } + } catch (\Exception $e) { } } From 8e1703762d82eef0dc76655db9ae262e625c5a2e Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Sun, 6 Oct 2019 18:21:11 +0800 Subject: [PATCH 081/126] Update upload_phar.sh --- script/upload_phar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index e3ac8791..ed03d360 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,2 @@ -curl -X DELETE $(curl https://api.github.com/repos/trustocean/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed -curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/trustocean/acme-client/releases/18962377/assets?name=acmephp.phar' -s \ No newline at end of file +curl -X DELETE $(curl https://api.github.com/repos/digitalsign/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed +curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/digitalsign/acme-client/releases/18962377/assets?name=acmephp.phar' -s From 11a656875d888b0414d9eaef3086267c0b91a250 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sun, 6 Oct 2019 18:41:19 +0800 Subject: [PATCH 082/126] fixbug --- src/Core/Challenge/Dns/AliyunSolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index 70be3842..a779e27e 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -122,7 +122,7 @@ public function solveAll(array $authorizationChallenges) * @var \AlibabaCloud\Client\Result\Result $list */ $list = $dns->v20150109()->describeSubDomainRecords() - ->withSubDomain($subDomain) + ->withSubDomain($subDomain . '.' . $topLevelDomain) ->withType($recordType) ->withPageSize(100) ->request(); From 5f47ad79a236bf06fcbd2aa1ea3ef8a20bc1558c Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sun, 6 Oct 2019 18:18:02 +0800 Subject: [PATCH 083/126] fixbug --- src/Core/Challenge/Dns/AliyunSolver.php | 38 +++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index ab6d110f..d2ac618d 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -107,15 +107,41 @@ public function solveAll(array $authorizationChallenges) $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); $recordName = $this->extractor->getRecordName($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + $recordType = $authorizationChallenge->getPath(); + if (!$recordType) { + $recordType = 'TXT'; + } + $subDomain = \str_replace('.' . $topLevelDomain . '.', '', $recordName); $dns = new Alidns(); - $do = $dns->v20150109()->addDomainRecord() - ->withDomainName('a.com') - ->withType('TXT') - ->withRR('bb') - ->withValue('aaa') - ->request(); + + if (strtolower($recordType) == 'cname') { + try { + /** + * @var \AlibabaCloud\Client\Result\Result $list + */ + $list = $dns->v20150109()->describeSubDomainRecords() + ->withSubDomain($subDomain) + ->withType($recordType) + ->withPageSize(100) + ->request(); + + $records = $list->get('DomainRecords'); + $records = isset($records['Record']) ? $records['Record'] : $records; + + foreach ($records as $record) { + try { + $recordId = $record['RecordId']; + $dns->v20150109()->deleteDomainRecord() + ->withRecordId($recordId) + ->request(); + } catch (\Exception $e) { + } + } + } catch (\Exception $e) { + } + } /** * @var \AlibabaCloud\Client\Result\Result $response From 51ed68d5bffb083b41f7a16b47e123fbe7afa13d Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sun, 6 Oct 2019 18:41:19 +0800 Subject: [PATCH 084/126] fixbug --- src/Core/Challenge/Dns/AliyunSolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/AliyunSolver.php b/src/Core/Challenge/Dns/AliyunSolver.php index d2ac618d..8320b11f 100644 --- a/src/Core/Challenge/Dns/AliyunSolver.php +++ b/src/Core/Challenge/Dns/AliyunSolver.php @@ -122,7 +122,7 @@ public function solveAll(array $authorizationChallenges) * @var \AlibabaCloud\Client\Result\Result $list */ $list = $dns->v20150109()->describeSubDomainRecords() - ->withSubDomain($subDomain) + ->withSubDomain($subDomain . '.' . $topLevelDomain) ->withType($recordType) ->withPageSize(100) ->request(); From b320d721dde0fee22d7cd99df523e0e6bb788e7c Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 6 Nov 2019 12:04:31 +0800 Subject: [PATCH 085/126] Fix unresolvable check url bug --- src/Core/Challenge/Http/HttpDataExtractor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Http/HttpDataExtractor.php b/src/Core/Challenge/Http/HttpDataExtractor.php index 468108b2..7b6c228f 100644 --- a/src/Core/Challenge/Http/HttpDataExtractor.php +++ b/src/Core/Challenge/Http/HttpDataExtractor.php @@ -31,7 +31,7 @@ public function getCheckUrl(AuthorizationChallenge $authorizationChallenge) { return sprintf( 'http://%s%s', - $authorizationChallenge->getDomain(), + preg_replace('*.', '', $authorizationChallenge->getDomain()), $this->getCheckPath($authorizationChallenge) ); } From ea0a21c9f57ef2fc37f800285001232ecee909a8 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 6 Nov 2019 12:06:32 +0800 Subject: [PATCH 086/126] fixbug --- src/Core/Challenge/Http/HttpDataExtractor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Challenge/Http/HttpDataExtractor.php b/src/Core/Challenge/Http/HttpDataExtractor.php index 7b6c228f..4cf7e31a 100644 --- a/src/Core/Challenge/Http/HttpDataExtractor.php +++ b/src/Core/Challenge/Http/HttpDataExtractor.php @@ -31,7 +31,7 @@ public function getCheckUrl(AuthorizationChallenge $authorizationChallenge) { return sprintf( 'http://%s%s', - preg_replace('*.', '', $authorizationChallenge->getDomain()), + preg_replace('/^\*\./', '', $authorizationChallenge->getDomain()), $this->getCheckPath($authorizationChallenge) ); } From c43a2345b1d88654efc4317e670900e4edc2e386 Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Wed, 13 Nov 2019 14:10:43 +0800 Subject: [PATCH 087/126] Update DistinguishedName.php --- src/Ssl/DistinguishedName.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Ssl/DistinguishedName.php b/src/Ssl/DistinguishedName.php index 1eb8ca6e..e4888e7c 100644 --- a/src/Ssl/DistinguishedName.php +++ b/src/Ssl/DistinguishedName.php @@ -64,17 +64,17 @@ public function __construct( $emailAddress = null, array $subjectAlternativeNames = [] ) { - Assert::stringNotEmpty($commonName, __CLASS__.'::$commonName expected a non empty string. Got: %s'); - Assert::nullOrString($countryName, __CLASS__.'::$countryName expected a string. Got: %s'); - Assert::nullOrString($stateOrProvinceName, __CLASS__.'::$stateOrProvinceName expected a string. Got: %s'); - Assert::nullOrString($localityName, __CLASS__.'::$localityName expected a string. Got: %s'); - Assert::nullOrString($organizationName, __CLASS__.'::$organizationName expected a string. Got: %s'); - Assert::nullOrString($organizationalUnitName, __CLASS__.'::$organizationalUnitName expected a string. Got: %s'); - Assert::nullOrString($emailAddress, __CLASS__.'::$emailAddress expected a string. Got: %s'); - Assert::allStringNotEmpty( - $subjectAlternativeNames, - __CLASS__.'::$subjectAlternativeNames expected an array of non empty string. Got: %s' - ); + //Assert::stringNotEmpty($commonName, __CLASS__.'::$commonName expected a non empty string. Got: %s'); + //Assert::nullOrString($countryName, __CLASS__.'::$countryName expected a string. Got: %s'); + //Assert::nullOrString($stateOrProvinceName, __CLASS__.'::$stateOrProvinceName expected a string. Got: %s'); + //Assert::nullOrString($localityName, __CLASS__.'::$localityName expected a string. Got: %s'); + //Assert::nullOrString($organizationName, __CLASS__.'::$organizationName expected a string. Got: %s'); + //Assert::nullOrString($organizationalUnitName, __CLASS__.'::$organizationalUnitName expected a string. Got: %s'); + //Assert::nullOrString($emailAddress, __CLASS__.'::$emailAddress expected a string. Got: %s'); + //Assert::allStringNotEmpty( + //$subjectAlternativeNames, + //__CLASS__.'::$subjectAlternativeNames expected an array of non empty string. Got: %s' + //); $this->commonName = $commonName; $this->countryName = $countryName; From d57efaadd6d821ae365b52c25e1e95126e536d18 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 14 Nov 2019 11:57:00 +0800 Subject: [PATCH 088/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDNSPod=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/DnspodSolver.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 2942bb23..47b8ea18 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -136,12 +136,13 @@ public function solveAll(array $authorizationChallenges) ]); $data = json_decode($cns->getLastResponse(), true); if ($data && isset($data['data']) && isset($data['data']['records']) && is_array($data['data']['records']) && count($data['data']['records'])) { - $existedRecord = $data['data']['records'][0]; - if (isset($existedRecord['id'])) { - $cns->RecordDelete([ - 'domain' => $topLevelDomain, - 'recordId' => $existedRecord['id'], - ]); + foreach ($data['data']['records'] as $existedRecord) { + if (isset($existedRecord['id'])) { + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $existedRecord['id'], + ]); + } } } } From 9e015039019ae2b5e6d5407d72cbbcac42177785 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 14 Nov 2019 11:57:00 +0800 Subject: [PATCH 089/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8DDNSPod=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/DnspodSolver.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 968c118a..3828cfee 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -123,6 +123,29 @@ public function solveAll(array $authorizationChallenges) $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); + $recordType = $this->extractor->getRecordType($authorizationChallenge); + + if (strtolower($recordType) == 'cname') { + // 因为 DNSPod 免费版套餐 同一名称 cname 不能并存 + // 所以要删除旧的 + + $cns->RecordList([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => $recordType, + ]); + $data = json_decode($cns->getLastResponse(), true); + if ($data && isset($data['data']) && isset($data['data']['records']) && is_array($data['data']['records']) && count($data['data']['records'])) { + foreach ($data['data']['records'] as $existedRecord) { + if (isset($existedRecord['id'])) { + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $existedRecord['id'], + ]); + } + } + } + } $solve = $cns->RecordCreate([ 'domain' => $topLevelDomain, 'subDomain' => $subDomain, From 2ff71f7a44d7222ff5331b416e494a4f2a7b3b55 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 15 Nov 2019 11:30:17 +0800 Subject: [PATCH 090/126] fixbug --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 509c97fb..28b78988 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,7 @@ "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", "alibabacloud/wafopenapi": "^1.7", - "alibabacloud/cdn": "^1.7", + "alibabacloud/cdn": "^1.7", "tencentyun-api/qcloudapi-sdk-php": "^2.0", "layershifter/tld-extract": "^2.0" }, From 6c390f6026a030f88de787ce7c7b1e04add2598a Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 15 Nov 2019 15:24:45 +0800 Subject: [PATCH 091/126] fix dns check does not support cname bug --- src/Core/AcmeClient.php | 3 +- src/Core/Challenge/Dns/DnsDataExtractor.php | 15 ++++++ .../Challenge/Dns/DnsResolverInterface.php | 10 ++++ src/Core/Challenge/Dns/DnsValidator.php | 10 +++- src/Core/Challenge/Dns/LibDnsResolver.php | 54 +++++++++++++++++++ src/Core/Challenge/Dns/SimpleDnsResolver.php | 31 +++++++++++ src/Core/Protocol/AuthorizationChallenge.php | 17 +++++- 7 files changed, 136 insertions(+), 4 deletions(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 72031d51..1977b044 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -394,7 +394,8 @@ private function createAuthorizationChallenge($domain, array $response) isset($response['filecontent']) ? $response['filecontent'] : ($response['token'].'.'.$base64encoder->encode($this->getHttpClient()->getJWKThumbprint())), isset($response['path']) ? $response['path'] : null, isset($response['verifyurl']) ? $response['verifyurl'] : null, - isset($response['filecontent']) ? $response['filecontent'] : null + isset($response['filecontent']) ? $response['filecontent'] : null, + isset($response['fqdn']) ? $response['fqdn'] : null ); } } diff --git a/src/Core/Challenge/Dns/DnsDataExtractor.php b/src/Core/Challenge/Dns/DnsDataExtractor.php index c04e1786..aa6ccb51 100644 --- a/src/Core/Challenge/Dns/DnsDataExtractor.php +++ b/src/Core/Challenge/Dns/DnsDataExtractor.php @@ -49,6 +49,21 @@ public function getRecordName(AuthorizationChallenge $authorizationChallenge) return sprintf('_acme-challenge.%s.', $authorizationChallenge->getDomain()); } + /** + * Retrieves the fqdn name of the TXT record to register. + * + * @param AuthorizationChallenge $authorizationChallenge + * + * @return string + */ + public function getRecordFqdn(AuthorizationChallenge $authorizationChallenge) + { + if (!$authorizationChallenge->getFqdn()) { + return $this->getRecordName($authorizationChallenge); + } + return $authorizationChallenge->getFqdn(); + } + /** * Retrieves the value of the TXT record to register. * diff --git a/src/Core/Challenge/Dns/DnsResolverInterface.php b/src/Core/Challenge/Dns/DnsResolverInterface.php index b44bf94e..54671f73 100644 --- a/src/Core/Challenge/Dns/DnsResolverInterface.php +++ b/src/Core/Challenge/Dns/DnsResolverInterface.php @@ -27,6 +27,16 @@ interface DnsResolverInterface */ public function getTxtEntries($domain); + /** + * Retrieves the list of specific type entries for the given domain. + * + * @param string $type + * @param string $domain + * + * @return array + */ + public function getEnteries($type, $domain); + /** * Return whether or not the Resolver is supported. * diff --git a/src/Core/Challenge/Dns/DnsValidator.php b/src/Core/Challenge/Dns/DnsValidator.php index 56198bcd..af53c08a 100644 --- a/src/Core/Challenge/Dns/DnsValidator.php +++ b/src/Core/Challenge/Dns/DnsValidator.php @@ -55,11 +55,17 @@ public function supports(AuthorizationChallenge $authorizationChallenge) */ public function isValid(AuthorizationChallenge $authorizationChallenge) { - $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordName = $this->extractor->getRecordFqdn($authorizationChallenge); $recordValue = $this->extractor->getRecordValue($authorizationChallenge); try { - return \in_array($recordValue, $this->dnsResolver->getTxtEntries($recordName)); + if (method_exists($this->extractor, 'getRecordType')) { + $type = $this->extractor->getRecordType($authorizationChallenge); + $records = $this->dnsResolver->getEnteries($type, $recordName); + } else { + $records = $this->dnsResolver->getTxtEntries($recordName); + } + return \in_array($recordValue, $records); } catch (AcmeDnsResolutionException $e) { return false; } diff --git a/src/Core/Challenge/Dns/LibDnsResolver.php b/src/Core/Challenge/Dns/LibDnsResolver.php index 6417b589..15c9a93e 100644 --- a/src/Core/Challenge/Dns/LibDnsResolver.php +++ b/src/Core/Challenge/Dns/LibDnsResolver.php @@ -12,6 +12,7 @@ namespace AcmePhp\Core\Challenge\Dns; use AcmePhp\Core\Exception\AcmeDnsResolutionException; +use Exception; use LibDNS\Decoder\Decoder; use LibDNS\Decoder\DecoderFactory; use LibDNS\Encoder\Encoder; @@ -118,6 +119,59 @@ public function getTxtEntries($domain) return json_decode(key($identicalEntries)); } + public function getEnteries($type, $domain) + { + $type_int = null; + switch (strtolower($type)) { + case 'cname': + $type_int = ResourceTypes::CNAME; + break; + + case 'a': + $type_int = ResourceTypes::A; + break; + + case 'txt': + $type_int = ResourceTypes::TXT; + break; + + default: + throw new Exception("Doesn't support type " . $type, 0); + break; + } + + $domain = rtrim($domain, '.'); + $nameServers = $this->getNameServers($domain); + $this->logger->debug('Fetched TXT records for domain', ['nsDomain' => $domain, 'servers' => $nameServers]); + $identicalEntries = []; + foreach ($nameServers as $nameServer) { + $ipNameServer = gethostbynamel($nameServer); + if (empty($ipNameServer)) { + throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer)); + } + try { + $response = $this->request($domain, $type_int, $ipNameServer[0]); + } catch (\Exception $e) { + throw new AcmeDnsResolutionException(sprintf('Unable to find domain %s on nameserver %s', $domain, $nameServer)); + } + $entries = []; + foreach ($response->getAnswerRecords() as $record) { + foreach ($record->getData() as $recordData) { + $entries[] = (string) $recordData; + } + } + + $identicalEntries[json_encode($entries)][] = $nameServer; + } + + $this->logger->info('DNS records fetched', ['mapping' => $identicalEntries]); + if (1 !== \count($identicalEntries)) { + throw new AcmeDnsResolutionException('Dns not fully propagated'); + } + + return json_decode(key($identicalEntries)); + } + private function getNameServers($domain) { if ('' === $domain) { diff --git a/src/Core/Challenge/Dns/SimpleDnsResolver.php b/src/Core/Challenge/Dns/SimpleDnsResolver.php index f7161214..c2cabd90 100644 --- a/src/Core/Challenge/Dns/SimpleDnsResolver.php +++ b/src/Core/Challenge/Dns/SimpleDnsResolver.php @@ -40,4 +40,35 @@ public function getTxtEntries($domain) return array_unique($entries); } + + public function getEnteries($type, $domain) + { + $entries = []; + $type_int = null; + switch (strtolower($type)) { + case 'cname': + $type_int = DNS_CNAME; + break; + + case 'a': + $type_int = DNS_A; + break; + + case 'txt': + $type_int = DNS_TXT; + break; + + default: + throw new Exception("Doesn't support type ".$type, 0); + break; + } + + foreach (dns_get_record($domain, $type_int) as $record) { + $entries = array_merge($entries, $record['entries']); + } + + sort($entries); + + return array_unique($entries); + } } diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index deb25a01..8f6c161b 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -65,6 +65,11 @@ class AuthorizationChallenge */ private $filecontent; + /** + * @var string|null + */ + private $fqdn; + /** * @param string $domain * @param string $status @@ -75,8 +80,9 @@ class AuthorizationChallenge * @param string $path * @param string $verifyurl * @param string $filecontent + * @param string $fqdn */ - public function __construct($domain, $status, $type, $url, $token, $payload, $path = null, $verifyurl = null, $filecontent = null) + public function __construct($domain, $status, $type, $url, $token, $payload, $path = null, $verifyurl = null, $filecontent = null, $fqdn = null) { Assert::stringNotEmpty($domain, 'Challenge::$domain expected a non-empty string. Got: %s'); Assert::stringNotEmpty($status, 'Challenge::$status expected a non-empty string. Got: %s'); @@ -94,6 +100,7 @@ public function __construct($domain, $status, $type, $url, $token, $payload, $pa $this->path = $path; $this->verifyurl = $verifyurl; $this->filecontent = $filecontent; + $this->fqdn = $fqdn; } /** @@ -221,4 +228,12 @@ public function getFilecontent() { return $this->filecontent; } + + /** + * @return string|null + */ + public function getFqdn() + { + return $this->fqdn; + } } From ae3cda1a983eb46f70c830a94af0d1c60369ae31 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 15 Nov 2019 15:25:37 +0800 Subject: [PATCH 092/126] fixbug --- src/Core/Challenge/Dns/DnspodSolver.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 3828cfee..bca66f1f 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -123,11 +123,15 @@ public function solveAll(array $authorizationChallenges) $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); - $recordType = $this->extractor->getRecordType($authorizationChallenge); + $recordType = 'txt'; + if (method_exists($this->extractor, 'getRecordType')) { + $recordType = $this->extractor->getRecordType($authorizationChallenge); + } - if (strtolower($recordType) == 'cname') { - // 因为 DNSPod 免费版套餐 同一名称 cname 不能并存 - // 所以要删除旧的 + if ('cname' === strtolower($recordType)) { + // Because DNSPod can't create conflicting cname records + // So we'd delete existing records first + clear: $cns->RecordList([ 'domain' => $topLevelDomain, @@ -135,7 +139,7 @@ public function solveAll(array $authorizationChallenges) 'recordType' => $recordType, ]); $data = json_decode($cns->getLastResponse(), true); - if ($data && isset($data['data']) && isset($data['data']['records']) && is_array($data['data']['records']) && count($data['data']['records'])) { + if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { foreach ($data['data']['records'] as $existedRecord) { if (isset($existedRecord['id'])) { $cns->RecordDelete([ @@ -159,6 +163,9 @@ public function solveAll(array $authorizationChallenges) * @var \QcloudApi_Common_Error */ $err = $cns->getError(); + if (strpos($err->getMessage(), '子域名负载均衡数量超出限制') !== false) { + goto clear; + } throw new Exception($err->getMessage(), $err->getCode()); } From ca888f0634540aabcaa616b5ceb2a4fe85764d69 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Fri, 15 Nov 2019 16:46:16 +0800 Subject: [PATCH 093/126] remove uses alias, change exception throwing to `AcmeDnsResolutionException` --- src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php index f9178c50..13f1f8ef 100644 --- a/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php +++ b/src/Core/Challenge/Dns/Traits/TopLevelDomainTrait.php @@ -11,8 +11,8 @@ namespace AcmePhp\Core\Challenge\Dns\Traits; -use LayerShifter\TLDDatabase\Exceptions\ParserException; -use LayerShifter\TLDExtract\Extract as TLDExtract; +use LayerShifter\TLDExtract\Extract; +use AcmePhp\Cli\Exception\AcmeDnsResolutionException; trait TopLevelDomainTrait { @@ -23,10 +23,10 @@ trait TopLevelDomainTrait */ protected function getTopLevelDomain($domain) { - $extract = new TLDExtract(); + $extract = new Extract(); $parse = $extract->parse(str_replace('*.', '', $domain)); if (!$parse->isValidDomain()) { - throw new ParserException($domain.' is not a valid domain', 1); + throw new AcmeDnsResolutionException($domain.' is not a valid domain'); } return $parse->getRegistrableDomain(); From bb2031755c7b44ada8b55ee4fc634a4049aebf90 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 13:20:39 +0800 Subject: [PATCH 094/126] Tencentcloud dns challenge err debug --- composer.json | 2 +- src/Core/Challenge/Dns/DnspodSolver.php | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index c44aa6bd..235f5fa5 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "lib-openssl": ">=0.9.8", "aws/aws-sdk-php": "^3.38", "daverandom/libdns": ">=1.0", - "guzzlehttp/guzzle": "^6.0", + "guzzlehttp/guzzle": "^6.4", "guzzlehttp/psr7": "^1.0", "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index a418dc7f..7bfd33b8 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -18,12 +18,14 @@ use Exception; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use function GuzzleHttp\json_decode; +use GuzzleHttp\Exception\InvalidArgumentException; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use QcloudApi; use Webmozart\Assert\Assert; +use function GuzzleHttp\json_decode; + /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). * @@ -112,7 +114,7 @@ public function solveAll(array $authorizationChallenges) 'DefaultRegion' => 'gz', ]; /** - * @var \QcloudApi_Module_Cns + * @var \QcloudApi_Module_Cns $cns */ $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); @@ -138,7 +140,18 @@ public function solveAll(array $authorizationChallenges) 'subDomain' => $subDomain, 'recordType' => $recordType, ]); - $data = json_decode($cns->getLastResponse(), true); + try { + $data = json_decode($cns->getLastResponse(), true); + } catch (InvalidArgumentException $e) { + $err = $cns->getError(); + if ($err) { + print_r($err->getExt()); + throw new \Exception($err->getMessage(), $err->getCode()); + } else { + echo $cns->getLastResponse(); + } + throw $e; + } if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { foreach ($data['data']['records'] as $existedRecord) { if (isset($existedRecord['id'])) { From 9fd22201a1c2cdcbe21507e36bd8804bd8a4df44 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 14:01:24 +0800 Subject: [PATCH 095/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8DQcloud=20SDK=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E5=BC=82=E5=B8=B8=E7=9A=84=E5=82=BB=E9=80=BC=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=20https://github.com/QcloudApi/qcloudapi-sdk-php/issu?= =?UTF-8?q?es/12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/DnspodSolver.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 7bfd33b8..484c99bc 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -22,6 +22,7 @@ use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use QcloudApi; +use QcloudApi_Common_Request; use Webmozart\Assert\Assert; use function GuzzleHttp\json_decode; @@ -145,6 +146,9 @@ public function solveAll(array $authorizationChallenges) } catch (InvalidArgumentException $e) { $err = $cns->getError(); if ($err) { + if ($err->getCode() == 3000) { + throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); + } print_r($err->getExt()); throw new \Exception($err->getMessage(), $err->getCode()); } else { From 548b609ea51a5a78b1c8fbb5b47b511bc827ea88 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 13:20:39 +0800 Subject: [PATCH 096/126] Tencentcloud dns challenge err debug --- src/Core/Challenge/Dns/DnspodSolver.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index bca66f1f..8cb1027f 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -18,12 +18,14 @@ use Exception; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use function GuzzleHttp\json_decode; +use GuzzleHttp\Exception\InvalidArgumentException; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use QcloudApi; use Webmozart\Assert\Assert; +use function GuzzleHttp\json_decode; + /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). * @@ -112,7 +114,7 @@ public function solveAll(array $authorizationChallenges) 'DefaultRegion' => 'gz', ]; /** - * @var \QcloudApi_Module_Cns + * @var \QcloudApi_Module_Cns $cns */ $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); @@ -138,7 +140,18 @@ public function solveAll(array $authorizationChallenges) 'subDomain' => $subDomain, 'recordType' => $recordType, ]); - $data = json_decode($cns->getLastResponse(), true); + try { + $data = json_decode($cns->getLastResponse(), true); + } catch (InvalidArgumentException $e) { + $err = $cns->getError(); + if ($err) { + print_r($err->getExt()); + throw new \Exception($err->getMessage(), $err->getCode()); + } else { + echo $cns->getLastResponse(); + } + throw $e; + } if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { foreach ($data['data']['records'] as $existedRecord) { if (isset($existedRecord['id'])) { From 4af7fd14828998c3fd2d59210fa679dc7aefca41 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 14:01:24 +0800 Subject: [PATCH 097/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8DQcloud=20SDK=E8=A6=86?= =?UTF-8?q?=E7=9B=96=E5=BC=82=E5=B8=B8=E7=9A=84=E5=82=BB=E9=80=BC=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=20https://github.com/QcloudApi/qcloudapi-sdk-php/issu?= =?UTF-8?q?es/12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/DnspodSolver.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 8cb1027f..aac2dbc9 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -22,6 +22,7 @@ use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use QcloudApi; +use QcloudApi_Common_Request; use Webmozart\Assert\Assert; use function GuzzleHttp\json_decode; @@ -145,6 +146,9 @@ public function solveAll(array $authorizationChallenges) } catch (InvalidArgumentException $e) { $err = $cns->getError(); if ($err) { + if ($err->getCode() == 3000) { + throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); + } print_r($err->getExt()); throw new \Exception($err->getMessage(), $err->getCode()); } else { From 5a2d100a96e57dc779ca5f42ac2f713ce9ef9159 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 17:32:16 +0800 Subject: [PATCH 098/126] fix --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 235f5fa5..ca1680d6 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "ACME client written in PHP", "type": "project", "license": "MIT", - "homepage": "https://github.com/trustocean/acme-client", + "homepage": "https://github.com/digitalsign/acme-client", "keywords": [ "acme", "acmephp", From d21c37b23e94178cbb3e7f0ec72a3640d976b980 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 18:04:05 +0800 Subject: [PATCH 099/126] timeout configurable --- src/Cli/Command/RunCommand.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 645f17c5..9c288d91 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -96,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $certificate ); } else { - $order = $this->challengeDomains($domainConfig, $keyOption); + $order = $this->challengeDomains($domainConfig, $keyOption, $config); $this->requestCertificate($order, $domainConfig, $keyOption); $certificate = $this->getRepository()->loadDomainCertificate($domain); @@ -240,7 +240,7 @@ private function requestCertificate(CertificateOrder $order, $domainConfig, KeyO return $response; } - private function challengeDomains(array $domainConfig, KeyOption $keyOption) + private function challengeDomains(array $domainConfig, KeyOption $keyOption, array $config) { $solverConfig = $domainConfig['solver']; $domain = $domainConfig['domain']; @@ -327,6 +327,8 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption) } } + $config_timeout = (isset($config['timeout']) ? $config['timeout'] : 180); + $startTestTime = time(); foreach ($authorizationChallengesToSolve as $domain => $authorizationChallenge) { if ($authorizationChallenge->isValid()) { @@ -334,12 +336,12 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption) } $this->output->writeln(sprintf('Testing the challenge for domain %s...', $domain)); - if (time() - $startTestTime > 180 || !$validator->isValid($authorizationChallenge)) { + if (time() - $startTestTime > $config_timeout || !$validator->isValid($authorizationChallenge)) { $this->output->writeln(sprintf('Can not self validate challenge for domain %s. Maybe letsencrypt will be able to do it...', $domain)); } $this->output->writeln(sprintf('Requesting authorization check for domain %s...', $domain)); - $client->challengeAuthorization($authorizationChallenge); + $client->challengeAuthorization($authorizationChallenge, $config_timeout); } if ($solver instanceof MultipleChallengesSolverInterface) { From 2e1e1083275b74012f932d0a03696f30f6c9a674 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 23 Nov 2019 18:15:21 +0800 Subject: [PATCH 100/126] fixbug --- src/Cli/Configuration/DomainConfiguration.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index 717551ce..b20fce09 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -72,6 +72,16 @@ public function getConfigTreeBuilder() ->thenInvalid('The keyType %s is not valid. Supported types are: RSA, EC') ->end() ->end() + ->scalarNode('timeout') + ->info('Timtout to challenge.') + ->defaultValue('180') + ->beforeNormalization() + ->ifTrue(function ($item) { + return !is_int($item) || $item < 1 || $item > 3600; + }) + ->thenInvalid('The timeout %s is not valid. Supported range is 1~3600') + ->end() + ->end() ->end() ->append($this->createDefaultsSection()) ->append($this->createCertificatesSection()); From a21445e696c6993415b9d0c36cf4867380e7be9e Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 12:12:39 +0800 Subject: [PATCH 101/126] enhancement --- script/upload_phar.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index ed03d360..7c435679 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,2 +1,4 @@ curl -X DELETE $(curl https://api.github.com/repos/digitalsign/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/digitalsign/acme-client/releases/18962377/assets?name=acmephp.phar' -s +fs=$(ls -l build/acmephp.phar | awk '{print $5}') +curl -F "size=$fs" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?Authorization=$WANTU_TOKEN' -s \ No newline at end of file From 21535ffe50c3fa829a605b30805383ea065c96f1 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 12:14:38 +0800 Subject: [PATCH 102/126] fix --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index 7c435679..e22247e1 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,4 +1,4 @@ curl -X DELETE $(curl https://api.github.com/repos/digitalsign/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/digitalsign/acme-client/releases/18962377/assets?name=acmephp.phar' -s fs=$(ls -l build/acmephp.phar | awk '{print $5}') -curl -F "size=$fs" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?Authorization=$WANTU_TOKEN' -s \ No newline at end of file +curl -F "size=$fs" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?UserAgent=ALIMEDIASDK_WORKSTATION&Authorization=$WANTU_TOKEN' -s \ No newline at end of file From f139c30d177c014b89832c879165e335a31e500a Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 12:27:59 +0800 Subject: [PATCH 103/126] fixbug --- script/upload_phar.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index e22247e1..d7dd9eb7 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,4 +1,3 @@ curl -X DELETE $(curl https://api.github.com/repos/digitalsign/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/digitalsign/acme-client/releases/18962377/assets?name=acmephp.phar' -s -fs=$(ls -l build/acmephp.phar | awk '{print $5}') -curl -F "size=$fs" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?UserAgent=ALIMEDIASDK_WORKSTATION&Authorization=$WANTU_TOKEN' -s \ No newline at end of file +curl -F "size=$(ls -l build/acmephp.phar | awk '{print $5}')" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?UserAgent=ALIMEDIASDK_WORKSTATION&Authorization=UPLOAD_AK_TOP%20$WANTU_TOKEN' -v \ No newline at end of file From b8ae9eb003ff7c84dee4a4f48d72a41f845d6f66 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 12:31:31 +0800 Subject: [PATCH 104/126] fixbug --- script/upload_phar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/upload_phar.sh b/script/upload_phar.sh index d7dd9eb7..b385a70c 100644 --- a/script/upload_phar.sh +++ b/script/upload_phar.sh @@ -1,3 +1,3 @@ curl -X DELETE $(curl https://api.github.com/repos/digitalsign/acme-client/releases/18962377/assets -H "Authorization: token $GITHUB_TOKEN" -s | grep '"url"' | grep "assets" | cut -d'"' -f4) -H "Authorization: token $GITHUB_TOKEN" -s || echo failed curl -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/php-archive" -T 'build/acmephp.phar' 'https://uploads.github.com/repos/digitalsign/acme-client/releases/18962377/assets?name=acmephp.phar' -s -curl -F "size=$(ls -l build/acmephp.phar | awk '{print $5}')" -F 'file=@build/acmephp.phar' 'https://upload.media.aliyun.com/api/proxy/upload?UserAgent=ALIMEDIASDK_WORKSTATION&Authorization=UPLOAD_AK_TOP%20$WANTU_TOKEN' -v \ No newline at end of file +curl -F "size=$(ls -l build/acmephp.phar | awk '{print $5}')" -F 'file=@build/acmephp.phar' "https://upload.media.aliyun.com/api/proxy/upload?UserAgent=ALIMEDIASDK_WORKSTATION&Authorization=UPLOAD_AK_TOP%20$WANTU_TOKEN" -v \ No newline at end of file From 2959ec1f315ab974828767f7afda252b4adf58ba Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 14:13:07 +0800 Subject: [PATCH 105/126] add doc --- src/Cli/Action/InstallTencentcloudCdnAction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/Action/InstallTencentcloudCdnAction.php b/src/Cli/Action/InstallTencentcloudCdnAction.php index 10cfa409..da72efd3 100644 --- a/src/Cli/Action/InstallTencentcloudCdnAction.php +++ b/src/Cli/Action/InstallTencentcloudCdnAction.php @@ -19,6 +19,7 @@ * Action to install certificate in an Aliyun Waf. * * @author Xiaohui Lam + * @link https://cloud.tencent.com/document/api/228/12965 */ class InstallTencentcloudCdnAction extends AbstractAction { From 3628106aa3ca8f91a965463ce531dcfc7f683a02 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 16:03:43 +0800 Subject: [PATCH 106/126] =?UTF-8?q?=E9=9B=86=E6=88=90DNSPod=E8=87=AA?= =?UTF-8?q?=E6=9C=89API=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=BB=BA=E8=AE=AE?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=85=BE=E8=AE=AF=E4=BA=91API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 12 +- src/Cli/Resources/services.xml | 8 +- src/Core/Challenge/Dns/DnspodSolver.php | 216 +++++++++------- src/Core/Challenge/Dns/TencentCloudSolver.php | 230 ++++++++++++++++++ src/Core/Protocol/AuthorizationChallenge.php | 31 +++ 5 files changed, 403 insertions(+), 94 deletions(-) create mode 100644 src/Core/Challenge/Dns/TencentCloudSolver.php diff --git a/composer.json b/composer.json index ca1680d6..1c4d7905 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": ">=7.0", "ext-hash": "*", "ext-json": "*", "ext-filter": "*", @@ -64,7 +64,8 @@ "alibabacloud/wafopenapi": "^1.7", "alibabacloud/cdn": "^1.7", "tencentyun-api/qcloudapi-sdk-php": "^2.0", - "layershifter/tld-extract": "^2.0" + "layershifter/tld-extract": "^2.0", + "jeremykendall/php-domain-parser": "^5.5" }, "suggest": { "daverandom/libdns": "^2.0" @@ -93,5 +94,10 @@ }, "bin": [ "bin/acme" - ] + ], + "config": { + "platform": { + "php": "7.2" + } + } } diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 31cab43b..5a5064bf 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -203,7 +203,13 @@ - + + + + + + + diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 484c99bc..700a7364 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -14,23 +14,23 @@ use AcmePhp\Core\Challenge\ConfigurableServiceInterface; use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Exception\AcmeCoreClientException; use AcmePhp\Core\Protocol\AuthorizationChallenge; -use Exception; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use GuzzleHttp\Exception\InvalidArgumentException; +use GuzzleHttp\RequestOptions; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; -use QcloudApi; -use QcloudApi_Common_Request; use Webmozart\Assert\Assert; use function GuzzleHttp\json_decode; +use function GuzzleHttp\json_encode; /** * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). * * @author Xiaohui Lam + * @link https://www.dnspod.cn/docs/records.html#record-list */ class DnspodSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface { @@ -52,14 +52,21 @@ class DnspodSolver implements MultipleChallengesSolverInterface, ConfigurableSer private $cacheZones; /** - * @var string + * @var int */ - private $secretId; + private $id; /** * @var string */ - private $secretKey; + private $token; + + /** + * 批量ID + * + * @var int + */ + private $job_id; /** * @param DnsDataExtractor $extractor @@ -81,8 +88,8 @@ public function __construct( */ public function configure(array $config) { - $this->secretId = $config['secret_id']; - $this->secretKey = $config['secret_key']; + $this->id = $config['id']; + $this->token = $config['token']; } /** @@ -108,87 +115,97 @@ public function solveAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); - $config = [ - 'SecretId' => $this->secretId, - 'SecretKey' => $this->secretKey, - 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz', - ]; - /** - * @var \QcloudApi_Module_Cns $cns - */ - $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + $domains = []; + $records_all = []; - foreach ($authorizationChallenges as $authorizationChallenge) { - $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); - $recordName = $this->extractor->getRecordName($authorizationChallenge); - $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + $http = new Client(); - $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); + $domainListResponse = $http->post('https://dnsapi.cn/Domain.List', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'length' => 2000, + ], + ]); + if ($domainListResponse->getStatusCode() == 200) { + $domainList = json_decode($domainListResponse->getBody()->__toString(), true); + foreach ($domainList['domains'] as $domain) { + $domains[$domain['name']] = $domain['id']; + } + } + foreach ($authorizationChallenges as $authorizationChallenge) { + $authorizationChallenge->getTopLevelDomain(); + $authorizationChallenge->getSubDomain(); $recordType = 'txt'; if (method_exists($this->extractor, 'getRecordType')) { $recordType = $this->extractor->getRecordType($authorizationChallenge); } + $listResponse = $http->post('https://dnsapi.cn/Record.List', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'domain' => $authorizationChallenge->getTopLevelDomain(), + 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $authorizationChallenge->getTopLevelDomain()) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), + 'record_type' => $recordType, + ], + ]); + $list = json_decode($listResponse->getBody()->__toString(), true); + if ($listResponse->getStatusCode() == 200 && $list['status']['code'] == 1) { + $domain = $list['domain']; + $domains[$authorizationChallenge->getTopLevelDomain()] = $domain['id']; + if ('cname' === strtolower($recordType)) { + if (isset($list['records']) && is_array($list['records'])) { + $records = $list['records']; + foreach ($records as $record) { + $this->logger->debug('Fetched Conflict records for domain, deleting', [ + 'domain' => $authorizationChallenge->getTopLevelDomain(), + 'record_type' => $recordType, + 'record_id' => $record['id'], + ]); - if ('cname' === strtolower($recordType)) { - // Because DNSPod can't create conflicting cname records - // So we'd delete existing records first - clear: - - $cns->RecordList([ - 'domain' => $topLevelDomain, - 'subDomain' => $subDomain, - 'recordType' => $recordType, - ]); - try { - $data = json_decode($cns->getLastResponse(), true); - } catch (InvalidArgumentException $e) { - $err = $cns->getError(); - if ($err) { - if ($err->getCode() == 3000) { - throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); - } - print_r($err->getExt()); - throw new \Exception($err->getMessage(), $err->getCode()); - } else { - echo $cns->getLastResponse(); - } - throw $e; - } - if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { - foreach ($data['data']['records'] as $existedRecord) { - if (isset($existedRecord['id'])) { - $cns->RecordDelete([ - 'domain' => $topLevelDomain, - 'recordId' => $existedRecord['id'], + $http->post('https://dnsapi.cn/Record.Remove', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'domain' => $authorizationChallenge->getTopLevelDomain(), + 'record_id' => $record['id'], + ], ]); } } } } - $solve = $cns->RecordCreate([ - 'domain' => $topLevelDomain, - 'subDomain' => $subDomain, - 'recordType' => $recordType, - 'recordLine' => '默认', - 'value' => $recordValue, - ]); - if (false === $solve) { - /** - * @var \QcloudApi_Common_Error - */ - $err = $cns->getError(); - if (strpos($err->getMessage(), '子域名负载均衡数量超出限制') !== false) { - goto clear; - } - throw new Exception($err->getMessage(), $err->getCode()); - } + $arr = [ + 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $authorizationChallenge->getTopLevelDomain()) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), + 'record_type' => $recordType, + 'record_line' => '默认', + 'value' => $this->extractor->getRecordValue($authorizationChallenge), + 'ttl' => 600, + ]; + $records_all[md5(http_build_query($arr))] = $arr; + } - $data = json_decode($cns->getLastResponse(), true); - $authorizationChallenge->recordId = $data['data']['record']['id']; + sort($records_all); + sort($domains); + + $this->logger->debug('Batch creating for domains', $domains); + $this->logger->debug('Batch creating records', $records_all); + $batchrResponse = $http->post('https://dnsapi.cn/Batch.Record.Create', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'domain_id' => implode(',', $domains), + 'records' => json_encode($records_all), + ], + ]); + + $batch = json_decode($batchrResponse->getBody()->__toString(), true); + if ($batch['status']['code'] != 1) { + throw new AcmeCoreClientException('Resolving domain fail!', new \Exception($batch['status']['message'], (int) $batch['status']['code'])); } + $this->job_id = $batch['job_id']; // log job id, after cert issued, it should be using to cleanup } /** @@ -206,24 +223,43 @@ public function cleanupAll(array $authorizationChallenges) { Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); - $config = [ - 'SecretId' => $this->secretId, - 'SecretKey' => $this->secretKey, - 'RequestMethod' => 'GET', - 'DefaultRegion' => 'gz', - ]; - /** - * @var \QcloudApi_Module_Cns - */ - $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + cleanup: + $http = new Client(); + $batchrResponse = $http->post('https://dnsapi.cn/Batch.Detail', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'job_id' => $this->job_id, + ], + ]); + $batch = json_decode($batchrResponse->getBody()->__toString(), true); + foreach ($batch as $tld) { + if ($tld['status'] == 'running' || $tld['status'] == 'waiting') { + $this->logger->debug('Batch task status ' . $tld['status'], $tld); + sleep(5); + goto cleanup; + } - foreach ($authorizationChallenges as $authorizationChallenge) { - $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + if ($tld['status'] == 'error') { + $this->logger->debug('Batch task status ' . $tld['status'], $tld); + continue; + // @TODO: + } - $cns->RecordDelete([ - 'domain' => $topLevelDomain, - 'recordId' => $authorizationChallenge->recordId, - ]); + if ($tld['status'] == 'ok') { + $records = $tld['records']; + + foreach ($records as $record) { + $http->post('https://dnsapi.cn/Record.Remove', [ + RequestOptions::FORM_PARAMS => [ + 'format' => 'json', + 'login_token' => implode(',', [$this->id, $this->token]), + 'domain_id' => $tld['id'], + 'record_id' => $record['id'], + ], + ]); + } + } } } } diff --git a/src/Core/Challenge/Dns/TencentCloudSolver.php b/src/Core/Challenge/Dns/TencentCloudSolver.php new file mode 100644 index 00000000..7418b374 --- /dev/null +++ b/src/Core/Challenge/Dns/TencentCloudSolver.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Challenge\Dns; + +use AcmePhp\Core\Challenge\ConfigurableServiceInterface; +use AcmePhp\Core\Challenge\Dns\Traits\TopLevelDomainTrait; +use AcmePhp\Core\Challenge\MultipleChallengesSolverInterface; +use AcmePhp\Core\Protocol\AuthorizationChallenge; +use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; +use GuzzleHttp\Exception\InvalidArgumentException; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use QcloudApi; +use QcloudApi_Common_Request; +use Webmozart\Assert\Assert; + +use function GuzzleHttp\json_decode; + +/** + * ACME DNS solver with automate configuration of a DnsPod.cn (TencentCloud NS). + * + * @author Xiaohui Lam + * @link https://cloud.tencent.com/document/api/302/3875 + */ +class TencentCloudSolver implements MultipleChallengesSolverInterface, ConfigurableServiceInterface +{ + use LoggerAwareTrait, TopLevelDomainTrait; + + /** + * @var DnsDataExtractor + */ + private $extractor; + + /** + * @var ClientInterface + */ + private $client; + + /** + * @var array + */ + private $cacheZones; + + /** + * @var string + */ + private $secretId; + + /** + * @var string + */ + private $secretKey; + + /** + * @param DnsDataExtractor $extractor + * @param ClientInterface $client + */ + public function __construct( + DnsDataExtractor $extractor = null, + ClientInterface $client = null + ) { + $this->extractor = null === $extractor ? new DnsDataExtractor() : $extractor; + $this->client = null === $client ? new Client() : $client; + $this->logger = new NullLogger(); + } + + /** + * Configure the service with a set of configuration. + * + * @param array $config + */ + public function configure(array $config) + { + $this->secretId = $config['secret_id']; + $this->secretKey = $config['secret_key']; + } + + /** + * {@inheritdoc} + */ + public function supports(AuthorizationChallenge $authorizationChallenge) + { + return 'dns-01' === $authorizationChallenge->getType(); + } + + /** + * {@inheritdoc} + */ + public function solve(AuthorizationChallenge $authorizationChallenge) + { + return $this->solveAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function solveAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + $config = [ + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz', + ]; + /** + * @var \QcloudApi_Module_Cns $cns + */ + $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + $recordName = $this->extractor->getRecordName($authorizationChallenge); + $recordValue = $this->extractor->getRecordValue($authorizationChallenge); + + $subDomain = \str_replace('.'.$topLevelDomain.'.', '', $recordName); + + $recordType = 'txt'; + if (method_exists($this->extractor, 'getRecordType')) { + $recordType = $this->extractor->getRecordType($authorizationChallenge); + } + + if ('cname' === strtolower($recordType)) { + // Because DNSPod can't create conflicting cname records + // So we'd delete existing records first + clear: + + $cns->RecordList([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => $recordType, + ]); + try { + $data = json_decode($cns->getLastResponse(), true); + } catch (InvalidArgumentException $e) { + $err = $cns->getError(); + if ($err) { + if ($err->getCode() == 3000) { + throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); + } + print_r($err->getExt()); + throw new \Exception($err->getMessage(), $err->getCode()); + } else { + echo $cns->getLastResponse(); + } + throw $e; + } + if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { + foreach ($data['data']['records'] as $existedRecord) { + if (isset($existedRecord['id'])) { + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $existedRecord['id'], + ]); + } + } + } + } + $solve = $cns->RecordCreate([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => $recordType, + 'recordLine' => '默认', + 'value' => $recordValue, + ]); + + if (false === $solve) { + /** + * @var \QcloudApi_Common_Error + */ + $err = $cns->getError(); + if (strpos($err->getMessage(), '子域名负载均衡数量超出限制') !== false) { + goto clear; + } + throw new Exception($err->getMessage(), $err->getCode()); + } + + $data = json_decode($cns->getLastResponse(), true); + $authorizationChallenge->recordId = $data['data']['record']['id']; + } + } + + /** + * {@inheritdoc} + */ + public function cleanup(AuthorizationChallenge $authorizationChallenge) + { + return $this->cleanupAll([$authorizationChallenge]); + } + + /** + * {@inheritdoc} + */ + public function cleanupAll(array $authorizationChallenges) + { + Assert::allIsInstanceOf($authorizationChallenges, AuthorizationChallenge::class); + + $config = [ + 'SecretId' => $this->secretId, + 'SecretKey' => $this->secretKey, + 'RequestMethod' => 'GET', + 'DefaultRegion' => 'gz', + ]; + /** + * @var \QcloudApi_Module_Cns + */ + $cns = QcloudApi::load(QcloudApi::MODULE_CNS, $config); + + foreach ($authorizationChallenges as $authorizationChallenge) { + $topLevelDomain = $this->getTopLevelDomain($authorizationChallenge->getDomain()); + + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $authorizationChallenge->recordId, + ]); + } + } +} diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index 8f6c161b..486d8418 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -11,6 +11,9 @@ namespace AcmePhp\Core\Protocol; +use Pdp\Cache; +use Pdp\Manager; +use Pdp\CurlHttpClient; use Webmozart\Assert\Assert; /** @@ -149,6 +152,34 @@ public function getDomain() return $this->domain; } + /** + * Get registerable(root、tld) domain + * + * @return string + */ + public function getTopLevelDomain() + { + $manager = new Manager(new Cache(), new CurlHttpClient()); + $rules = $manager->getRules(); + + $domain = $rules->resolve($this->getDomain()); + return $domain->getRegistrableDomain(); + } + + /** + * Get registerable(root、tld) domain + * + * @return string + */ + public function getSubDomain() + { + $manager = new Manager(new Cache(), new CurlHttpClient()); + $rules = $manager->getRules(); + + $domain = $rules->resolve($this->getDomain()); + return $domain->getSubDomain(); + } + /** * @return string */ From fee5ef27afa8fdf2a5b7c110043c92e1b084f0ef Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 16:25:09 +0800 Subject: [PATCH 107/126] fixbug --- src/Core/Challenge/Dns/DnspodSolver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index 700a7364..b59a66c2 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -233,7 +233,8 @@ public function cleanupAll(array $authorizationChallenges) ], ]); $batch = json_decode($batchrResponse->getBody()->__toString(), true); - foreach ($batch as $tld) { + + foreach ($batch['detail'] as $tld) { if ($tld['status'] == 'running' || $tld['status'] == 'waiting') { $this->logger->debug('Batch task status ' . $tld['status'], $tld); sleep(5); From 324406cb13cc44eeec908a4c19c571446030951c Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 16:44:15 +0800 Subject: [PATCH 108/126] fixbug --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d03378f8..533c4d68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ matrix: install: - composer global require bamarni/composer-bin-plugin - - composer config platform.php 5.6.3 - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest - composer global require humbug/box - mv box.json.dist box.json From 61ac41de2a29793818d1d2fcbac9f59243a8bf04 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 16:58:57 +0800 Subject: [PATCH 109/126] fixbug --- src/Cli/Resources/services.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Resources/services.xml b/src/Cli/Resources/services.xml index 5a5064bf..fbf7b436 100644 --- a/src/Cli/Resources/services.xml +++ b/src/Cli/Resources/services.xml @@ -209,7 +209,7 @@ - + From 77b6f5b64ca779f5656948d042a1b3e0610b107d Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 17:05:56 +0800 Subject: [PATCH 110/126] fixbug --- src/Core/Protocol/AuthorizationChallenge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index 486d8418..c8fa04f9 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -159,7 +159,7 @@ public function getDomain() */ public function getTopLevelDomain() { - $manager = new Manager(new Cache(), new CurlHttpClient()); + $manager = new Manager(new Cache(''), new CurlHttpClient()); $rules = $manager->getRules(); $domain = $rules->resolve($this->getDomain()); @@ -173,7 +173,7 @@ public function getTopLevelDomain() */ public function getSubDomain() { - $manager = new Manager(new Cache(), new CurlHttpClient()); + $manager = new Manager(new Cache(''), new CurlHttpClient()); $rules = $manager->getRules(); $domain = $rules->resolve($this->getDomain()); From 2d8b8ec459d0bc94b94d052093dea507bc36de0b Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 17:23:16 +0800 Subject: [PATCH 111/126] jeremykendall/php-domain-parser can't run in phar --- .travis.yml | 1 + composer.json | 12 +++------ src/Core/Challenge/Dns/DnspodSolver.php | 14 +++++----- src/Core/Protocol/AuthorizationChallenge.php | 28 -------------------- 4 files changed, 10 insertions(+), 45 deletions(-) diff --git a/.travis.yml b/.travis.yml index 533c4d68..d03378f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ matrix: install: - composer global require bamarni/composer-bin-plugin + - composer config platform.php 5.6.3 - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer update --no-dev --prefer-dist --no-interaction --no-suggest - composer global require humbug/box - mv box.json.dist box.json diff --git a/composer.json b/composer.json index 1c4d7905..0e863fa1 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ } ], "require": { - "php": ">=7.0", + "php": ">=5.6", "ext-hash": "*", "ext-json": "*", "ext-filter": "*", @@ -64,8 +64,7 @@ "alibabacloud/wafopenapi": "^1.7", "alibabacloud/cdn": "^1.7", "tencentyun-api/qcloudapi-sdk-php": "^2.0", - "layershifter/tld-extract": "^2.0", - "jeremykendall/php-domain-parser": "^5.5" + "layershifter/tld-extract": "^2.0" }, "suggest": { "daverandom/libdns": "^2.0" @@ -94,10 +93,5 @@ }, "bin": [ "bin/acme" - ], - "config": { - "platform": { - "php": "7.2" - } - } + ] } diff --git a/src/Core/Challenge/Dns/DnspodSolver.php b/src/Core/Challenge/Dns/DnspodSolver.php index b59a66c2..18256d7c 100644 --- a/src/Core/Challenge/Dns/DnspodSolver.php +++ b/src/Core/Challenge/Dns/DnspodSolver.php @@ -135,8 +135,6 @@ public function solveAll(array $authorizationChallenges) } foreach ($authorizationChallenges as $authorizationChallenge) { - $authorizationChallenge->getTopLevelDomain(); - $authorizationChallenge->getSubDomain(); $recordType = 'txt'; if (method_exists($this->extractor, 'getRecordType')) { $recordType = $this->extractor->getRecordType($authorizationChallenge); @@ -145,21 +143,21 @@ public function solveAll(array $authorizationChallenges) RequestOptions::FORM_PARAMS => [ 'format' => 'json', 'login_token' => implode(',', [$this->id, $this->token]), - 'domain' => $authorizationChallenge->getTopLevelDomain(), - 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $authorizationChallenge->getTopLevelDomain()) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), + 'domain' => $this->getTopLevelDomain($authorizationChallenge->getDomain()), + 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $this->getTopLevelDomain($authorizationChallenge->getDomain())) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), 'record_type' => $recordType, ], ]); $list = json_decode($listResponse->getBody()->__toString(), true); if ($listResponse->getStatusCode() == 200 && $list['status']['code'] == 1) { $domain = $list['domain']; - $domains[$authorizationChallenge->getTopLevelDomain()] = $domain['id']; + $domains[$this->getTopLevelDomain($authorizationChallenge->getDomain())] = $domain['id']; if ('cname' === strtolower($recordType)) { if (isset($list['records']) && is_array($list['records'])) { $records = $list['records']; foreach ($records as $record) { $this->logger->debug('Fetched Conflict records for domain, deleting', [ - 'domain' => $authorizationChallenge->getTopLevelDomain(), + 'domain' => $this->getTopLevelDomain($authorizationChallenge->getDomain()), 'record_type' => $recordType, 'record_id' => $record['id'], ]); @@ -168,7 +166,7 @@ public function solveAll(array $authorizationChallenges) RequestOptions::FORM_PARAMS => [ 'format' => 'json', 'login_token' => implode(',', [$this->id, $this->token]), - 'domain' => $authorizationChallenge->getTopLevelDomain(), + 'domain' => $this->getTopLevelDomain($authorizationChallenge->getDomain()), 'record_id' => $record['id'], ], ]); @@ -178,7 +176,7 @@ public function solveAll(array $authorizationChallenges) } $arr = [ - 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $authorizationChallenge->getTopLevelDomain()) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), + 'sub_domain' => preg_replace('/\.' . str_replace('.', '\.', $this->getTopLevelDomain($authorizationChallenge->getDomain())) . '$/', '', $this->extractor->getRecordFqdn($authorizationChallenge)), 'record_type' => $recordType, 'record_line' => '默认', 'value' => $this->extractor->getRecordValue($authorizationChallenge), diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index c8fa04f9..e20c48f0 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -152,34 +152,6 @@ public function getDomain() return $this->domain; } - /** - * Get registerable(root、tld) domain - * - * @return string - */ - public function getTopLevelDomain() - { - $manager = new Manager(new Cache(''), new CurlHttpClient()); - $rules = $manager->getRules(); - - $domain = $rules->resolve($this->getDomain()); - return $domain->getRegistrableDomain(); - } - - /** - * Get registerable(root、tld) domain - * - * @return string - */ - public function getSubDomain() - { - $manager = new Manager(new Cache(''), new CurlHttpClient()); - $rules = $manager->getRules(); - - $domain = $rules->resolve($this->getDomain()); - return $domain->getSubDomain(); - } - /** * @return string */ From c6a7bb49cac7da43f0ab231d51e73932164de4ca Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Tue, 26 Nov 2019 21:42:14 +0800 Subject: [PATCH 112/126] =?UTF-8?q?=E8=85=BE=E8=AE=AF=E4=BA=91CDN=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E9=9C=80=E8=A6=81=20base64=20encode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Action/InstallTencentcloudCdnAction.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cli/Action/InstallTencentcloudCdnAction.php b/src/Cli/Action/InstallTencentcloudCdnAction.php index da72efd3..31d47746 100644 --- a/src/Cli/Action/InstallTencentcloudCdnAction.php +++ b/src/Cli/Action/InstallTencentcloudCdnAction.php @@ -43,8 +43,8 @@ public function handle($config, CertificateResponse $response) $data = [ 'host' => $config['host'], 'httpsType' => $config['https_type'], - 'cert' => $cert, - 'privateKey' => $key, + 'cert' => base64_encode($cert), + 'privateKey' => base64_encode($key), ]; $response = $this->_createRequest($config['secret_id'], $config['secret_key'], $data, true); @@ -62,7 +62,7 @@ protected function _createRequest($secretId, $secretKey, $privateParams, $isHttp $commonParams = [ 'Nonce' => rand(), - 'Timestamp' => time(NULL), + 'Timestamp' => time(), 'Action' => 'SetHttpsInfo', 'SecretId' => $secretId, ]; From 6ad71ba1d31ffe5fdf1868ea1face5b05dfcab40 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Thu, 28 Nov 2019 14:54:58 +0800 Subject: [PATCH 113/126] update DataSigner to use web-token for ECDSA --- composer.json | 10 +++- src/Ssl/Signer/DataSigner.php | 110 +++++++++++++--------------------- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/composer.json b/composer.json index 0e863fa1..0aae4aa5 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,8 @@ "alibabacloud/wafopenapi": "^1.7", "alibabacloud/cdn": "^1.7", "tencentyun-api/qcloudapi-sdk-php": "^2.0", - "layershifter/tld-extract": "^2.0" + "layershifter/tld-extract": "^2.0", + "digitalsign/jwt-core": "*" }, "suggest": { "daverandom/libdns": "^2.0" @@ -93,5 +94,10 @@ }, "bin": [ "bin/acme" - ] + ], + "config": { + "platform": { + "php": "5.6.3" + } + } } diff --git a/src/Ssl/Signer/DataSigner.php b/src/Ssl/Signer/DataSigner.php index 56db1d76..67f4ec18 100644 --- a/src/Ssl/Signer/DataSigner.php +++ b/src/Ssl/Signer/DataSigner.php @@ -17,6 +17,7 @@ use AcmePhp\Ssl\PublicKey; use AcmePhp\Ssl\Exception\DataCheckingSignException; use AcmePhp\Core\Exception\AcmeCoreClientException; +use Jose\Component\Core\Util\ECSignature; /** * Provide tools to sign data using a private key. @@ -54,18 +55,28 @@ public function signData($data, PrivateKey $privateKey, $algorithm = OPENSSL_ALG switch ($format) { case self::FORMAT_DER: return $signature; + break; + case self::FORMAT_ECDSA: switch ($algorithm) { case OPENSSL_ALGO_SHA256: - return $this->DERtoECDSA($signature, 64); + return ECSignature::fromAsn1($signature, 64); + break; + case OPENSSL_ALGO_SHA384: - return $this->DERtoECDSA($signature, 96); + return ECSignature::fromAsn1($signature, 96); + break; + case OPENSSL_ALGO_SHA512: - return $this->DERtoECDSA($signature, 132); + return ECSignature::fromAsn1($signature, 132); + break; } throw new DataSigningException('Unable to generate a ECDSA signature with the given algorithm'); + break; + default: throw new DataSigningException('The given format does exists'); + break; } } @@ -84,81 +95,44 @@ public function checkSign($signature, $data, PublicKey $publicKey, $algorithm = Assert::oneOf($format, [self::FORMAT_ECDSA, self::FORMAT_DER], 'The format %s to sign request does not exists. Available format: %s'); $resource = $publicKey->getResource(); - if (1 != openssl_verify($data, $signature, $resource, $algorithm)) { - throw new DataCheckingSignException( - sprintf('OpenSSL data checking sign failed with error: %s', openssl_error_string()) - ); - } - openssl_free_key($resource); - } + switch ($format) { + case self::FORMAT_DER: + $signature = $signature; + break; - /** - * Convert a DER signature into ECDSA. - * - * The code is a copy/paste from another lib (web-token/jwt-core) which is not compatible with php <= 7.0 - * - * @see https://github.com/web-token/jwt-core/blob/master/Util/ECSignature.php - */ - private function DERtoECDSA($der, $partLength) - { - $hex = unpack('H*', $der)[1]; - if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE - throw new DataSigningException('Invalid signature provided'); - } - if ('81' === mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128 - $hex = mb_substr($hex, 6, null, '8bit'); - } else { - $hex = mb_substr($hex, 4, null, '8bit'); - } - if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER - throw new DataSigningException('Invalid signature provided'); - } + case self::FORMAT_ECDSA: + switch ($algorithm) { + case OPENSSL_ALGO_SHA256: + $signature = ECSignature::toAsn1($signature, 64); + break; - $Rl = hexdec(mb_substr($hex, 2, 2, '8bit')); - $R = $this->retrievePositiveInteger(mb_substr($hex, 4, $Rl * 2, '8bit')); - $R = str_pad($R, $partLength, '0', STR_PAD_LEFT); + case OPENSSL_ALGO_SHA384: + $signature = ECSignature::toAsn1($signature, 96); + break; - $hex = mb_substr($hex, 4 + $Rl * 2, null, '8bit'); - if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER - throw new DataSigningException('Invalid signature provided'); - } - $Sl = hexdec(mb_substr($hex, 2, 2, '8bit')); - $S = $this->retrievePositiveInteger(mb_substr($hex, 4, $Sl * 2, '8bit')); - $S = str_pad($S, $partLength, '0', STR_PAD_LEFT); + case OPENSSL_ALGO_SHA512: + $signature = ECSignature::toAsn1($signature, 132); + break; - return pack('H*', $R.$S); - } + default: + throw new DataSigningException('Unable to generate a ECDSA signature with the given algorithm'); + break; + } + break; - /** - * The code is a copy/paste from another lib (web-token/jwt-core) which is not compatible with php <= 7.0. - * - * @see https://github.com/web-token/jwt-core/blob/master/Util/ECSignature.php - */ - private function preparePositiveInteger($data) - { - if (mb_substr($data, 0, 2, '8bit') > '7f') { - return '00'.$data; - } - while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') <= '7f') { - $data = mb_substr($data, 2, null, '8bit'); + default: + throw new DataSigningException('The given format does exists'); + break; } - return $data; - } - - /** - * The code is a copy/paste from another lib (web-token/jwt-core) which is not compatible with php <= 7.0. - * - * @see https://github.com/web-token/jwt-core/blob/master/Util/ECSignature.php - */ - private function retrievePositiveInteger($data) - { - while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') { - $data = mb_substr($data, 2, null, '8bit'); + if (1 != openssl_verify($data, $signature, $resource, $algorithm)) { + throw new DataCheckingSignException( + sprintf('OpenSSL data checking sign failed with error: %s', openssl_error_string()) + ); } - return $data; + openssl_free_key($resource); } /** From f0e94224ac687b6d33857042e7a679e55dc5e27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Mon, 9 Dec 2019 10:02:28 +0100 Subject: [PATCH 114/126] Add php 7.3 and 7.4 --- .travis.yml | 27 +++++++++++----- composer.json | 4 +-- phpunit.xml.dist | 1 + tests/Cli/AbstractApplicationTest.php | 26 ++++++--------- tests/Cli/Action/InstallAwsElbActionTest.php | 2 +- .../Cli/Action/InstallAwsElbv2ActionTest.php | 2 +- .../Cli/Repository/AbstractRepositoryTest.php | 20 +++--------- tests/Core/AcmeClientTest.php | 6 ++-- .../Core/Challenge/Dns/Route53SolverTest.php | 2 +- tests/Core/Http/SecureHttpClientTest.php | 10 ++---- tests/Core/Http/ServerErrorHandlerTest.php | 14 ++++---- tests/Ssl/CertificateTest.php | 2 +- tests/Ssl/Generator/KeyPairGeneratorTest.php | 32 +++++++++---------- tests/Ssl/Parser/CertificateParserTest.php | 4 +-- tests/Ssl/Parser/KeyParserTest.php | 16 +++------- .../Signer/CertificateRequestSignerTest.php | 12 +++---- 16 files changed, 82 insertions(+), 98 deletions(-) diff --git a/.travis.yml b/.travis.yml index a434d871..22d67aca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,23 +15,30 @@ notifications: - ./tests/setup.sh install: composer install --no-interaction --no-progress --ansi script: ./tests/run.sh + env: + SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 cache: directories: - $HOME/.composer/cache/files .template_phpunit_low: &phpunit_low <<: *phpunit - install: composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable + install: + - composer require --dev "sebastian/comparator:^2.0" + - composer update --no-interaction --no-progress --ansi --prefer-lowest --prefer-stable env: - COMMENT: low deps + COMMENT: "low deps" + SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 .template_phpunit_high: &phpunit_high <<: *phpunit install: composer update --no-interaction --no-progress --ansi env: - COMMENT: high deps + COMMENT: "high deps" + SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: 1 jobs: include: - <<: *phpunit + dist: trusty php: 5.5 - <<: *phpunit php: 5.6 @@ -39,10 +46,14 @@ jobs: php: 7.0 - <<: *phpunit php: 7.1 - - <<: *phpunit_high + - <<: *phpunit php: 7.2 + - <<: *phpunit + php: 7.3 + - <<: *phpunit_high + php: 7.4 - stage: split - php: 7.2 + php: 7.4 cache: directories: - $HOME/.gitsplit/cache @@ -53,9 +64,9 @@ jobs: - git config remote.origin.mirror true - git fetch --unshallow || true env: - COMMENT: split repo + COMMENT: "split repo" script: - docker run --rm -t -e GH_TOKEN -v "$HOME/.gitsplit/cache":/cache/gitsplit -v ${PWD}:/srv jderusse/gitsplit:2.0 gitsplit --ref "${TRAVIS_BRANCH}"; env: - global: - secure: "guVs6Ym+RKa+4l3UAR/LbS360VziKilA+FHn89R9vszDuOm7Yj0+KvuE+c6nCSNL6JgVsBN6X14naDuDPrb/61HMCOZZhehL3lHu5j76sF7dxRgkftuT6vdn7z0Zz1Kaw7iH1oEgSsVpL9zKXEC9acgWHNb894ecBn80lhLiI5GfGzEcTEpfuowT0MOaFe0Oa5mFIFux4p4DWEZSkEmc21GYZI/CQe5VhRUGjApJNNiKtcHw1Qvdbu1Ubaq9W0issjXjEeBibSeIfjN6++L+k6QH1AITTTTJRRsz1O2T+8N9PxGjHR0o3+IfCC1oEQ6Ezlx10uS8TL85ytlRtZg+NNTJ1FDHQ39/M5ZcOpkrL9ohlfbVq3bcxp5i/Iu2UnO4ZvpMxRQLT1sH735gEt5NeNn9vsNm2M7ke/7oEi8rksixI1bakEFkOrsbGBDMg797wE9DtUFoY3JOKzKkCQjXiUkCsT2UalDoUauF7CkM3+QrEyhPOeNFK5lgRBsVJfeD6idNCmIvONoN8F8FCBlYvhrsLkTtwpO1uFyMboJ3/CIT8S4l1C3JPcTnMt2DUMYXxOv/tl/1x1MtL785tOb+OWs1Sta2H/Bllxp9qUPmNM1+uj71aTPqBLpWCwF2skRywW7Jqb9qGsROiOVUa6to1hMbbKSOoL59KqZF6e2EDpo=" + global: + secure: "guVs6Ym+RKa+4l3UAR/LbS360VziKilA+FHn89R9vszDuOm7Yj0+KvuE+c6nCSNL6JgVsBN6X14naDuDPrb/61HMCOZZhehL3lHu5j76sF7dxRgkftuT6vdn7z0Zz1Kaw7iH1oEgSsVpL9zKXEC9acgWHNb894ecBn80lhLiI5GfGzEcTEpfuowT0MOaFe0Oa5mFIFux4p4DWEZSkEmc21GYZI/CQe5VhRUGjApJNNiKtcHw1Qvdbu1Ubaq9W0issjXjEeBibSeIfjN6++L+k6QH1AITTTTJRRsz1O2T+8N9PxGjHR0o3+IfCC1oEQ6Ezlx10uS8TL85ytlRtZg+NNTJ1FDHQ39/M5ZcOpkrL9ohlfbVq3bcxp5i/Iu2UnO4ZvpMxRQLT1sH735gEt5NeNn9vsNm2M7ke/7oEi8rksixI1bakEFkOrsbGBDMg797wE9DtUFoY3JOKzKkCQjXiUkCsT2UalDoUauF7CkM3+QrEyhPOeNFK5lgRBsVJfeD6idNCmIvONoN8F8FCBlYvhrsLkTtwpO1uFyMboJ3/CIT8S4l1C3JPcTnMt2DUMYXxOv/tl/1x1MtL785tOb+OWs1Sta2H/Bllxp9qUPmNM1+uj71aTPqBLpWCwF2skRywW7Jqb9qGsROiOVUa6to1hMbbKSOoL59KqZF6e2EDpo=" diff --git a/composer.json b/composer.json index c72e7a72..42b4b8c5 100644 --- a/composer.json +++ b/composer.json @@ -65,10 +65,10 @@ "daverandom/libdns": "^2.0" }, "require-dev": { - "phpspec/prophecy": "^1.8", + "phpspec/prophecy": "^1.9", "symfony/finder": "^3.4|^4.0", "symfony/process": "^3.4|^4.0", - "symfony/phpunit-bridge": "^3.4.19|^4.0", + "symfony/phpunit-bridge": "^4.4", "symfony/var-dumper": "^3.4|^4.0" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 08911a33..6518c17c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,7 @@ beStrictAboutTestsThatDoNotTestAnything = "true" beStrictAboutOutputDuringTests = "true" colors = "true" + convertNoticesToExceptions = "false" verbose = "true" bootstrap = "vendor/autoload.php" > diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index 9a8a6b27..f86c0803 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -63,8 +63,8 @@ public function testFullProcess() $registerDisplay = $registerTester->getDisplay(); - $this->assertContains('No account key pair was found, generating one', $registerDisplay); - $this->assertContains('Account registered successfully', $registerDisplay); + $this->assertStringContainsString('No account key pair was found, generating one', $registerDisplay); + $this->assertStringContainsString('Account registered successfully', $registerDisplay); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/account/key.private.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/account/key.public.pem'); @@ -81,8 +81,8 @@ public function testFullProcess() $authorizeDisplay = $authorizeTest->getDisplay(); - $this->assertContains('The authorization tokens was successfully fetched', $authorizeDisplay); - $this->assertContains('http://acmephp.com/.well-known/acme-challenge/', $authorizeDisplay); + $this->assertStringContainsString('The authorization tokens was successfully fetched', $authorizeDisplay); + $this->assertStringContainsString('http://acmephp.com/.well-known/acme-challenge/', $authorizeDisplay); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/var/acmephp.com/authorization_challenge.json'); /* @@ -112,7 +112,7 @@ public function testFullProcess() $checkDisplay = $checkTest->getDisplay(); - $this->assertContains('The authorization check was successful', $checkDisplay); + $this->assertStringContainsString('The authorization check was successful', $checkDisplay); } finally { $process->stop(); } @@ -136,8 +136,8 @@ public function testFullProcess() $requestDisplay = $requestTest->getDisplay(); - $this->assertContains('The SSL certificate was fetched successfully', $requestDisplay); - $this->assertContains(Path::canonicalize(__DIR__.'/Fixtures/local/master'), $requestDisplay); + $this->assertStringContainsString('The SSL certificate was fetched successfully', $requestDisplay); + $this->assertStringContainsString(Path::canonicalize(__DIR__.'/Fixtures/local/master'), $requestDisplay); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.private.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/private/key.public.pem'); $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/cert.pem'); @@ -146,11 +146,9 @@ public function testFullProcess() $this->assertFileExists(__DIR__.'/../Cli/Fixtures/local/master/certs/acmephp.com/public/fullchain.pem'); } - /** - * @expectedException \AcmePhp\Cli\Exception\CommandFlowException - */ public function testCheckWithoutKeyFail() { + $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); $this->application = new SimpleApplication(); $command = $this->application->find('check'); @@ -162,11 +160,9 @@ public function testCheckWithoutKeyFail() ]); } - /** - * @expectedException \AcmePhp\Cli\Exception\CommandFlowException - */ public function testAuthorizeWithoutKeyFail() { + $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); $this->application = new SimpleApplication(); $command = $this->application->find('authorize'); @@ -178,11 +174,9 @@ public function testAuthorizeWithoutKeyFail() ]); } - /** - * @expectedException \AcmePhp\Cli\Exception\CommandFlowException - */ public function testRequestWithoutKeyFail() { + $this->expectException('AcmePhp\Cli\Exception\CommandFlowException'); $this->application = new SimpleApplication(); $command = $this->application->find('request'); diff --git a/tests/Cli/Action/InstallAwsElbActionTest.php b/tests/Cli/Action/InstallAwsElbActionTest.php index c9432f6d..c507d9b1 100644 --- a/tests/Cli/Action/InstallAwsElbActionTest.php +++ b/tests/Cli/Action/InstallAwsElbActionTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Tests\AcmePhp\Core\Challenge\Dns; +namespace Tests\AcmePhp\Cli\Action; use AcmePhp\Cli\Action\InstallAwsElbAction; use AcmePhp\Cli\Aws\ClientFactory; diff --git a/tests/Cli/Action/InstallAwsElbv2ActionTest.php b/tests/Cli/Action/InstallAwsElbv2ActionTest.php index 95ec294d..1a4d6928 100644 --- a/tests/Cli/Action/InstallAwsElbv2ActionTest.php +++ b/tests/Cli/Action/InstallAwsElbv2ActionTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Tests\AcmePhp\Core\Challenge\Dns; +namespace Tests\AcmePhp\Cli\Action; use AcmePhp\Cli\Action\InstallAwsElbv2Action; use AcmePhp\Cli\Aws\ClientFactory; diff --git a/tests/Cli/Repository/AbstractRepositoryTest.php b/tests/Cli/Repository/AbstractRepositoryTest.php index 66a10889..10680647 100644 --- a/tests/Cli/Repository/AbstractRepositoryTest.php +++ b/tests/Cli/Repository/AbstractRepositoryTest.php @@ -82,11 +82,9 @@ public function testLoadAccountKeyPair() $this->assertEquals($keyPair, $this->repository->loadAccountKeyPair()); } - /** - * @expectedException \AcmePhp\Cli\Exception\AcmeCliException - */ public function testLoadAccountKeyPairFail() { + $this->expectException('AcmePhp\Cli\Exception\AcmeCliException'); $this->repository->loadAccountKeyPair(); } @@ -108,11 +106,9 @@ public function testLoadDomainKeyPair() $this->assertEquals($keyPair, $this->repository->loadDomainKeyPair('example.com')); } - /** - * @expectedException \AcmePhp\Cli\Exception\AcmeCliException - */ public function testLoadDomainKeyPairFail() { + $this->expectException('AcmePhp\Cli\Exception\AcmeCliException'); $this->repository->loadDomainKeyPair('example.com'); } @@ -158,11 +154,9 @@ public function testLoadDomainAuthorizationChallenge() $this->assertEquals($challenge, $this->repository->loadDomainAuthorizationChallenge('example.com')); } - /** - * @expectedException \AcmePhp\Cli\Exception\AcmeCliException - */ public function testLoadDomainAuthorizationChallengeFail() { + $this->expectException('AcmePhp\Cli\Exception\AcmeCliException'); $this->repository->loadDomainAuthorizationChallenge('example.com'); } @@ -215,11 +209,9 @@ public function testLoadDomainDistinguishedName() $this->assertEquals($dn, $this->repository->loadDomainDistinguishedName('example.com')); } - /** - * @expectedException \AcmePhp\Cli\Exception\AcmeCliException - */ public function testLoadDomainDistinguishedNameFail() { + $this->expectException('AcmePhp\Cli\Exception\AcmeCliException'); $this->repository->loadDomainDistinguishedName('example.com'); } @@ -247,11 +239,9 @@ public function testLoadDomainCertificate() $this->assertEquals($cert, $this->repository->loadDomainCertificate('example.com')); } - /** - * @expectedException \AcmePhp\Cli\Exception\AcmeCliException - */ public function testLoadDomainCertificateFail() { + $this->expectException('AcmePhp\Cli\Exception\AcmeCliException'); $this->repository->loadDomainCertificate('example.com'); } diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index 6fb4ffd0..a27aab2e 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -53,7 +53,7 @@ public function testFullProcess(KeyOption $keyOption) */ $data = $client->registerAccount(); - $this->assertInternalType('array', $data); + $this->assertIsArray($data); $this->assertArrayHasKey('key', $data); $solver = new SimpleHttpSolver(); @@ -71,7 +71,7 @@ public function testFullProcess(KeyOption $keyOption) $this->assertInstanceOf(AuthorizationChallenge::class, $challenge); $this->assertEquals('acmephp.com', $challenge->getDomain()); - $this->assertContains('https://localhost:14000/chalZ/', $challenge->getUrl()); + $this->assertStringContainsString('https://localhost:14000/chalZ/', $challenge->getUrl()); $solver->solve($challenge); @@ -108,7 +108,7 @@ public function testFullProcess(KeyOption $keyOption) try { $client->revokeCertificate($response->getCertificate()); } catch (CertificateRevocationException $e) { - $this->assertContains('Unable to find specified certificate', $e->getPrevious()->getPrevious()->getMessage()); + $this->assertStringContainsString('Unable to find specified certificate', $e->getPrevious()->getPrevious()->getMessage()); } } diff --git a/tests/Core/Challenge/Dns/Route53SolverTest.php b/tests/Core/Challenge/Dns/Route53SolverTest.php index a8ee8fc0..bd9581af 100644 --- a/tests/Core/Challenge/Dns/Route53SolverTest.php +++ b/tests/Core/Challenge/Dns/Route53SolverTest.php @@ -74,7 +74,7 @@ public function testSolve() ], ] ); - $mockClient->changeResourceRecordSets(Argument::any())->shouldBeCalled(); + $mockClient->changeResourceRecordSets(Argument::any())->shouldBeCalled()->willReturn(['ChangeInfo' => ['Id' => 'foo']]); $mockClient->waitUntil('ResourceRecordSetsChanged', Argument::any())->shouldBeCalled(); $solver->solve($stubChallenge->reveal()); diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index 40a58486..586dce7d 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -72,11 +72,9 @@ public function testValidUnsignedJsonRequest() $this->assertEquals(['test' => 'ok'], $data); } - /** - * @expectedException \AcmePhp\Core\Exception\Protocol\ExpectedJsonException - */ public function testInvalidUnsignedJsonRequest() { + $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); $client = $this->createMockedClient([new Response(200, [], 'invalid json')], false); $client->unsignedRequest('GET', '/foo', ['foo' => 'bar'], true); } @@ -95,11 +93,9 @@ public function testValidSignedJsonRequest() $this->assertEquals(['test' => 'ok'], $data); } - /** - * @expectedException \AcmePhp\Core\Exception\Protocol\ExpectedJsonException - */ public function testInvalidSignedJsonRequest() { + $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); $client = $this->createMockedClient([new Response(200, [], 'invalid json')], false); $client->signedRequest('GET', '/foo', ['foo' => 'bar'], true); } @@ -142,7 +138,7 @@ public function testSignedRequestPayload() $body = \GuzzleHttp\Psr7\copy_to_string($request->getBody()); $payload = @json_decode($body, true); - $this->assertInternalType('array', $payload); + $this->assertIsArray($payload); $this->assertArrayHasKey('protected', $payload); $this->assertArrayHasKey('payload', $payload); $this->assertArrayHasKey('signature', $payload); diff --git a/tests/Core/Http/ServerErrorHandlerTest.php b/tests/Core/Http/ServerErrorHandlerTest.php index 75ac8d0c..2ce3d30c 100644 --- a/tests/Core/Http/ServerErrorHandlerTest.php +++ b/tests/Core/Http/ServerErrorHandlerTest.php @@ -60,9 +60,9 @@ public function testAcmeExceptionThrown($type, $exceptionClass) $exception = $errorHandler->createAcmeExceptionForResponse(new Request('GET', '/foo/bar'), $response); $this->assertInstanceOf($exceptionClass, $exception); - $this->assertContains($type, $exception->getMessage()); - $this->assertContains($exceptionClass.'Detail', $exception->getMessage()); - $this->assertContains('/foo/bar', $exception->getMessage()); + $this->assertStringContainsString($type, $exception->getMessage()); + $this->assertStringContainsString($exceptionClass.'Detail', $exception->getMessage()); + $this->assertStringContainsString('/foo/bar', $exception->getMessage()); } public function testDefaultExceptionThrownWithInvalidJson() @@ -75,8 +75,8 @@ public function testDefaultExceptionThrownWithInvalidJson() ); $this->assertInstanceOf(AcmeCoreServerException::class, $exception); - $this->assertContains('non-ACME', $exception->getMessage()); - $this->assertContains('/foo/bar', $exception->getMessage()); + $this->assertStringContainsString('non-ACME', $exception->getMessage()); + $this->assertStringContainsString('/foo/bar', $exception->getMessage()); } public function testDefaultExceptionThrownNonAcmeJson() @@ -89,7 +89,7 @@ public function testDefaultExceptionThrownNonAcmeJson() ); $this->assertInstanceOf(AcmeCoreServerException::class, $exception); - $this->assertContains('non-ACME', $exception->getMessage()); - $this->assertContains('/foo/bar', $exception->getMessage()); + $this->assertStringContainsString('non-ACME', $exception->getMessage()); + $this->assertStringContainsString('/foo/bar', $exception->getMessage()); } } diff --git a/tests/Ssl/CertificateTest.php b/tests/Ssl/CertificateTest.php index 084ea1e1..9531a7c4 100644 --- a/tests/Ssl/CertificateTest.php +++ b/tests/Ssl/CertificateTest.php @@ -102,6 +102,6 @@ public function test getPublicKeyResource returns a resource() $resource = $certificate->getPublicKeyResource(); - $this->assertInternalType('resource', $resource); + $this->assertIsResource($resource); } } diff --git a/tests/Ssl/Generator/KeyPairGeneratorTest.php b/tests/Ssl/Generator/KeyPairGeneratorTest.php index fdb146bb..23a8b722 100644 --- a/tests/Ssl/Generator/KeyPairGeneratorTest.php +++ b/tests/Ssl/Generator/KeyPairGeneratorTest.php @@ -45,10 +45,10 @@ public function test generateKeyPair generate random instance of KeyPair() $result = $this->service->generateKeyPair(new RsaKeyOption(1024)); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertContains('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertContains('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertInternalType('resource', $result->getPublicKey()->getResource()); - $this->assertInternalType('resource', $result->getPrivateKey()->getResource()); + $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); + $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); + $this->assertIsResource($result->getPublicKey()->getResource()); + $this->assertIsResource($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(1024, $details['bits']); @@ -63,10 +63,10 @@ public function test generateKeyPair generate random instance of KeyPair  )); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertContains('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertContains('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertInternalType('resource', $result->getPublicKey()->getResource()); - $this->assertInternalType('resource', $result->getPrivateKey()->getResource()); + $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); + $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); + $this->assertIsResource($result->getPublicKey()->getResource()); + $this->assertIsResource($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertArrayHasKey('dh', $details); @@ -77,10 +77,10 @@ public function test generateKeyPair generate random instance of KeyPair  $result = $this->service->generateKeyPair(new DsaKeyOption(1024)); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertContains('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertContains('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertInternalType('resource', $result->getPublicKey()->getResource()); - $this->assertInternalType('resource', $result->getPrivateKey()->getResource()); + $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); + $this->assertStringContainsString('-----BEGIN PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); + $this->assertIsResource($result->getPublicKey()->getResource()); + $this->assertIsResource($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(1024, $details['bits']); @@ -95,10 +95,10 @@ public function test generateKeyPair generate random instance of KeyPair  $result = $this->service->generateKeyPair(new EcKeyOption('secp112r1')); $this->assertInstanceOf(KeyPair::class, $result); - $this->assertContains('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); - $this->assertContains('-----BEGIN EC PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); - $this->assertInternalType('resource', $result->getPublicKey()->getResource()); - $this->assertInternalType('resource', $result->getPrivateKey()->getResource()); + $this->assertStringContainsString('-----BEGIN PUBLIC KEY-----', $result->getPublicKey()->getPEM()); + $this->assertStringContainsString('-----BEGIN EC PRIVATE KEY-----', $result->getPrivateKey()->getPEM()); + $this->assertIsResource($result->getPublicKey()->getResource()); + $this->assertIsResource($result->getPrivateKey()->getResource()); $details = openssl_pkey_get_details($result->getPrivateKey()->getResource()); $this->assertEquals(112, $details['bits']); diff --git a/tests/Ssl/Parser/CertificateParserTest.php b/tests/Ssl/Parser/CertificateParserTest.php index e8821f18..309d7ef5 100644 --- a/tests/Ssl/Parser/CertificateParserTest.php +++ b/tests/Ssl/Parser/CertificateParserTest.php @@ -28,11 +28,9 @@ public function setUp() $this->service = new CertificateParser(); } - /** - * @expectedException \AcmePhp\Ssl\Exception\CertificateParsingException - */ public function test parse raise proper exception() { + $this->expectException('AcmePhp\Ssl\Exception\CertificateParsingException'); $this->service->parse(new Certificate('Not a cert')); } diff --git a/tests/Ssl/Parser/KeyParserTest.php b/tests/Ssl/Parser/KeyParserTest.php index ad97aed8..6b0ad0d9 100644 --- a/tests/Ssl/Parser/KeyParserTest.php +++ b/tests/Ssl/Parser/KeyParserTest.php @@ -30,19 +30,15 @@ public function setUp() $this->service = new KeyParser(); } - /** - * @expectedException \AcmePhp\Ssl\Exception\KeyParsingException - */ public function test parse PublicKey raise proper exception() { + $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PublicKey('Not a key')); } - /** - * @expectedException \AcmePhp\Ssl\Exception\KeyParsingException - */ public function test parse PrivateKey raise proper exception() { + $this->expectException('AcmePhp\Ssl\Exception\KeyParsingException'); $this->service->parse(new PrivateKey('Not a key')); } @@ -51,11 +47,9 @@ public function test get PrivateKey has invalid detail() $this->assertFalse($this->service->parse($this->getPrivateKey())->hasDetail('invalid')); } - /** - * @expectedException \InvalidArgumentException - */ public function test get PrivateKey get invalid detail raise proper exception() { + $this->expectException('InvalidArgumentException'); $this->service->parse($this->getPrivateKey())->getDetail('invalid'); } @@ -67,7 +61,7 @@ public function test parse PrivateKey returns instance of ParsedKey() $this->assertInstanceOf(Key::class, $result->getSource()); $this->assertEquals(OPENSSL_KEYTYPE_RSA, $result->getType()); $this->assertEquals(4096, $result->getBits()); - $this->assertInternalType('array', $result->getDetails()); + $this->assertIsArray($result->getDetails()); $this->assertEquals(256, \strlen($result->getDetail('p'))); $this->assertEquals(256, \strlen($result->getDetail('q'))); $this->assertEquals(trim($this->getPublicKey()->getPEM()), trim($result->getKey())); @@ -81,7 +75,7 @@ public function test parse PublicKey returns instance of ParsedKey() $this->assertInstanceOf(Key::class, $result->getSource()); $this->assertEquals(OPENSSL_KEYTYPE_RSA, $result->getType()); $this->assertEquals(4096, $result->getBits()); - $this->assertInternalType('array', $result->getDetails()); + $this->assertIsArray($result->getDetails()); $this->assertEquals(trim($this->getPublicKey()->getPEM()), trim($result->getKey())); } diff --git a/tests/Ssl/Signer/CertificateRequestSignerTest.php b/tests/Ssl/Signer/CertificateRequestSignerTest.php index 4f3330df..779c3a0d 100644 --- a/tests/Ssl/Signer/CertificateRequestSignerTest.php +++ b/tests/Ssl/Signer/CertificateRequestSignerTest.php @@ -41,8 +41,8 @@ public function test signCertificateRequest returns a certificate() $result = $this->service->signCertificateRequest( new CertificateRequest($dummyDistinguishedName, $dummyKeyPair) ); - $this->assertInternalType('string', $result); - $this->assertContains('-----BEGIN CERTIFICATE REQUEST-----', $result); + $this->assertIsString($result); + $this->assertStringContainsString('-----BEGIN CERTIFICATE REQUEST-----', $result); $csrResult = openssl_csr_get_subject($result, false); $this->assertSame( @@ -69,8 +69,8 @@ public function test signCertificateRequest use default values() $result = $this->service->signCertificateRequest( new CertificateRequest($dummyDistinguishedName, $dummyKeyPair) ); - $this->assertInternalType('string', $result); - $this->assertContains('-----BEGIN CERTIFICATE REQUEST-----', $result); + $this->assertIsString($result); + $this->assertStringContainsString('-----BEGIN CERTIFICATE REQUEST-----', $result); $csrResult = openssl_csr_get_subject($result, false); $this->assertSame( [ @@ -91,8 +91,8 @@ public function test signCertificateRequest with subject alternative names( $result = $this->service->signCertificateRequest( new CertificateRequest($dummyDistinguishedName, $dummyKeyPair) ); - $this->assertInternalType('string', $result); - $this->assertContains('-----BEGIN CERTIFICATE REQUEST-----', $result); + $this->assertIsString($result); + $this->assertStringContainsString('-----BEGIN CERTIFICATE REQUEST-----', $result); $csrResult = openssl_csr_get_subject($result, false); $this->assertSame( From dbc895bf12578960c6e0e5e6e1380012570f9977 Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Wed, 11 Dec 2019 16:44:13 +0800 Subject: [PATCH 115/126] =?UTF-8?q?DNSPOD=E4=B8=8D=E6=8E=A5=E5=8F=97TXT?= =?UTF-8?q?=E5=80=BC+=E5=90=8D=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/Challenge/Dns/TencentCloudSolver.php | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Core/Challenge/Dns/TencentCloudSolver.php b/src/Core/Challenge/Dns/TencentCloudSolver.php index 7418b374..3b657d86 100644 --- a/src/Core/Challenge/Dns/TencentCloudSolver.php +++ b/src/Core/Challenge/Dns/TencentCloudSolver.php @@ -132,39 +132,37 @@ public function solveAll(array $authorizationChallenges) $recordType = $this->extractor->getRecordType($authorizationChallenge); } - if ('cname' === strtolower($recordType)) { - // Because DNSPod can't create conflicting cname records - // So we'd delete existing records first - clear: - - $cns->RecordList([ - 'domain' => $topLevelDomain, - 'subDomain' => $subDomain, - 'recordType' => $recordType, - ]); - try { - $data = json_decode($cns->getLastResponse(), true); - } catch (InvalidArgumentException $e) { - $err = $cns->getError(); - if ($err) { - if ($err->getCode() == 3000) { - throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); - } - print_r($err->getExt()); - throw new \Exception($err->getMessage(), $err->getCode()); - } else { - echo $cns->getLastResponse(); + // Because DNSPod can't create conflicting cname records + // So we'd delete existing records first + clear: + + $cns->RecordList([ + 'domain' => $topLevelDomain, + 'subDomain' => $subDomain, + 'recordType' => $recordType, + ]); + try { + $data = json_decode($cns->getLastResponse(), true); + } catch (InvalidArgumentException $e) { + $err = $cns->getError(); + if ($err) { + if ($err->getCode() == 3000) { + throw new \Exception('DNSPod Api Exception:' . QcloudApi_Common_Request::getRawResponse(), $err->getCode()); } - throw $e; + print_r($err->getExt()); + throw new \Exception($err->getMessage(), $err->getCode()); + } else { + echo $cns->getLastResponse(); } - if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { - foreach ($data['data']['records'] as $existedRecord) { - if (isset($existedRecord['id'])) { - $cns->RecordDelete([ - 'domain' => $topLevelDomain, - 'recordId' => $existedRecord['id'], - ]); - } + throw $e; + } + if ($data && isset($data['data']) && isset($data['data']['records']) && \is_array($data['data']['records']) && \count($data['data']['records'])) { + foreach ($data['data']['records'] as $existedRecord) { + if (isset($existedRecord['id'])) { + $cns->RecordDelete([ + 'domain' => $topLevelDomain, + 'recordId' => $existedRecord['id'], + ]); } } } @@ -184,6 +182,9 @@ public function solveAll(array $authorizationChallenges) if (strpos($err->getMessage(), '子域名负载均衡数量超出限制') !== false) { goto clear; } + if ($err->getCode() == '8104104') { + goto clear; + } throw new Exception($err->getMessage(), $err->getCode()); } From c592b8500f0123eb4f7077763b4f9faeac132f0d Mon Sep 17 00:00:00 2001 From: Paul Adams Date: Wed, 11 Dec 2019 22:34:43 +0000 Subject: [PATCH 116/126] added orderNotReady error type --- .../Server/OrderNotReadyServerException.php | 27 +++++++++++++++++++ src/Core/Http/ServerErrorHandler.php | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 src/Core/Exception/Server/OrderNotReadyServerException.php diff --git a/src/Core/Exception/Server/OrderNotReadyServerException.php b/src/Core/Exception/Server/OrderNotReadyServerException.php new file mode 100644 index 00000000..462cbf4e --- /dev/null +++ b/src/Core/Exception/Server/OrderNotReadyServerException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace AcmePhp\Core\Exception\Server; + +use AcmePhp\Core\Exception\AcmeCoreServerException; +use Psr\Http\Message\RequestInterface; + +class OrderNotReadyServerException extends AcmeCoreServerException +{ + public function __construct(RequestInterface $request, $detail, \Exception $previous = null) + { + parent::__construct( + $request, + '[orderNotReady] Order could not be finalized: '.$detail, + $previous + ); + } +} diff --git a/src/Core/Http/ServerErrorHandler.php b/src/Core/Http/ServerErrorHandler.php index b602ad30..3fd7ecc0 100644 --- a/src/Core/Http/ServerErrorHandler.php +++ b/src/Core/Http/ServerErrorHandler.php @@ -18,6 +18,7 @@ use AcmePhp\Core\Exception\Server\InternalServerException; use AcmePhp\Core\Exception\Server\InvalidEmailServerException; use AcmePhp\Core\Exception\Server\MalformedServerException; +use AcmePhp\Core\Exception\Server\OrderNotReadyServerException; use AcmePhp\Core\Exception\Server\RateLimitedServerException; use AcmePhp\Core\Exception\Server\TlsServerException; use AcmePhp\Core\Exception\Server\UnauthorizedServerException; @@ -41,6 +42,7 @@ class ServerErrorHandler 'serverInternal' => InternalServerException::class, 'invalidEmail' => InvalidEmailServerException::class, 'malformed' => MalformedServerException::class, + 'orderNotReady' => OrderNotReadyServerException::class, 'rateLimited' => RateLimitedServerException::class, 'tls' => TlsServerException::class, 'unauthorized' => UnauthorizedServerException::class, From 25a12e374a85ddafab66f0514cb3f0f8d285846c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Fri, 13 Dec 2019 11:15:25 +0100 Subject: [PATCH 117/126] Split Sign and Request --- .gitignore | 10 -- composer.json | 3 +- phpunit.xml.dist | 2 +- src/Cli/Command/AuthorizeCommand.php | 2 + src/Cli/Command/CheckCommand.php | 2 + src/Cli/Command/MonitoringTestCommand.php | 2 + src/Cli/Command/RegisterCommand.php | 2 + src/Cli/Command/RequestCommand.php | 4 + src/Cli/Command/RevokeCommand.php | 2 + src/Cli/Command/RunCommand.php | 2 + src/Cli/Command/SelfUpdateCommand.php | 16 +- src/Cli/Command/StatusCommand.php | 2 + src/Core/AcmeClient.php | 61 ++++--- src/Core/Http/SecureHttpClient.php | 168 ++++++++++++------ tests/Cli/AbstractApplicationTest.php | 8 +- .../.well-known/acme-challenge/.gitkeep | 0 tests/Cli/Fixtures/local/backup/.gitignore | 2 + tests/Cli/Fixtures/local/backup/.gitkeep | 0 tests/Cli/Fixtures/local/master/.gitignore | 2 + tests/Cli/Fixtures/local/master/.gitkeep | 0 tests/Cli/Fixtures/sftp/.gitignore | 2 + tests/Cli/Fixtures/sftp/.gitkeep | 0 tests/Cli/MonitoredApplicationTest.php | 1 - tests/Cli/SftpNginxProxyApplicationTest.php | 1 - tests/Cli/SimpleApplicationTest.php | 1 - tests/Core/AbstractFunctionnalTest.php | 44 ++--- tests/Core/AcmeClientTest.php | 8 +- .../.well-known/acme-challenge/.gitkeep | 0 tests/Core/Http/SecureHttpClientTest.php | 154 ++++++++++++++++ tests/run.sh | 2 +- tests/setup.sh | 7 +- tests/teardown.sh | 2 +- 32 files changed, 361 insertions(+), 151 deletions(-) delete mode 100644 tests/Cli/Fixtures/challenges/.well-known/acme-challenge/.gitkeep create mode 100644 tests/Cli/Fixtures/local/backup/.gitignore delete mode 100644 tests/Cli/Fixtures/local/backup/.gitkeep create mode 100644 tests/Cli/Fixtures/local/master/.gitignore delete mode 100644 tests/Cli/Fixtures/local/master/.gitkeep create mode 100644 tests/Cli/Fixtures/sftp/.gitignore delete mode 100644 tests/Cli/Fixtures/sftp/.gitkeep delete mode 100644 tests/Core/Fixtures/challenges/.well-known/acme-challenge/.gitkeep diff --git a/.gitignore b/.gitignore index cb7a87ce..3cda68c4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,13 +5,3 @@ vendor/ .php_cs.cache doc/.couscous .subsplit -tests/Cli/Fixtures/local/master/* -!tests/Cli/Fixtures/local/master/.gitkeep -tests/Cli/Fixtures/sftp/* -!tests/Cli/Fixtures/sftp/.gitkeep -tests/Cli/Fixtures/monitoring/* -!tests/Cli/Fixtures/monitoring/.gitkeep -tests/Cli/Fixtures/challenges/.well-known/acme-challenge/* -!tests/Cli/Fixtures/challenges/.well-known/acme-challenge/.gitkeep -tests/Core/Fixtures/challenges/.well-known/acme-challenge/* -!tests/Core/Fixtures/challenges/.well-known/acme-challenge/.gitkeep diff --git a/composer.json b/composer.json index 42b4b8c5..0f79c2d4 100644 --- a/composer.json +++ b/composer.json @@ -67,8 +67,7 @@ "require-dev": { "phpspec/prophecy": "^1.9", "symfony/finder": "^3.4|^4.0", - "symfony/process": "^3.4|^4.0", - "symfony/phpunit-bridge": "^4.4", + "symfony/phpunit-bridge": "^5.0", "symfony/var-dumper": "^3.4|^4.0" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6518c17c..e94cde70 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,7 +13,7 @@ - + diff --git a/src/Cli/Command/AuthorizeCommand.php b/src/Cli/Command/AuthorizeCommand.php index 739b66fb..c767b0d1 100644 --- a/src/Cli/Command/AuthorizeCommand.php +++ b/src/Cli/Command/AuthorizeCommand.php @@ -132,5 +132,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $solverName, implode(' ', array_keys($order->getAuthorizationsChallenges())) )); + + return 0; } } diff --git a/src/Cli/Command/CheckCommand.php b/src/Cli/Command/CheckCommand.php index 259d489e..200d5ae1 100644 --- a/src/Cli/Command/CheckCommand.php +++ b/src/Cli/Command/CheckCommand.php @@ -149,5 +149,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $solver->cleanup($authorizationChallenge); } } + + return 0; } } diff --git a/src/Cli/Command/MonitoringTestCommand.php b/src/Cli/Command/MonitoringTestCommand.php index e6ca6b28..80e04bb1 100644 --- a/src/Cli/Command/MonitoringTestCommand.php +++ b/src/Cli/Command/MonitoringTestCommand.php @@ -67,5 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->notice('Triggered successfully'); $this->info('You should have been alerted'); + + return 0; } } diff --git a/src/Cli/Command/RegisterCommand.php b/src/Cli/Command/RegisterCommand.php index 52e2e656..de703367 100644 --- a/src/Cli/Command/RegisterCommand.php +++ b/src/Cli/Command/RegisterCommand.php @@ -90,5 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $client->registerAccount(null, $email); $this->notice('Account registered successfully!'); + + return 0; } } diff --git a/src/Cli/Command/RequestCommand.php b/src/Cli/Command/RequestCommand.php index 6b8befe6..265924b0 100644 --- a/src/Cli/Command/RequestCommand.php +++ b/src/Cli/Command/RequestCommand.php @@ -238,6 +238,8 @@ private function executeFirstRequest($domain, array $alternativeNames, $keyType) ]; $this->info(strtr(strtr($success, $replacements), ['{domain}' => $domain])); + + return 0; } /** @@ -343,6 +345,8 @@ private function executeRenewal($domain, array $alternativeNames) throw $e; } + + return 0; } /** diff --git a/src/Cli/Command/RevokeCommand.php b/src/Cli/Command/RevokeCommand.php index 89e5279d..0a6db097 100644 --- a/src/Cli/Command/RevokeCommand.php +++ b/src/Cli/Command/RevokeCommand.php @@ -72,5 +72,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->notice('Certificate revoked successfully!'); + + return 0; } } diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 52ba77e6..8204ee42 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -102,6 +102,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->installCertificate($response, $domainConfig['install']); } + + return 0; } private function register($email, KeyOption $keyOption) diff --git a/src/Cli/Command/SelfUpdateCommand.php b/src/Cli/Command/SelfUpdateCommand.php index 88d41cae..bb11a37f 100644 --- a/src/Cli/Command/SelfUpdateCommand.php +++ b/src/Cli/Command/SelfUpdateCommand.php @@ -102,13 +102,13 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('rollback')) { $this->rollback(); - return; + return 0; } if ($input->getOption('check')) { $this->printAvailableUpdates(); - return; + return 0; } /* @@ -117,25 +117,25 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('dev')) { $this->updateToDevelopmentBuild(); - return; + return 0; } if ($input->getOption('pre')) { $this->updateToPreReleaseBuild(); - return; + return 0; } if ($input->getOption('stable')) { $this->updateToStableBuild(); - return; + return 0; } if ($input->getOption('non-dev')) { $this->updateToMostRecentNonDevRemote(); - return; + return 0; } /* @@ -145,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($parser->isStable($this->version)) { $this->updateToStableBuild(); - return; + return 0; } /* @@ -153,6 +153,8 @@ protected function execute(InputInterface $input, OutputInterface $output) * of stability. */ $this->updateToMostRecentNonDevRemote(); + + return 0; } protected function getStableUpdater() diff --git a/src/Cli/Command/StatusCommand.php b/src/Cli/Command/StatusCommand.php index a88e4c95..daec4282 100644 --- a/src/Cli/Command/StatusCommand.php +++ b/src/Cli/Command/StatusCommand.php @@ -87,5 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } $table->render(); + + return 0; } } diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 118c11c2..f6e538af 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -110,8 +110,10 @@ public function registerAccount($agreement = null, $email = null) } $this->requestResource('POST', ResourcesDirectory::NEW_ACCOUNT, $payload); + $account = $this->getResourceAccount(); + $client = $this->getHttpClient(); - return $this->getHttpClient()->signedKidRequest('POST', $this->getResourceAccount(), $this->getResourceAccount()); + return $client->request('POST', $account, $client->signKidPayload($account, $account, null)); } /** @@ -147,14 +149,16 @@ function ($domain) { ), ]; - $response = $this->getHttpClient()->signedKidRequest('POST', $this->getResourceUrl(ResourcesDirectory::NEW_ORDER), $this->getResourceAccount(), $payload); + $client = $this->getHttpClient(); + $resourceUrl = $this->getResourceUrl(ResourcesDirectory::NEW_ORDER); + $response = $client->request('POST', $resourceUrl, $client->signKidPayload($resourceUrl, $this->getResourceAccount(), $payload)); if (!isset($response['authorizations']) || !$response['authorizations']) { throw new ChallengeNotSupportedException(); } - $orderEndpoint = $this->getHttpClient()->getLastLocation(); + $orderEndpoint = $client->getLastLocation(); foreach ($response['authorizations'] as $authorizationEndpoint) { - $authorizationsResponse = $this->getHttpClient()->unsignedRequest('GET', $authorizationEndpoint, null, true); + $authorizationsResponse = $client->request('POST', $authorizationEndpoint, $client->signKidPayload($authorizationEndpoint, $this->getResourceAccount(), null)); $domain = (empty($authorizationsResponse['wildcard']) ? '' : '*.').$authorizationsResponse['identifier']['value']; foreach ($authorizationsResponse['challenges'] as $challenge) { $authorizationsChallenges[$domain][] = $this->createAuthorizationChallenge($authorizationsResponse['identifier']['value'], $challenge); @@ -169,7 +173,9 @@ function ($domain) { */ public function reloadAuthorization(AuthorizationChallenge $challenge) { - $response = (array) $this->getHttpClient()->unsignedRequest('GET', $challenge->getUrl()); + $client = $this->getHttpClient(); + $challengeUrl = $challenge->getUrl(); + $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null)); return $this->createAuthorizationChallenge($challenge->getDomain(), $response); } @@ -182,15 +188,17 @@ public function challengeAuthorization(AuthorizationChallenge $challenge, $timeo Assert::integer($timeout, 'challengeAuthorization::$timeout expected an integer. Got: %s'); $endTime = time() + $timeout; - $response = (array) $this->getHttpClient()->unsignedRequest('GET', $challenge->getUrl()); + $client = $this->getHttpClient(); + $challengeUrl = $challenge->getUrl(); + $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null)); if ('pending' === $response['status']) { - $response = (array) $this->getHttpClient()->signedKidRequest('POST', $challenge->getUrl(), $this->getResourceAccount(), []); + $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), [])); } // Waiting loop while (time() <= $endTime && (!isset($response['status']) || 'pending' === $response['status'])) { sleep(1); - $response = (array) $this->getHttpClient()->unsignedRequest('GET', $challenge->getUrl()); + $response = (array) $client->request('POST', $challengeUrl, $client->signKidPayload($challengeUrl, $this->getResourceAccount(), null)); } if (isset($response['status']) && 'pending' === $response['status']) { @@ -224,30 +232,30 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, Assert::integer($timeout, 'finalizeOrder::$timeout expected an integer. Got: %s'); $endTime = time() + $timeout; - $response = $this->getHttpClient()->signedKidRequest('GET', $order->getOrderEndpoint(), $this->getResourceAccount()); + $client = $this->getHttpClient(); + $orderEndpoint = $order->getOrderEndpoint(); + $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); if (\in_array($response['status'], ['pending', 'ready'])) { $humanText = ['-----BEGIN CERTIFICATE REQUEST-----', '-----END CERTIFICATE REQUEST-----']; $csrContent = $this->csrSigner->signCertificateRequest($csr); $csrContent = trim(str_replace($humanText, '', $csrContent)); - $csrContent = trim($this->getHttpClient()->getBase64Encoder()->encode(base64_decode($csrContent))); + $csrContent = trim($client->getBase64Encoder()->encode(base64_decode($csrContent))); - $response = $this->getHttpClient()->signedKidRequest('POST', $response['finalize'], $this->getResourceAccount(), [ - 'csr' => $csrContent, - ]); + $response = $client->request('POST', $response['finalize'], $client->signKidPayload($response['finalize'], $this->getResourceAccount(), ['csr' => $csrContent])); } // Waiting loop while (time() <= $endTime && (!isset($response['status']) || \in_array($response['status'], ['pending', 'processing', 'ready']))) { sleep(1); - $response = $this->getHttpClient()->signedKidRequest('GET', $order->getOrderEndpoint(), $this->getResourceAccount()); + $response = $client->request('POST', $orderEndpoint, $client->signKidPayload($orderEndpoint, $this->getResourceAccount(), null)); } if ('valid' !== $response['status']) { throw new CertificateRequestFailedException('The order has not been validated'); } - $response = $this->getHttpClient()->unsignedRequest('GET', $response['certificate'], null, false); + $response = $client->request('POST', $response['certificate'], $client->signKidPayload($response['certificate'], $this->getResourceAccount(), null), false); $certificatesChain = null; foreach (array_reverse(explode("\n\n", $response)) as $pem) { $certificatesChain = new Certificate($pem, $certificatesChain); @@ -261,7 +269,7 @@ public function finalizeOrder(CertificateOrder $order, CertificateRequest $csr, */ public function revokeCertificate(Certificate $certificate, RevocationReason $revocationReason = null) { - if (!$this->getResourceUrl(ResourcesDirectory::REVOKE_CERT)) { + if (!$endpoint = $this->getResourceUrl(ResourcesDirectory::REVOKE_CERT)) { throw new CertificateRevocationException('This ACME server does not support certificate revocation.'); } @@ -273,14 +281,14 @@ public function revokeCertificate(Certificate $certificate, RevocationReason $re $formattedPem = str_ireplace('-----BEGIN CERTIFICATE-----', '', $formattedPem); $formattedPem = str_ireplace('-----END CERTIFICATE-----', '', $formattedPem); - $formattedPem = $this->getHttpClient()->getBase64Encoder()->encode(base64_decode(trim($formattedPem))); + $client = $this->getHttpClient(); + $formattedPem = $client->getBase64Encoder()->encode(base64_decode(trim($formattedPem))); try { - $this->getHttpClient()->signedKidRequest( + $client->request( 'POST', - $this->getResourceUrl(ResourcesDirectory::REVOKE_CERT), - $this->getResourceAccount(), - ['certificate' => $formattedPem, 'reason' => $revocationReason->getReasonType()], + $endpoint, + $client->signKidPayload($endpoint, $this->getResourceAccount(), ['certificate' => $formattedPem, 'reason' => $revocationReason->getReasonType()]), false ); } catch (AcmeCoreServerException $e) { @@ -301,7 +309,7 @@ public function getResourceUrl($resource) { if (!$this->directory) { $this->directory = new ResourcesDirectory( - $this->getHttpClient()->unsignedRequest('GET', $this->directoryUrl, null, true) + $this->getHttpClient()->request('GET', $this->directoryUrl) ); } @@ -323,10 +331,13 @@ public function getResourceUrl($resource) */ protected function requestResource($method, $resource, array $payload, $returnJson = true) { - return $this->getHttpClient()->signedRequest( + $client = $this->getHttpClient(); + $endpoint = $this->getResourceUrl($resource); + + return $client->request( $method, - $this->getResourceUrl($resource), - $payload, + $endpoint, + $client->signJwkPayload($endpoint, $payload), $returnJson ); } diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index 07f35d13..5d4a3a04 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -110,32 +110,9 @@ public function __construct( */ public function signedRequest($method, $endpoint, array $payload = [], $returnJson = true) { - $privateKey = $this->accountKeyPair->getPrivateKey(); - - $alg = $this->getAlg(); - $protected = [ - 'alg' => $alg, - 'jwk' => $this->getJWK(), - 'nonce' => $this->getNonce(), - 'url' => $endpoint, - ]; - list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); - - $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); - $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); - $signature = $this->base64Encoder->encode($this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format)); - - $payload = [ - 'protected' => $protected, - 'payload' => $payload, - 'signature' => $signature, - ]; + @trigger_error('The method signedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead', E_USER_DEPRECATED); - try { - return $this->unsignedRequest($method, $endpoint, $payload, $returnJson); - } catch (BadNonceServerException $e) { - return $this->unsignedRequest($method, $endpoint, $payload, $returnJson); - } + return $this->request($method, $endpoint, $this->signJwkPayload($endpoint, $payload), $returnJson); } private function getAlg() @@ -217,51 +194,104 @@ public function getJWKThumbprint() } /** - * Send a request encoded in the format defined by the ACME protocol. + * Generates a payload signed with account's KID. * - * @param string $method * @param string $endpoint * @param string $account * @param array $payload - * @param bool $returnJson * - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code - * @throws AcmeCoreClientException when an error occured during response parsing + * @return array the signed Pyaload + */ + public function signKidPayload($endpoint, $account, array $payload = null) + { + return $this->signPayload( + [ + 'alg' => $this->getAlg(), + 'kid' => $account, + 'nonce' => $this->getNonce(), + 'url' => $endpoint, + ], + $payload + ); + } + + /** + * Generates a payload signed with JWK. * - * @return array|string Array of parsed JSON if $returnJson = true, string otherwise + * @param string $endpoint + * @param array $payload + * + * @return array the signed Payload */ - public function signedKidRequest($method, $endpoint, $account, array $payload = [], $returnJson = true) + public function signJwkPayload($endpoint, array $payload = null) { - $privateKey = $this->accountKeyPair->getPrivateKey(); + return $this->signPayload( + [ + 'alg' => $this->getAlg(), + 'jwk' => $this->getJWK(), + 'nonce' => $this->getNonce(), + 'url' => $endpoint, + ], + $payload + ); + } - $alg = $this->getAlg(); - $protected = [ - 'alg' => $alg, - 'kid' => $account, - 'nonce' => $this->getNonce(), - 'url' => $endpoint, - ]; + /** + * Sign the given Payload. + * + * @param array $protected + * @param array|null $payload + * + * @return array + */ + private function signPayload(array $protected, array $payload = null) + { + if (!isset($protected['alg'])) { + throw new \InvalidArgumentException('The property "alg" is required in the protected array'); + } + $alg = $protected['alg']; + + $privateKey = $this->accountKeyPair->getPrivateKey(); list($algorithm, $format) = $this->extractSignOptionFromJWSAlg($alg); $protected = $this->base64Encoder->encode(json_encode($protected, JSON_UNESCAPED_SLASHES)); - if ($payload === []) { + if (null === $payload) { + $payload = ''; + } elseif ($payload === []) { $payload = $this->base64Encoder->encode('{}'); } else { $payload = $this->base64Encoder->encode(json_encode($payload, JSON_UNESCAPED_SLASHES)); } - $signature = $this->base64Encoder->encode($this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format)); + $signature = $this->base64Encoder->encode( + $this->dataSigner->signData($protected.'.'.$payload, $privateKey, $algorithm, $format) + ); - $payload = [ + return [ 'protected' => $protected, 'payload' => $payload, 'signature' => $signature, ]; + } - try { - return $this->unsignedRequest($method, $endpoint, $payload, $returnJson); - } catch (BadNonceServerException $e) { - return $this->unsignedRequest($method, $endpoint, $payload, $returnJson); - } + /** + * Send a request encoded in the format defined by the ACME protocol. + * + * @param string $method + * @param string $endpoint + * @param string $account + * @param array $payload + * @param bool $returnJson + * + * @throws AcmeCoreClientException when an error occured during response parsing + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code + * + * @return array|string Array of parsed JSON if $returnJson = true, string otherwise + */ + public function signedKidRequest($method, $endpoint, $account, array $payload = [], $returnJson = true) + { + @trigger_error('The method signedKidRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request, signKidPayload instead.', E_USER_DEPRECATED); + + return $this->request($method, $endpoint, $this->signKidPayload($endpoint, $account, $payload), $returnJson); } /** @@ -272,20 +302,50 @@ public function signedKidRequest($method, $endpoint, $account, array $payload = * @param array $data * @param bool $returnJson * - * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * @throws AcmeCoreClientException when an error occured during response parsing * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code * * @return array|string Array of parsed JSON if $returnJson = true, string otherwise */ public function unsignedRequest($method, $endpoint, array $data = null, $returnJson = true) { - $request = $this->createRequest($method, $endpoint, $data); + @trigger_error('The method unsignedRequest is deprecated since version 1.1 and will be removed in 2.0. use methods request instead.', E_USER_DEPRECATED); + + return $this->request($method, $endpoint, $data, $returnJson); + } + + /** + * Send a request encoded in the format defined by the ACME protocol. + * + * @param string $method + * @param string $endpoint + * @param array $data + * @param bool $returnJson + * + * @throws AcmeCoreClientException when an error occured during response parsing + * @throws ExpectedJsonException when $returnJson = true and the response is not valid JSON + * @throws AcmeCoreServerException when the ACME server returns an error HTTP status code + * + * @return array|string Array of parsed JSON if $returnJson = true, string otherwise + */ + public function request($method, $endpoint, array $data = [], $returnJson = true) + { + $call = function () use ($method, $endpoint, $data) { + $request = $this->createRequest($method, $endpoint, $data); + try { + $this->lastResponse = $this->httpClient->send($request); + } catch (\Exception $exception) { + $this->handleClientException($request, $exception); + } + + return $request; + }; try { - $this->lastResponse = $this->httpClient->send($request); - } catch (\Exception $exception) { - $this->handleClientException($request, $exception); + $request = $call(); + } catch (BadNonceServerException $e) { + $request = $call(); } $body = \GuzzleHttp\Psr7\copy_to_string($this->lastResponse->getBody()); @@ -421,7 +481,7 @@ private function getNonce() } if (null !== $this->nonceEndpoint) { - $this->unsignedRequest('HEAD', $this->nonceEndpoint, null, false); + $this->request('HEAD', $this->nonceEndpoint, [], false); if ($this->lastResponse->hasHeader('Replay-Nonce')) { return $this->lastResponse->getHeaderLine('Replay-Nonce'); } diff --git a/tests/Cli/AbstractApplicationTest.php b/tests/Cli/AbstractApplicationTest.php index f86c0803..b8f164d4 100644 --- a/tests/Cli/AbstractApplicationTest.php +++ b/tests/Cli/AbstractApplicationTest.php @@ -95,11 +95,7 @@ public function testFullProcess() true ); - $process = $this->createServerProcess($challenge['token'], $challenge['payload']); - $process->start(); - - $this->assertTrue($process->isRunning()); - + $this->handleChallenge($challenge['token'], $challenge['payload']); try { $check = $this->application->find('check'); $checkTest = new CommandTester($check); @@ -114,7 +110,7 @@ public function testFullProcess() $this->assertStringContainsString('The authorization check was successful', $checkDisplay); } finally { - $process->stop(); + $this->cleanChallenge($challenge['token']); } /* diff --git a/tests/Cli/Fixtures/challenges/.well-known/acme-challenge/.gitkeep b/tests/Cli/Fixtures/challenges/.well-known/acme-challenge/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Cli/Fixtures/local/backup/.gitignore b/tests/Cli/Fixtures/local/backup/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/Cli/Fixtures/local/backup/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Cli/Fixtures/local/backup/.gitkeep b/tests/Cli/Fixtures/local/backup/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Cli/Fixtures/local/master/.gitignore b/tests/Cli/Fixtures/local/master/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/Cli/Fixtures/local/master/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Cli/Fixtures/local/master/.gitkeep b/tests/Cli/Fixtures/local/master/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Cli/Fixtures/sftp/.gitignore b/tests/Cli/Fixtures/sftp/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/Cli/Fixtures/sftp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Cli/Fixtures/sftp/.gitkeep b/tests/Cli/Fixtures/sftp/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Cli/MonitoredApplicationTest.php b/tests/Cli/MonitoredApplicationTest.php index fc515d16..4ed65a55 100644 --- a/tests/Cli/MonitoredApplicationTest.php +++ b/tests/Cli/MonitoredApplicationTest.php @@ -33,7 +33,6 @@ class MonitoredApplicationTest extends AbstractApplicationTest protected function getFixturesDirectories() { return [ - __DIR__.'/../Cli/Fixtures/challenges/.well-known/acme-challenge', __DIR__.'/../Cli/Fixtures/local/backup', __DIR__.'/../Cli/Fixtures/local/master', ]; diff --git a/tests/Cli/SftpNginxProxyApplicationTest.php b/tests/Cli/SftpNginxProxyApplicationTest.php index d75b7720..513c250a 100644 --- a/tests/Cli/SftpNginxProxyApplicationTest.php +++ b/tests/Cli/SftpNginxProxyApplicationTest.php @@ -24,7 +24,6 @@ class SftpNginxProxyApplicationTest extends AbstractApplicationTest protected function getFixturesDirectories() { return [ - __DIR__.'/../Cli/Fixtures/challenges/.well-known/acme-challenge', __DIR__.'/../Cli/Fixtures/local/backup', __DIR__.'/../Cli/Fixtures/local/master', __DIR__.'/../Cli/Fixtures/sftp', diff --git a/tests/Cli/SimpleApplicationTest.php b/tests/Cli/SimpleApplicationTest.php index db021d9c..ca01697e 100644 --- a/tests/Cli/SimpleApplicationTest.php +++ b/tests/Cli/SimpleApplicationTest.php @@ -22,7 +22,6 @@ class SimpleApplicationTest extends AbstractApplicationTest protected function getFixturesDirectories() { return [ - __DIR__.'/../Cli/Fixtures/challenges/.well-known/acme-challenge', __DIR__.'/../Cli/Fixtures/local/master', ]; } diff --git a/tests/Core/AbstractFunctionnalTest.php b/tests/Core/AbstractFunctionnalTest.php index d12eab46..fce7c099 100644 --- a/tests/Core/AbstractFunctionnalTest.php +++ b/tests/Core/AbstractFunctionnalTest.php @@ -11,45 +11,25 @@ namespace Tests\AcmePhp\Core; +use GuzzleHttp\Client; +use GuzzleHttp\RequestOptions; use PHPUnit\Framework\TestCase; -use Symfony\Component\Process\PhpExecutableFinder; -use Symfony\Component\Process\Process; abstract class AbstractFunctionnalTest extends TestCase { - /** - * @param string $token - * @param string $payload - * - * @return Process - */ - protected function createServerProcess($token, $payload) + protected function handleChallenge($token, $payload) { - $listen = '0.0.0.0:5002'; - $documentRoot = __DIR__.'/Fixtures/challenges'; + $fakeServer = new Client(); + $response = $fakeServer->post('http://localhost:8055/add-http01', [RequestOptions::JSON => ['token' => $token, 'content' => $payload]]); - // Create file - file_put_contents($documentRoot.'/.well-known/acme-challenge/'.$token, $payload); - - // Start server - $finder = new PhpExecutableFinder(); - - if (false === $binary = $finder->find()) { - throw new \RuntimeException('Unable to find PHP binary to start server.'); - } - - $script = implode(' ', [ - '"'.$binary.'"', - '"-S"', - '"'.$listen.'"', - '"-t"', - '"'.$documentRoot.'"', - ]); + $this->assertSame(200, $response->getStatusCode()); + } - if (method_exists(Process::class, 'fromShellCommandline')) { - return Process::fromShellCommandline('exec '.$script, $documentRoot, null, null, null); - } + protected function cleanChallenge($token) + { + $fakeServer = new Client(); + $response = $fakeServer->post('http://localhost:8055/del-http01', [RequestOptions::JSON => ['token' => $token]]); - return new Process('exec '.$script, $documentRoot, null, null, null); + $this->assertSame(200, $response->getStatusCode()); } } diff --git a/tests/Core/AcmeClientTest.php b/tests/Core/AcmeClientTest.php index a27aab2e..8a0f6bcf 100644 --- a/tests/Core/AcmeClientTest.php +++ b/tests/Core/AcmeClientTest.php @@ -78,16 +78,12 @@ public function testFullProcess(KeyOption $keyOption) /* * Challenge check */ - $process = $this->createServerProcess($challenge->getToken(), $challenge->getPayload()); - $process->start(); - sleep(1); - $this->assertTrue($process->isRunning()); - + $this->handleChallenge($challenge->getToken(), $challenge->getPayload()); try { $check = $client->challengeAuthorization($challenge); $this->assertEquals('valid', $check['status']); } finally { - $process->stop(); + $this->cleanChallenge($challenge->getToken()); } /* diff --git a/tests/Core/Fixtures/challenges/.well-known/acme-challenge/.gitkeep b/tests/Core/Fixtures/challenges/.well-known/acme-challenge/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/Core/Http/SecureHttpClientTest.php b/tests/Core/Http/SecureHttpClientTest.php index 586dce7d..ffacfac4 100644 --- a/tests/Core/Http/SecureHttpClientTest.php +++ b/tests/Core/Http/SecureHttpClientTest.php @@ -58,6 +58,142 @@ private function createMockedClient(array $responses, $willThrow = false) ); } + public function testSignKidPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signKidPayload('/foo', 'account', ['foo' => 'bar']); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('protected', $payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertArrayHasKey('signature', $payload); + $this->assertSame('{"foo":"bar"}', \base64_decode($payload['payload'])); + } + + public function testSignKidPayloadWithEmptyPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signKidPayload('/foo', 'account', []); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertSame('{}', \base64_decode($payload['payload'])); + } + + public function testSignKidPayloadWithNullPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signKidPayload('/foo', 'account'); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertSame('', \base64_decode($payload['payload'])); + } + + public function testSignJwkPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signJwkPayload('/foo', ['foo' => 'bar']); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('protected', $payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertArrayHasKey('signature', $payload); + $this->assertSame('{"foo":"bar"}', \base64_decode($payload['payload'])); + } + + public function testSignJwkPayloadWithEmptyPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signJwkPayload('/foo', []); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertSame('{}', \base64_decode($payload['payload'])); + } + + public function testSignJwkPayloadWithNullPayload() + { + $client = $this->createMockedClient([]); + $payload = $client->signJwkPayload('/foo'); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertSame('', \base64_decode($payload['payload'])); + } + + public function testValidStringRequest() + { + $client = $this->createMockedClient([new Response(200, [], 'foo')], false); + $body = $client->request('GET', '/foo', ['foo' => 'bar'], false); + $this->assertEquals('foo', $body); + } + + public function testValidJsonRequest() + { + $client = $this->createMockedClient([new Response(200, [], json_encode(['test' => 'ok']))], false); + $data = $client->request('GET', '/foo', ['foo' => 'bar'], true); + $this->assertEquals(['test' => 'ok'], $data); + } + + public function testInvalidJsonRequest() + { + $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); + $client = $this->createMockedClient([new Response(200, [], 'invalid json')], false); + $client->request('GET', '/foo', ['foo' => 'bar'], true); + } + + /** + * @group legacy + */ + public function testRequestPayload() + { + $container = []; + + $stack = HandlerStack::create(new MockHandler([new Response(200, [], json_encode(['test' => 'ok']))])); + $stack->push(Middleware::history($container)); + + $keyPairGenerator = new KeyPairGenerator(); + + $dataSigner = $this->getMockBuilder(DataSigner::class)->getMock(); + $dataSigner->expects($this->once()) + ->method('signData') + ->willReturn('foobar'); + + $client = new SecureHttpClient( + $keyPairGenerator->generateKeyPair(), + new Client(['handler' => $stack]), + new Base64SafeEncoder(), + new KeyParser(), + $dataSigner, + $this->getMockBuilder(ServerErrorHandler::class)->getMock() + ); + + $client->request('POST', '/acme/new-reg', $client->signJwkPayload('/acme/new-reg', ['contact' => 'foo@bar.com']), true); + + // Check request object + $this->assertCount(1, $container); + + /** @var RequestInterface $request */ + $request = $container[0]['request']; + + $this->assertInstanceOf(RequestInterface::class, $request); + $this->assertEquals('POST', $request->getMethod()); + $this->assertEquals('/acme/new-reg', ($request->getUri() instanceof Uri) ? $request->getUri()->getPath() : $request->getUri()); + + $body = \GuzzleHttp\Psr7\copy_to_string($request->getBody()); + $payload = @json_decode($body, true); + + $this->assertIsArray($payload); + $this->assertArrayHasKey('protected', $payload); + $this->assertArrayHasKey('payload', $payload); + $this->assertArrayHasKey('signature', $payload); + $this->assertEquals('Zm9vYmFy', $payload['signature']); + } + + /** + * @group legacy + */ public function testValidUnsignedStringRequest() { $client = $this->createMockedClient([new Response(200, [], 'foo')], false); @@ -65,6 +201,9 @@ public function testValidUnsignedStringRequest() $this->assertEquals('foo', $body); } + /** + * @group legacy + */ public function testValidUnsignedJsonRequest() { $client = $this->createMockedClient([new Response(200, [], json_encode(['test' => 'ok']))], false); @@ -72,6 +211,9 @@ public function testValidUnsignedJsonRequest() $this->assertEquals(['test' => 'ok'], $data); } + /** + * @group legacy + */ public function testInvalidUnsignedJsonRequest() { $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); @@ -79,6 +221,9 @@ public function testInvalidUnsignedJsonRequest() $client->unsignedRequest('GET', '/foo', ['foo' => 'bar'], true); } + /** + * @group legacy + */ public function testValidSignedStringRequest() { $client = $this->createMockedClient([new Response(200, [], 'foo')], false); @@ -86,6 +231,9 @@ public function testValidSignedStringRequest() $this->assertEquals('foo', $body); } + /** + * @group legacy + */ public function testValidSignedJsonRequest() { $client = $this->createMockedClient([new Response(200, [], json_encode(['test' => 'ok']))], false); @@ -93,6 +241,9 @@ public function testValidSignedJsonRequest() $this->assertEquals(['test' => 'ok'], $data); } + /** + * @group legacy + */ public function testInvalidSignedJsonRequest() { $this->expectException('AcmePhp\Core\Exception\Protocol\ExpectedJsonException'); @@ -100,6 +251,9 @@ public function testInvalidSignedJsonRequest() $client->signedRequest('GET', '/foo', ['foo' => 'bar'], true); } + /** + * @group legacy + */ public function testSignedRequestPayload() { $container = []; diff --git a/tests/run.sh b/tests/run.sh index 5ec9d58b..d2c3ed8a 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -27,4 +27,4 @@ p9BI7gVKtWSZYegicA== echo "$cert" > "$tempfile" trap "{ rm -f $tempfile; }" EXIT -php -d curl.cainfo="$tempfile" ./vendor/bin/simple-phpunit $* +php -d curl.cainfo="$tempfile" ./vendor/bin/simple-phpunit "$@" diff --git a/tests/setup.sh b/tests/setup.sh index 71fb6870..5ed674aa 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -7,8 +7,9 @@ cd .. # SFTP docker run -d --name acme_sftp -p 8022:22 atmoz/sftp acmephp:acmephp:::share -# testing-ca -docker run -d --name acme_boulder --net host --add-host acmephp.com:127.0.0.1 acmephp/testing-ca:2.1 +# pebble +docker run -d --name acme_server --net host letsencrypt/pebble-challtestsrv pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 127.0.0.1 +docker run -d --name acme_pebble --net host -e PEBBLE_VA_NOSLEEP=1 -e PEBBLE_WFE_NONCEREJECT=0 letsencrypt/pebble pebble -dnsserver 127.0.0.1:8053 # Wait for boot to be completed -docker run --rm --net host martin/wait -c localhost:14000,localhost:8022 -t 120 +docker run --rm --net host martin/wait -c localhost:14000,localhost:8022,localhost:8053,localhost:5002 -t 120 diff --git a/tests/teardown.sh b/tests/teardown.sh index 1356a8e8..a08bca63 100755 --- a/tests/teardown.sh +++ b/tests/teardown.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -docker rm -fv acme_sftp acme_boulder +docker rm -fv acme_sftp acme_pebble acme_server From 028470a44075529bbf36877bd8a818a742a2699d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Nov=C3=BD?= Date: Mon, 16 Dec 2019 16:21:44 +0000 Subject: [PATCH 118/126] fix the wrong return type --- src/Core/Protocol/AuthorizationChallenge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index 887a5e2f..bddce280 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -124,7 +124,7 @@ public function getStatus() } /** - * @return string + * @return boolean */ public function isValid() { @@ -132,7 +132,7 @@ public function isValid() } /** - * @return string + * @return boolean */ public function isPending() { From ec1bbbaf8ea10717f8bea5a698fe1d14dca3ad18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Nov=C3=BD?= Date: Mon, 16 Dec 2019 16:25:21 +0000 Subject: [PATCH 119/126] fix the wrong return type --- src/Core/Protocol/AuthorizationChallenge.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Protocol/AuthorizationChallenge.php b/src/Core/Protocol/AuthorizationChallenge.php index bddce280..4a30decd 100644 --- a/src/Core/Protocol/AuthorizationChallenge.php +++ b/src/Core/Protocol/AuthorizationChallenge.php @@ -124,7 +124,7 @@ public function getStatus() } /** - * @return boolean + * @return bool */ public function isValid() { @@ -132,7 +132,7 @@ public function isValid() } /** - * @return boolean + * @return bool */ public function isPending() { From 24b857524424d7a0b765c453bf0e99eb20fac8ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Nov=C3=BD?= Date: Tue, 17 Dec 2019 14:19:42 +0000 Subject: [PATCH 120/126] Fix getIssuerCertificate return type --- src/Ssl/Certificate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ssl/Certificate.php b/src/Ssl/Certificate.php index 99269ef6..c7bbab55 100644 --- a/src/Ssl/Certificate.php +++ b/src/Ssl/Certificate.php @@ -64,7 +64,7 @@ public function getPEM() } /** - * @return Certificate + * @return Certificate|null */ public function getIssuerCertificate() { From b796512251634a738f0bc45aaa91606355f0f6b2 Mon Sep 17 00:00:00 2001 From: Titouan Galopin Date: Wed, 15 Jan 2020 22:42:55 +0100 Subject: [PATCH 121/126] Release of new version 1.2.0 --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++ src/Cli/Application.php | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e158f131..81f8bdf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +15/01/2020 22:42 1.2.0 Release version 1.2.0 +d031223 Merge pull request #185 from miranovy/master +24b8575 Fix getIssuerCertificate return type +df8f156 Merge pull request #183 from jderusse/split-request +b86d2d6 Merge pull request #184 from miranovy/master +ec1bbba fix the wrong return type +028470a fix the wrong return type +25a12e3 Split Sign and Request +937a2f7 Merge pull request #182 from pauladams8/master +c592b85 added orderNotReady error type +0d09084 Merge pull request #180 from jderusse/fix-test +f0e9422 Add php 7.3 and 7.4 +6b65f1a Merge pull request #164 from trustocean/feat-install-aliyun-cdn +d060f0c style ci, no spacing near dot +222f184 重名时阿里云会报错 +51da67e Merge branch 'master' of https://github.com/acmephp/acmephp into feat-install-aliyun-cdn +9203dec Merge pull request #163 from trustocean/feat-install-aliyun-waf +ce4bb92 style ci +45037e1 Merge branch 'master' of https://github.com/acmephp/acmephp into feat-install-aliyun-cdn +dcd1ad7 fixbug +bce7829 fixbug +3292b41 fix +2400302 remove wrong conf +0501e56 register in services +505d4c2 stash +0d522f7 Update InstallAliyunWafAction.php +91049e3 Remove try...catch wrapper +e9fefc3 stash +2d88030 Merge pull request #161 from jderusse/deprecate-commands +723de09 style ci +1a17f6e fix style ci +40558ea close #17 +05a2f39 fix +8541342 statsh +d865632 Merge pull request #162 from aik099/gandi-dns-solver-feat +83e47d3 Removed unused code +62d9b1b CS fixes +9eeb20a Adding Gandi.Net DNS solver class +8bae348 Deprecate commands in favor of run +9ef2916 Merge pull request #153 from elliotfehr/missed-memleak +c1271c0 free openssl resource after reading +b276743 Merge pull request #151 from elliotfehr/openssl-mem-leak +1effe3e Merge branch 'master' into openssl-mem-leak +58ee1e6 Merge pull request #152 from jderusse/fix-cs +2071f9d Fix CS +bb55db6 free the key resource after reading +8c9d313 Build 1.1.1 PHAR 18/01/2019 15:17 1.1.1 Several bug fixes 952b1a6 Merge pull request #148 from rokclimb15/patch-1 56df417 Correctly throw ChallengeTimedOutException diff --git a/src/Cli/Application.php b/src/Cli/Application.php index a057715a..8de32692 100644 --- a/src/Cli/Application.php +++ b/src/Cli/Application.php @@ -30,7 +30,7 @@ */ class Application extends BaseApplication { - const VERSION = '1.1.1'; + const VERSION = '1.2.0'; /** * {@inheritdoc} From c5de9ade206a1eeef7b6b8b9f559c7caeae8bfee Mon Sep 17 00:00:00 2001 From: "xiaohui.lam" Date: Sat, 18 Jan 2020 12:06:44 +0800 Subject: [PATCH 122/126] =?UTF-8?q?=E5=BD=93check=E4=B8=BAfalse=E6=97=B6?= =?UTF-8?q?=E5=80=99=EF=BC=8C=E8=B7=B3=E8=BF=87=E8=B4=B9=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Cli/Command/RunCommand.php | 8 +++++--- src/Cli/Configuration/DomainConfiguration.php | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Cli/Command/RunCommand.php b/src/Cli/Command/RunCommand.php index 9c288d91..1fa129d4 100644 --- a/src/Cli/Command/RunCommand.php +++ b/src/Cli/Command/RunCommand.php @@ -335,9 +335,11 @@ private function challengeDomains(array $domainConfig, KeyOption $keyOption, arr continue; } - $this->output->writeln(sprintf('Testing the challenge for domain %s...', $domain)); - if (time() - $startTestTime > $config_timeout || !$validator->isValid($authorizationChallenge)) { - $this->output->writeln(sprintf('Can not self validate challenge for domain %s. Maybe letsencrypt will be able to do it...', $domain)); + if (!isset($config['check']) || $config['check']) { + $this->output->writeln(sprintf('Testing the challenge for domain %s...', $domain)); + if (time() - $startTestTime > $config_timeout || !$validator->isValid($authorizationChallenge)) { + $this->output->writeln(sprintf('Can not self validate challenge for domain %s. Maybe letsencrypt will be able to do it...', $domain)); + } } $this->output->writeln(sprintf('Requesting authorization check for domain %s...', $domain)); diff --git a/src/Cli/Configuration/DomainConfiguration.php b/src/Cli/Configuration/DomainConfiguration.php index b20fce09..0fb16350 100644 --- a/src/Cli/Configuration/DomainConfiguration.php +++ b/src/Cli/Configuration/DomainConfiguration.php @@ -72,6 +72,10 @@ public function getConfigTreeBuilder() ->thenInvalid('The keyType %s is not valid. Supported types are: RSA, EC') ->end() ->end() + ->scalarNode('check') + ->info('Run self validate or not.') + ->defaultValue(true) + ->end() ->scalarNode('timeout') ->info('Timtout to challenge.') ->defaultValue('180') From edb3d2690fa7a3140f019bb2c0179028ac4fb499 Mon Sep 17 00:00:00 2001 From: Xiaohui Lam Date: Tue, 3 Mar 2020 13:08:50 +0800 Subject: [PATCH 123/126] Update composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0aae4aa5..1039a69a 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,6 @@ "ext-mbstring": "*", "ext-openssl": "*", "lib-openssl": ">=0.9.8", - "aws/aws-sdk-php": "^3.38", "daverandom/libdns": ">=1.0", "guzzlehttp/guzzle": "^6.4", "guzzlehttp/psr7": "^1.0", @@ -68,7 +67,8 @@ "digitalsign/jwt-core": "*" }, "suggest": { - "daverandom/libdns": "^2.0" + "daverandom/libdns": "^2.0", + "aws/aws-sdk-php": "^3.38" }, "require-dev": { "phpspec/prophecy": "^1.8", From 2a59ecca9c1e9d640d6b7e6c82e79b3d6eda4bf1 Mon Sep 17 00:00:00 2001 From: xiaohui lam Date: Tue, 17 Mar 2020 12:07:04 +0800 Subject: [PATCH 124/126] monolog/monolog ^1.19|^2.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1039a69a..3b8fc863 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "league/flysystem": "^1.0.19", "league/flysystem-memory": "^1.0", "league/flysystem-sftp": "^1.0.7", - "monolog/monolog": "^1.19", + "monolog/monolog": "^1.19|^2.0", "padraic/phar-updater": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0", From 0ba4c4dc13d739438f1ac926609f2b2c75403121 Mon Sep 17 00:00:00 2001 From: xiaohui lam Date: Tue, 17 Mar 2020 12:23:38 +0800 Subject: [PATCH 125/126] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/AcmeClient.php | 2 +- src/Core/Http/SecureHttpClient.php | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Core/AcmeClient.php b/src/Core/AcmeClient.php index 7dbf32c8..69f6c886 100644 --- a/src/Core/AcmeClient.php +++ b/src/Core/AcmeClient.php @@ -341,7 +341,7 @@ public function isCsrEager() { if (!$this->directory) { $this->directory = new ResourcesDirectory( - $this->getHttpClient()->unsignedRequest('GET', $this->directoryUrl, null, true) + $this->getHttpClient()->unsignedRequest('GET', $this->directoryUrl, [], true) ); } diff --git a/src/Core/Http/SecureHttpClient.php b/src/Core/Http/SecureHttpClient.php index c3635dbc..28bb92ff 100644 --- a/src/Core/Http/SecureHttpClient.php +++ b/src/Core/Http/SecureHttpClient.php @@ -156,6 +156,32 @@ private function getAlg() } } + private function extractSignOptionFromJWSAlg($alg) + { + if (!preg_match('/^([A-Z]+)(\d+)$/', $alg, $match)) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + if (!\defined('OPENSSL_ALGO_SHA'.$match[2])) { + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + $algorithm = \constant('OPENSSL_ALGO_SHA'.$match[2]); + + switch ($match[1]) { + case 'RS': + $format = DataSigner::FORMAT_DER; + break; + case 'ES': + $format = DataSigner::FORMAT_ECDSA; + break; + default: + throw new AcmeCoreClientException(sprintf('The given "%s" algorithm is not supported', $alg)); + } + + return [$algorithm, $format]; + } + public function getJWK() { $privateKey = $this->accountKeyPair->getPrivateKey(); From 486dbb6d0995bd1abb0ba11db2ec473391d2218e Mon Sep 17 00:00:00 2001 From: xiaohui lam Date: Tue, 17 Mar 2020 13:11:53 +0800 Subject: [PATCH 126/126] fixbug --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 76efd899..dd137b86 100644 --- a/composer.json +++ b/composer.json @@ -51,12 +51,12 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "swiftmailer/swiftmailer": "^5.4|^6.0", - "symfony/config": "^3.0|^4.0", - "symfony/console": "^3.0|^4.0", - "symfony/dependency-injection": "^3.3|^4.0", - "symfony/filesystem": "^3.0|^4.0", - "symfony/serializer": "^3.0|^4.0", - "symfony/yaml": "^3.0|^4.0", + "symfony/config": "^3.0|^4.0|^5.0", + "symfony/console": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^3.3|^4.0|^5.0", + "symfony/filesystem": "^3.0|^4.0|^5.0", + "symfony/serializer": "^3.0|^4.0|^5.0", + "symfony/yaml": "^3.0|^4.0|^5.0", "webmozart/assert": "^1.0", "webmozart/path-util": "^2.3", "alibabacloud/alidns": "^1.7",