Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0c09881ea | ||
|
|
07f9a03702 | ||
|
|
d04e758598 | ||
|
|
029f4c533a | ||
|
|
c02e758a21 | ||
|
|
e1ea62b5e7 | ||
|
|
a090e99a19 | ||
|
|
de3b82da1d | ||
|
|
791b250742 | ||
|
|
9f849691c2 | ||
|
|
c4d15e2e26 | ||
|
|
0722e104d4 | ||
|
|
583318fa0b | ||
|
|
3472bd1b3c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
/build/
|
||||
/data/
|
||||
/info/
|
||||
/vendor/
|
||||
/config.test.yml
|
||||
1
bin/acme
1
bin/acme
@@ -40,6 +40,7 @@ HELP;
|
||||
require __DIR__ . "/../vendor/autoload.php";
|
||||
|
||||
$commands = [
|
||||
"auto" => "Setup, issue and renew based on a single configuration file.",
|
||||
"setup" => "Setup and register account.",
|
||||
"issue" => "Issue a new certificate.",
|
||||
"check" => "Check if a certificate is still valid long enough.",
|
||||
|
||||
325
composer.lock
generated
325
composer.lock
generated
@@ -4,8 +4,8 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "c27b6e29c15798ac2cf219d7993e5771",
|
||||
"content-hash": "a0b266ee9280981bcacbfac39d0969eb",
|
||||
"hash": "f645228f022f95d362e3cfa543321bd7",
|
||||
"content-hash": "af2e73ef42c235311d53fcf4eb5aa5ab",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amphp/amp",
|
||||
@@ -342,16 +342,16 @@
|
||||
},
|
||||
{
|
||||
"name": "amphp/socket",
|
||||
"version": "v0.9.7",
|
||||
"version": "v0.9.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amphp/socket.git",
|
||||
"reference": "aa784fe3a18fa8514e57714142488f629073c7a9"
|
||||
"reference": "bdc72fc545e6dcee444419d98eea80875bffc806"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/amphp/socket/zipball/aa784fe3a18fa8514e57714142488f629073c7a9",
|
||||
"reference": "aa784fe3a18fa8514e57714142488f629073c7a9",
|
||||
"url": "https://api.github.com/repos/amphp/socket/zipball/bdc72fc545e6dcee444419d98eea80875bffc806",
|
||||
"reference": "bdc72fc545e6dcee444419d98eea80875bffc806",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -393,7 +393,7 @@
|
||||
"tcp",
|
||||
"tls"
|
||||
],
|
||||
"time": "2016-04-06 16:38:57"
|
||||
"time": "2016-06-19 20:16:19"
|
||||
},
|
||||
{
|
||||
"name": "daverandom/libdns",
|
||||
@@ -647,16 +647,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "ba6fb78f727cd09f2a649113b95468019e490585"
|
||||
"reference": "3d265f7c079f5b37d33475f996d7a383c5fc8aeb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ba6fb78f727cd09f2a649113b95468019e490585",
|
||||
"reference": "ba6fb78f727cd09f2a649113b95468019e490585",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/3d265f7c079f5b37d33475f996d7a383c5fc8aeb",
|
||||
"reference": "3d265f7c079f5b37d33475f996d7a383c5fc8aeb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -676,6 +676,9 @@
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib\\": "phpseclib/"
|
||||
}
|
||||
@@ -732,7 +735,7 @@
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"time": "2016-01-18 17:07:21"
|
||||
"time": "2016-05-13 01:15:21"
|
||||
},
|
||||
{
|
||||
"name": "rdlowrey/auryn",
|
||||
@@ -1004,16 +1007,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "eca51b7b65eb9be6af88ad7cc91685f1556f5c9a"
|
||||
"reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca51b7b65eb9be6af88ad7cc91685f1556f5c9a",
|
||||
"reference": "eca51b7b65eb9be6af88ad7cc91685f1556f5c9a",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/c5a7e7fc273c758b92b85dcb9c46149ccda89623",
|
||||
"reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1049,7 +1052,7 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-05-26 21:46:24"
|
||||
"time": "2016-06-14 11:18:07"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
@@ -1158,16 +1161,16 @@
|
||||
},
|
||||
{
|
||||
"name": "fabpot/php-cs-fixer",
|
||||
"version": "v1.11.3",
|
||||
"version": "v1.11.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
|
||||
"reference": "b0a383d856d884d6b16e15892f507ecf89f8dbd2"
|
||||
"reference": "eeb280e909834603ffe03524dbe0066e77c83084"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b0a383d856d884d6b16e15892f507ecf89f8dbd2",
|
||||
"reference": "b0a383d856d884d6b16e15892f507ecf89f8dbd2",
|
||||
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/eeb280e909834603ffe03524dbe0066e77c83084",
|
||||
"reference": "eeb280e909834603ffe03524dbe0066e77c83084",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1209,7 +1212,7 @@
|
||||
],
|
||||
"description": "A tool to automatically fix PHP code style",
|
||||
"abandoned": "friendsofphp/php-cs-fixer",
|
||||
"time": "2016-05-26 23:49:24"
|
||||
"time": "2016-06-07 07:51:27"
|
||||
},
|
||||
{
|
||||
"name": "league/event",
|
||||
@@ -1465,38 +1468,87 @@
|
||||
"time": "2014-01-15 17:24:13"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "2.0.4",
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
|
||||
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
|
||||
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
|
||||
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
|
||||
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"dflydev/markdown": "~1.0",
|
||||
"erusev/parsedown": "~1.0"
|
||||
"phpunit/phpunit": "^4.6"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"phpDocumentor": [
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jaap van Otterdijk",
|
||||
"email": "opensource@ijaap.nl"
|
||||
}
|
||||
],
|
||||
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
|
||||
"homepage": "http://www.phpdoc.org",
|
||||
"keywords": [
|
||||
"FQSEN",
|
||||
"phpDocumentor",
|
||||
"phpdoc",
|
||||
"reflection",
|
||||
"static analysis"
|
||||
],
|
||||
"time": "2015-12-27 11:43:31"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "9270140b940ff02e58ec577c237274e92cd40cdd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd",
|
||||
"reference": "9270140b940ff02e58ec577c237274e92cd40cdd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"phpdocumentor/reflection-common": "^1.0@dev",
|
||||
"phpdocumentor/type-resolver": "^0.2.0",
|
||||
"webmozart/assert": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.4",
|
||||
"phpunit/phpunit": "^4.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
@@ -1508,39 +1560,87 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike van Riel",
|
||||
"email": "mike.vanriel@naenius.com"
|
||||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"time": "2015-02-03 12:10:50"
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"time": "2016-06-10 09:48:41"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.6.0",
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972"
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443",
|
||||
"reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"phpdocumentor/reflection-common": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.4",
|
||||
"phpunit/phpunit": "^5.2||^4.8.24"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike van Riel",
|
||||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"time": "2016-06-10 07:14:17"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "v1.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "58a8137754bc24b25740d4281399a4a3596058e0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0",
|
||||
"reference": "58a8137754bc24b25740d4281399a4a3596058e0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": "^5.3|^7.0",
|
||||
"phpdocumentor/reflection-docblock": "~2.0",
|
||||
"sebastian/comparator": "~1.1",
|
||||
"sebastian/recursion-context": "~1.0"
|
||||
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
|
||||
"sebastian/comparator": "^1.1",
|
||||
"sebastian/recursion-context": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "~2.0"
|
||||
"phpspec/phpspec": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.5.x-dev"
|
||||
"dev-master": "1.6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1573,20 +1673,20 @@
|
||||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"time": "2016-02-15 07:46:21"
|
||||
"time": "2016-06-07 08:13:47"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "3.3.3",
|
||||
"version": "4.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "44cd8e3930e431658d1a5de7d282d5cb37837fd5"
|
||||
"reference": "900370c81280cc0d942ffbc5912d80464eaee7e9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/44cd8e3930e431658d1a5de7d282d5cb37837fd5",
|
||||
"reference": "44cd8e3930e431658d1a5de7d282d5cb37837fd5",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/900370c81280cc0d942ffbc5912d80464eaee7e9",
|
||||
"reference": "900370c81280cc0d942ffbc5912d80464eaee7e9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1600,7 +1700,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-xdebug": ">=2.1.4",
|
||||
"phpunit/phpunit": "~5"
|
||||
"phpunit/phpunit": "^5.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "*",
|
||||
@@ -1610,7 +1710,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3.x-dev"
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1636,7 +1736,7 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2016-05-27 16:24:29"
|
||||
"time": "2016-06-03 05:03:56"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -1821,16 +1921,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "5.3.4",
|
||||
"version": "5.4.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "00dd95ffb48805503817ced06399017df315fe5c"
|
||||
"reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/00dd95ffb48805503817ced06399017df315fe5c",
|
||||
"reference": "00dd95ffb48805503817ced06399017df315fe5c",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
|
||||
"reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1842,14 +1942,14 @@
|
||||
"myclabs/deep-copy": "~1.3",
|
||||
"php": "^5.6 || ^7.0",
|
||||
"phpspec/prophecy": "^1.3.1",
|
||||
"phpunit/php-code-coverage": "^3.3.0",
|
||||
"phpunit/php-code-coverage": "^4.0",
|
||||
"phpunit/php-file-iterator": "~1.4",
|
||||
"phpunit/php-text-template": "~1.2",
|
||||
"phpunit/php-timer": "^1.0.6",
|
||||
"phpunit/phpunit-mock-objects": "^3.1",
|
||||
"phpunit/phpunit-mock-objects": "^3.2",
|
||||
"sebastian/comparator": "~1.1",
|
||||
"sebastian/diff": "~1.2",
|
||||
"sebastian/environment": "~1.3",
|
||||
"sebastian/environment": "^1.3 || ^2.0",
|
||||
"sebastian/exporter": "~1.2",
|
||||
"sebastian/global-state": "~1.0",
|
||||
"sebastian/object-enumerator": "~1.0",
|
||||
@@ -1857,6 +1957,9 @@
|
||||
"sebastian/version": "~1.0|~2.0",
|
||||
"symfony/yaml": "~2.1|~3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpdocumentor/reflection-docblock": "3.0.2"
|
||||
},
|
||||
"suggest": {
|
||||
"phpunit/php-invoker": "~1.1"
|
||||
},
|
||||
@@ -1866,7 +1969,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.3.x-dev"
|
||||
"dev-master": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1892,30 +1995,33 @@
|
||||
"testing",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2016-05-11 13:28:45"
|
||||
"time": "2016-06-16 06:01:15"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit-mock-objects",
|
||||
"version": "3.1.3",
|
||||
"version": "3.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
|
||||
"reference": "151c96874bff6fe61a25039df60e776613a61489"
|
||||
"reference": "b13d0d9426ced06958bd32104653526a6c998a52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/151c96874bff6fe61a25039df60e776613a61489",
|
||||
"reference": "151c96874bff6fe61a25039df60e776613a61489",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/b13d0d9426ced06958bd32104653526a6c998a52",
|
||||
"reference": "b13d0d9426ced06958bd32104653526a6c998a52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/instantiator": "^1.0.2",
|
||||
"php": ">=5.6",
|
||||
"phpunit/php-text-template": "~1.2",
|
||||
"sebastian/exporter": "~1.2"
|
||||
"php": "^5.6 || ^7.0",
|
||||
"phpunit/php-text-template": "^1.2",
|
||||
"sebastian/exporter": "^1.2"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5"
|
||||
"phpunit/phpunit": "^5.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-soap": "*"
|
||||
@@ -1923,7 +2029,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1.x-dev"
|
||||
"dev-master": "3.2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1948,7 +2054,7 @@
|
||||
"mock",
|
||||
"xunit"
|
||||
],
|
||||
"time": "2016-04-20 14:39:26"
|
||||
"time": "2016-06-12 07:37:26"
|
||||
},
|
||||
{
|
||||
"name": "rych/bytesize",
|
||||
@@ -2208,16 +2314,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "7ae5513327cb536431847bcc0c10edba2701064e"
|
||||
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
|
||||
"reference": "7ae5513327cb536431847bcc0c10edba2701064e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
|
||||
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2225,12 +2331,13 @@
|
||||
"sebastian/recursion-context": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-mbstring": "*",
|
||||
"phpunit/phpunit": "~4.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "1.3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2270,7 +2377,7 @@
|
||||
"export",
|
||||
"exporter"
|
||||
],
|
||||
"time": "2015-06-21 07:55:53"
|
||||
"time": "2016-06-17 09:04:28"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
@@ -2509,16 +2616,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.8.6",
|
||||
"version": "v2.8.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609"
|
||||
"reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/48221d3de4dc22d2cd57c97e8b9361821da86609",
|
||||
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
|
||||
"reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2565,20 +2672,20 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-04-26 12:00:47"
|
||||
"time": "2016-06-06 15:06:25"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "0343b2cedd0edb26cdc791212a8eb645c406018b"
|
||||
"reference": "f5b7563f67779c6d3d5370e23448e707c858df3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0343b2cedd0edb26cdc791212a8eb645c406018b",
|
||||
"reference": "0343b2cedd0edb26cdc791212a8eb645c406018b",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f5b7563f67779c6d3d5370e23448e707c858df3e",
|
||||
"reference": "f5b7563f67779c6d3d5370e23448e707c858df3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2625,11 +2732,11 @@
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-04-12 18:27:47"
|
||||
"time": "2016-06-06 11:42:41"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
@@ -2678,16 +2785,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v2.8.6",
|
||||
"version": "v2.8.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "ca24cf2cd4e3826f571e0067e535758e73807aa1"
|
||||
"reference": "3ec095fab1800222732ca522a95dce8fa124007b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/ca24cf2cd4e3826f571e0067e535758e73807aa1",
|
||||
"reference": "ca24cf2cd4e3826f571e0067e535758e73807aa1",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/3ec095fab1800222732ca522a95dce8fa124007b",
|
||||
"reference": "3ec095fab1800222732ca522a95dce8fa124007b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2723,7 +2830,7 @@
|
||||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-03-10 10:53:53"
|
||||
"time": "2016-06-06 11:11:27"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
@@ -2786,16 +2893,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "1574f3451b40fa9bbae518ef71d19a56f409cac0"
|
||||
"reference": "6350e63ed9c232da50e00f00a7e0330f066387a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/1574f3451b40fa9bbae518ef71d19a56f409cac0",
|
||||
"reference": "1574f3451b40fa9bbae518ef71d19a56f409cac0",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/6350e63ed9c232da50e00f00a7e0330f066387a2",
|
||||
"reference": "6350e63ed9c232da50e00f00a7e0330f066387a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2831,20 +2938,20 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-04-12 19:11:33"
|
||||
"time": "2016-06-06 11:42:41"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
"version": "v3.1.0",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/stopwatch.git",
|
||||
"reference": "4670f122fa32a4900003a6802f6b8575f3f0b17e"
|
||||
"reference": "e7238f98c90b99e9b53f3674a91757228663b04d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/4670f122fa32a4900003a6802f6b8575f3f0b17e",
|
||||
"reference": "4670f122fa32a4900003a6802f6b8575f3f0b17e",
|
||||
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/e7238f98c90b99e9b53f3674a91757228663b04d",
|
||||
"reference": "e7238f98c90b99e9b53f3674a91757228663b04d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2880,7 +2987,7 @@
|
||||
],
|
||||
"description": "Symfony Stopwatch Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-03-04 07:56:56"
|
||||
"time": "2016-06-06 11:42:41"
|
||||
},
|
||||
{
|
||||
"name": "webignition/readable-duration",
|
||||
|
||||
70
doc/advanced-usage.md
Normal file
70
doc/advanced-usage.md
Normal file
@@ -0,0 +1,70 @@
|
||||
## Advanced Usage
|
||||
|
||||
Please read the document about [basic usage](./usage.md) first.
|
||||
|
||||
## Register an Account
|
||||
|
||||
```
|
||||
acme-client setup --email me@example.com
|
||||
```
|
||||
|
||||
After a successful registration you're able to issue certificates.
|
||||
This client assumes you have a HTTP server setup and running.
|
||||
You must have a document root setup in order to use this client.
|
||||
|
||||
## Issue a Certificate
|
||||
|
||||
```
|
||||
acme-client issue -d example.com:www.example.com -p /var/www/example.com
|
||||
```
|
||||
|
||||
You can separate multiple domains (`-d`) with `,`, `:` or `;`. You can separate multiple document roots (`-p`) with your system's path separator:
|
||||
* Colon (`:`) for Unix
|
||||
* Semicolon (`;`) for Windows
|
||||
|
||||
If you specify less paths than domains, the last one will be used for the remaining domains.
|
||||
|
||||
Please note that Let's Encrypt has rate limits. Currently it's five certificates per domain per seven days. If you combine multiple subdomains in a single certificate, they count as just one certificate. If you just want to test things out, you can use their staging server, which has way higher rate limits by appending `--s letsencrypt:staging`.
|
||||
|
||||
## Revoke a Certificate
|
||||
|
||||
To revoke a certificate, you need a valid account key, just like for issuance.
|
||||
|
||||
```
|
||||
acme-client revoke --name example.com
|
||||
```
|
||||
|
||||
`--name` is the common name of the certificate that you want to revoke.
|
||||
|
||||
## Renew a Certificate
|
||||
|
||||
For renewal, there's the `acme-client check` subcommand.
|
||||
It exists with a non-zero exit code, if the certificate is going to expire soon.
|
||||
Default check time is 30 days, but you can use `--ttl` to customize it.
|
||||
|
||||
You may use this as daily cron:
|
||||
|
||||
```
|
||||
acme-client check --name example.com || acme-client issue ...
|
||||
```
|
||||
|
||||
You can also use a more advanced script to automatically reload the server as well. For this example we assume you're using Nginx.
|
||||
Something similar should work for Apache. But usually you shouldn't need any script, see [basic usage](./usage.md).
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
acme-client check --name example.com --ttl 30
|
||||
|
||||
if [ $? -eq 1 ]; then
|
||||
acme-client issue -d example.com:www.example.com -p /var/www
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
nginx -t -q
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
nginx -s reload
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
```
|
||||
142
doc/usage.md
142
doc/usage.md
@@ -1,79 +1,95 @@
|
||||
# Usage
|
||||
# Basic Usage
|
||||
|
||||
**The client stores all data in `./data` if you're using the Composer installation method, otherwise in the directory you configured. Be sure to backup this folder regularly. It contains your account keys, domain keys and certificates.**
|
||||
The client stores your account keys, domain keys and certificates in a single directory. If you're using the PHAR,
|
||||
you usually configure the storage in the configuration file. If you're using it with Composer, all data is stored in `./data`.
|
||||
|
||||
Before you can issue certificates, you have to register an account first and read and understand the terms of service of the ACME CA you're using.
|
||||
For the Let's Encrypt certificate authority, there's a [subscriber agreement](https://letsencrypt.org/repository/) you have to accept.
|
||||
**Be sure to backup that directory regularly.**
|
||||
|
||||
By using this client you agree to any agreement and any further updates by continued usage.
|
||||
You're responsible to react to updates and stop the automation if you no longer agree with the terms of service.
|
||||
Before you can issue certificates, you have to register an account. You have to read and understand the terms of service
|
||||
of the certificate authority you're using. For the Let's Encrypt certificate authority, there's a
|
||||
[subscriber agreement](https://letsencrypt.org/repository/) you have to accept.
|
||||
|
||||
These usage instructions assume you have installed the client globally as a Phar. If you are using the Phar, but don't have it globally, replace `acme-client` with the location to your Phar.
|
||||
By using this client you agree to any agreement and any further updates by continued usage. You're responsible to react
|
||||
to updates and stop the automation if you no longer agree with the terms of service.
|
||||
|
||||
If you're using the client with Composer, replace `acme-client` with `bin/acme`. You have to specify the server with `-s` / `--server`, because there's currently no config file support for this installation method.
|
||||
These usage instructions assume you have installed the client globally as a PHAR. If you are using the PHAR,
|
||||
but don't have it globally, replace `acme-client` with the location to your PHAR or add that path to your `$PATH` variable.
|
||||
|
||||
## Register an Account
|
||||
## Configuration
|
||||
|
||||
```
|
||||
acme-client setup --email me@example.com
|
||||
The client can be configured using a (global) configuration file. The client takes the first available of
|
||||
`./acme-client.yml` (if running as PHAR), `$HOME/.acme-client.yml`, `/etc/acme-client.yml` (if not on Windows).
|
||||
|
||||
The configuration file has the following format:
|
||||
|
||||
```yml
|
||||
# Storage directory for certificates and keys.
|
||||
storage: /etc/acme
|
||||
|
||||
# Server to use. URL to the ACME directory.
|
||||
# "letsencrypt" and "letsencrypt:staging" are valid shortcuts.
|
||||
server: letsencrypt
|
||||
|
||||
# E-mail to use for the setup.
|
||||
# This e-mail will receive expiration notices from Let's Encrypt.
|
||||
email: me@example.com
|
||||
|
||||
# List of certificates to issue.
|
||||
certificates:
|
||||
# For each certificate, there are a few options.
|
||||
#
|
||||
# Required: paths
|
||||
# Optional: bits, user
|
||||
#
|
||||
# paths: Map of document roots to domains.
|
||||
# /tmp is used here for domains without a real document root.
|
||||
# The client will place a file into $path/.well-known/acme-challenge/
|
||||
# to verify ownership to the CA
|
||||
#
|
||||
# bits: Number of bits for the domain private key
|
||||
#
|
||||
# user: User running the web server. Challenge files are world readable,
|
||||
# but some servers might require to be owner of files they serve.
|
||||
#
|
||||
- bits: 4096
|
||||
paths:
|
||||
/tmp:
|
||||
- docs.example.org
|
||||
- git.example.org
|
||||
# You can have multiple certificate with different users and key options.
|
||||
- user: www-data
|
||||
paths:
|
||||
/var/www: example.org
|
||||
```
|
||||
|
||||
After a successful registration you're able to issue certificates.
|
||||
This client assumes you have a HTTP server setup and running.
|
||||
You must have a document root setup in order to use this client.
|
||||
All configuration keys are optional and can be passed as arguments directly (except for `certificates` when using `acme-client auto`).
|
||||
|
||||
## Issue a Certificate
|
||||
## Certificate Issuance
|
||||
|
||||
```
|
||||
acme-client issue -d example.com:www.example.com -p /var/www/example.com
|
||||
```
|
||||
You can use `acme-client auto` to issue certificates and renew them if necessary. It uses the configuration file to
|
||||
determine the certificates to request. It will store certificates in the configured storage in a sub directory called `./certs`.
|
||||
|
||||
You can separate multiple domains (`-d`) with `,`, `:` or `;`. You can separate multiple document roots (`-p`) with your system's path separator:
|
||||
* Colon (`:`) for Unix
|
||||
* Semicolon (`;`) for Windows
|
||||
If everything has been successful, you'll see a message for each issued certificate. If nothing has to be renewed,
|
||||
the script will be quiet to be cron friendly. If an error occurs, the script will dump all available information.
|
||||
|
||||
If you specify less paths than domains, the last one will be used for the remaining domains.
|
||||
|
||||
Please note that Let's Encrypt has rate limits. Currently it's five certificates per domain per seven days. If you combine multiple subdomains in a single certificate, they count as just one certificate. If you just want to test things out, you can use their staging server, which has way higher rate limits by appending `--s letsencrypt:staging`.
|
||||
|
||||
## Revoke a Certificate
|
||||
|
||||
To revoke a certificate, you need a valid account key, just like for issuance.
|
||||
|
||||
```
|
||||
acme-client revoke --name example.com
|
||||
```
|
||||
|
||||
`--name` is the common name of the certificate that you want to revoke.
|
||||
|
||||
## Renewing a Certificate
|
||||
|
||||
For renewal, there's the `acme-client check` subcommand.
|
||||
It exists with a non-zero exit code, if the certificate is going to expire soon.
|
||||
Default check time is 30 days, but you can use `--ttl` to customize it.
|
||||
|
||||
You may use this as daily cron:
|
||||
|
||||
```
|
||||
acme-client check --name example.com || acme-client issue ...
|
||||
```
|
||||
|
||||
You can also use a more advanced script to automatically reload the server as well. For this example we assume you're using Nginx. Something similar should work for Apache.
|
||||
You should execute `acme-client auto` as a daily cron. It's recommended to setup e-mail notifications for all output of
|
||||
that script.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
acme-client check --name example.com --ttl 30
|
||||
|
||||
if [ $? -eq 1 ]; then
|
||||
acme-client issue -d example.com:www.example.com -p /var/www
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
nginx -t -q
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
nginx -s reload
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
0 0 * * * acme-client auto; exit=$?; if [[ $exit = 4 ]] || [[ $exit = 5 ]]; then service nginx reload; fi
|
||||
```
|
||||
|
||||
| Exit Code | Description |
|
||||
|-----------|-------------|
|
||||
| 0 | Nothing to do, all certificates still valid. |
|
||||
| 1 | Config file invalid. |
|
||||
| 2 | Issue during account setup. |
|
||||
| 3 | Error during issuance. |
|
||||
| 4 | Error during issuance, but some certificates could be renewed. |
|
||||
| 5 | Everything fine, new certificates have been issued. |
|
||||
|
||||
Exit codes `4` and `5` usually need a server reload, to reload the new certificates. It's already handled in the recommended
|
||||
cron setup.
|
||||
|
||||
If you want a more fine grained control or revoke certificates, you can have a look at the [advanced usage](./advanced-usage.md) document. The client allows to handle setup / issuance / revocation and other commands
|
||||
separately from `acme-client auto`.
|
||||
|
||||
300
src/Commands/Auto.php
Normal file
300
src/Commands/Auto.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace Kelunik\AcmeClient\Commands;
|
||||
|
||||
use Amp\CoroutineResult;
|
||||
use Amp\File\FilesystemException;
|
||||
use Amp\Process;
|
||||
use Kelunik\Acme\AcmeException;
|
||||
use Kelunik\AcmeClient\ConfigException;
|
||||
use League\CLImate\Argument\Manager;
|
||||
use League\CLImate\CLImate;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Auto implements Command {
|
||||
const EXIT_CONFIG_ERROR = 1;
|
||||
const EXIT_SETUP_ERROR = 2;
|
||||
const EXIT_ISSUANCE_ERROR = 3;
|
||||
const EXIT_ISSUANCE_PARTIAL = 4;
|
||||
const EXIT_ISSUANCE_OK = 5;
|
||||
|
||||
const STATUS_NO_CHANGE = 0;
|
||||
const STATUS_RENEWED = 1;
|
||||
|
||||
private $climate;
|
||||
|
||||
public function __construct(CLImate $climate) {
|
||||
$this->climate = $climate;
|
||||
}
|
||||
|
||||
public function execute(Manager $args) {
|
||||
return \Amp\resolve($this->doExecute($args));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Manager $args
|
||||
* @return \Generator
|
||||
*/
|
||||
private function doExecute(Manager $args) {
|
||||
$server = $args->get("server");
|
||||
$storage = $args->get("storage");
|
||||
$configPath = $args->get("config");
|
||||
|
||||
try {
|
||||
$config = Yaml::parse(
|
||||
yield \Amp\File\get($configPath)
|
||||
);
|
||||
} catch (FilesystemException $e) {
|
||||
$this->climate->error("Config file ({$configPath}) not found.");
|
||||
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||
return;
|
||||
} catch (ParseException $e) {
|
||||
$this->climate->error("Config file ({$configPath}) had an invalid format and couldn't be parsed.");
|
||||
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($config["email"])) {
|
||||
$this->climate->error("Config file ({$configPath}) didn't have a 'email' set.");
|
||||
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($config["certificates"]) || !is_array($config["certificates"])) {
|
||||
$this->climate->error("Config file ({$configPath}) didn't have a 'certificates' section that's an array.");
|
||||
yield new CoroutineResult(self::EXIT_CONFIG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$command = implode(" ", array_map("escapeshellarg", [
|
||||
PHP_BINARY,
|
||||
$GLOBALS["argv"][0],
|
||||
"setup",
|
||||
"--server",
|
||||
$server,
|
||||
"--storage",
|
||||
$storage,
|
||||
"--email",
|
||||
$config["email"],
|
||||
]));
|
||||
|
||||
$process = new Process($command);
|
||||
$result = (yield $process->exec(Process::BUFFER_ALL));
|
||||
|
||||
if ($result->exit !== 0) {
|
||||
$this->climate->error("Registration failed ({$result->exit})");
|
||||
$this->climate->error($command);
|
||||
$this->climate->br()->out($result->stdout);
|
||||
$this->climate->br()->error($result->stderr);
|
||||
yield new CoroutineResult(self::EXIT_SETUP_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$certificateChunks = array_chunk($config["certificates"], 10, true);
|
||||
|
||||
$errors = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($certificateChunks as $certificateChunk) {
|
||||
$promises = [];
|
||||
|
||||
foreach ($certificateChunk as $certificate) {
|
||||
$promises[] = \Amp\resolve($this->checkAndIssue($certificate, $server, $storage));
|
||||
}
|
||||
|
||||
list($chunkErrors, $chunkValues) = (yield \Amp\any($promises));
|
||||
|
||||
$errors += $chunkErrors;
|
||||
$values += $chunkValues;
|
||||
}
|
||||
|
||||
$status = [
|
||||
"no_change" => count(array_filter($values, function($value) { return $value === self::STATUS_NO_CHANGE; })),
|
||||
"renewed" => count(array_filter($values, function($value) { return $value === self::STATUS_RENEWED; })),
|
||||
"failure" => count($errors),
|
||||
];
|
||||
|
||||
if ($status["renewed"] > 0) {
|
||||
foreach ($values as $i => $value) {
|
||||
if ($value === self::STATUS_RENEWED) {
|
||||
$certificate = $config["certificates"][$i];
|
||||
$this->climate->info("Certificate for " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"]))) . " successfully renewed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($status["failure"] > 0) {
|
||||
foreach ($errors as $i => $error) {
|
||||
$certificate = $config["certificates"][$i];
|
||||
$this->climate->error("Issuance for the following domains failed: " . implode(", ", array_keys($this->toDomainPathMap($certificate["paths"]))));
|
||||
$this->climate->error("Reason: {$error}");
|
||||
}
|
||||
|
||||
$exitCode = $status["renewed"] > 0
|
||||
? self::EXIT_ISSUANCE_PARTIAL
|
||||
: self::EXIT_ISSUANCE_ERROR;
|
||||
|
||||
yield new CoroutineResult($exitCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($status["renewed"] > 0) {
|
||||
yield new CoroutineResult(self::EXIT_ISSUANCE_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $certificate certificate configuration
|
||||
* @param string $server server to use for issuance
|
||||
* @param string $storage storage directory
|
||||
* @return \Generator
|
||||
* @throws AcmeException if something does wrong
|
||||
*/
|
||||
private function checkAndIssue(array $certificate, $server, $storage) {
|
||||
$domainPathMap = $this->toDomainPathMap($certificate["paths"]);
|
||||
$domains = array_keys($domainPathMap);
|
||||
$commonName = reset($domains);
|
||||
|
||||
$args = [
|
||||
PHP_BINARY,
|
||||
$GLOBALS["argv"][0],
|
||||
"check",
|
||||
"--server",
|
||||
$server,
|
||||
"--storage",
|
||||
$storage,
|
||||
"--name",
|
||||
$commonName,
|
||||
];
|
||||
|
||||
$command = implode(" ", array_map("escapeshellarg", $args));
|
||||
|
||||
$process = new Process($command);
|
||||
$result = (yield $process->exec(Process::BUFFER_ALL));
|
||||
|
||||
if ($result->exit === 0) {
|
||||
// No need for renewal
|
||||
yield new CoroutineResult(self::STATUS_NO_CHANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($result->exit === 1) {
|
||||
// Renew certificate
|
||||
$args = [
|
||||
PHP_BINARY,
|
||||
$GLOBALS["argv"][0],
|
||||
"issue",
|
||||
"--server",
|
||||
$server,
|
||||
"--storage",
|
||||
$storage,
|
||||
"--domains",
|
||||
implode(",", $domains),
|
||||
"--path",
|
||||
implode(PATH_SEPARATOR, array_values($domainPathMap)),
|
||||
];
|
||||
|
||||
if (isset($certificate["user"])) {
|
||||
$args[] = "--user";
|
||||
$args[] = $certificate["user"];
|
||||
}
|
||||
|
||||
if (isset($certificate["bits"])) {
|
||||
$args[] = "--bits";
|
||||
$args[] = $certificate["bits"];
|
||||
}
|
||||
|
||||
$command = implode(" ", array_map("escapeshellarg", $args));
|
||||
|
||||
$process = new Process($command);
|
||||
$result = (yield $process->exec(Process::BUFFER_ALL));
|
||||
|
||||
if ($result->exit !== 0) {
|
||||
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'." . PHP_EOL . $result->stdout . PHP_EOL . PHP_EOL . $result->stderr);
|
||||
}
|
||||
|
||||
yield new CoroutineResult(self::STATUS_RENEWED);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new AcmeException("Unexpected exit code ({$result->exit}) for '{$command}'." . PHP_EOL . $result->stdout . PHP_EOL . PHP_EOL . $result->stderr);
|
||||
}
|
||||
|
||||
private function toDomainPathMap(array $paths) {
|
||||
$result = [];
|
||||
|
||||
foreach ($paths as $path => $domains) {
|
||||
if (is_numeric($path)) {
|
||||
$message = <<<MESSAGE
|
||||
Your configuration has the wrong format. Received a numeric value as path name.
|
||||
|
||||
This is most probably due to your "paths" value not being a map but a list instead.
|
||||
|
||||
If your configuration looks like this:
|
||||
|
||||
certificates:
|
||||
- paths:
|
||||
- /www/a: a.example.org
|
||||
- /www/b: b.example.org
|
||||
|
||||
Rewrite it to the following format for a single certificate:
|
||||
|
||||
certificates:
|
||||
- paths:
|
||||
/www/a: a.example.org
|
||||
/www/b: b.example.org
|
||||
|
||||
Rewrite it to the following format for two separate certificates:
|
||||
|
||||
certificates:
|
||||
- paths:
|
||||
/www/a: a.example.org
|
||||
- paths:
|
||||
/www/b: b.example.org
|
||||
|
||||
Documentation is available at https://github.com/kelunik/acme-client/blob/master/doc/usage.md#configuration
|
||||
|
||||
If this doesn't solve your issue, please reply to the following issue: https://github.com/kelunik/acme-client/issues/30
|
||||
MESSAGE;
|
||||
|
||||
throw new ConfigException($message);
|
||||
}
|
||||
|
||||
$domains = (array) $domains;
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
if (isset($result[$domain])) {
|
||||
throw new ConfigException("Duplicate domain: {$domain}");
|
||||
}
|
||||
|
||||
$result[$domain] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getDefinition() {
|
||||
$args = [
|
||||
"server" => \Kelunik\AcmeClient\getArgumentDescription("server"),
|
||||
"storage" => \Kelunik\AcmeClient\getArgumentDescription("storage"),
|
||||
"config" => [
|
||||
"prefix" => "c",
|
||||
"longPrefix" => "config",
|
||||
"description" => "Configuration file to read.",
|
||||
"required" => true,
|
||||
],
|
||||
];
|
||||
|
||||
$configPath = \Kelunik\AcmeClient\getConfigPath();
|
||||
|
||||
if ($configPath) {
|
||||
$args["config"]["required"] = false;
|
||||
$args["config"]["defaultValue"] = $configPath;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use Kelunik\AcmeClient\Stores\KeyStore;
|
||||
use Kelunik\AcmeClient\Stores\KeyStoreException;
|
||||
use League\CLImate\Argument\Manager;
|
||||
use League\CLImate\CLImate;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Setup implements Command {
|
||||
private $climate;
|
||||
@@ -85,9 +86,7 @@ class Setup implements Command {
|
||||
}
|
||||
|
||||
public static function getDefinition() {
|
||||
|
||||
|
||||
return [
|
||||
$args = [
|
||||
"server" => \Kelunik\AcmeClient\getArgumentDescription("server"),
|
||||
"storage" => \Kelunik\AcmeClient\getArgumentDescription("storage"),
|
||||
"email" => [
|
||||
@@ -96,5 +95,18 @@ class Setup implements Command {
|
||||
"required" => true,
|
||||
],
|
||||
];
|
||||
|
||||
$configPath = \Kelunik\AcmeClient\getConfigPath();
|
||||
|
||||
if ($configPath) {
|
||||
$config = Yaml::parse(file_get_contents($configPath));
|
||||
|
||||
if (isset($config["email"]) && is_string($config["email"])) {
|
||||
$args["email"]["required"] = false;
|
||||
$args["email"]["defaultValue"] = $config["email"];
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
5
src/ConfigException.php
Normal file
5
src/ConfigException.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Kelunik\AcmeClient;
|
||||
|
||||
class ConfigException extends \Exception { }
|
||||
@@ -68,7 +68,11 @@ class KeyStore {
|
||||
try {
|
||||
// TODO: Replace with async version once available
|
||||
if (!file_exists(dirname($file))) {
|
||||
mkdir(dirname($file), 0755, true);
|
||||
$success = mkdir(dirname($file), 0755, true);
|
||||
|
||||
if (!$success) {
|
||||
throw new KeyStoreException("Could not create key store directory.");
|
||||
}
|
||||
}
|
||||
|
||||
yield \Amp\File\put($file, $keyPair->getPrivate());
|
||||
|
||||
@@ -111,43 +111,59 @@ function normalizePath($path) {
|
||||
return rtrim(str_replace("\\", "/", $path), "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most appropriate config path to use.
|
||||
*
|
||||
* @return string|null Resolves to the config path or null.
|
||||
*/
|
||||
function getConfigPath() {
|
||||
$paths = isPhar() ? [substr(dirname(Phar::running(true)), strlen("phar://")) . "/acme-client.yml"] : [];
|
||||
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
||||
if ($home = getenv("HOME")) {
|
||||
$paths[] = $home . "/.acme-client.yml";
|
||||
}
|
||||
|
||||
$paths[] = "/etc/acme-client.yml";
|
||||
}
|
||||
|
||||
do {
|
||||
$path = array_shift($paths);
|
||||
|
||||
if (file_exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
} while (count($paths));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a consistent argument description for CLIMate. Valid arguments are "server" and "storage".
|
||||
*
|
||||
* @param string $argument argument name
|
||||
* @return array CLIMate argument description
|
||||
* @throws AcmeException if the provided acme-client.yml file is invalid
|
||||
* @throws ConfigException if the provided configuration file is invalid
|
||||
*/
|
||||
function getArgumentDescription($argument) {
|
||||
$isPhar = \Kelunik\AcmeClient\isPhar();
|
||||
|
||||
$config = [];
|
||||
|
||||
if ($isPhar) {
|
||||
$configPath = substr(dirname(Phar::running(true)), strlen("phar://")) . "/acme-client.yml";
|
||||
if ($configPath = getConfigPath()) {
|
||||
$configContent = file_get_contents($configPath);
|
||||
|
||||
if (file_exists($configPath)) {
|
||||
$configContent = file_get_contents($configPath);
|
||||
try {
|
||||
$config = Yaml::parse($configContent);
|
||||
|
||||
try {
|
||||
$value = Yaml::parse($configContent);
|
||||
|
||||
if (isset($value["server"]) && is_string($value["server"])) {
|
||||
$config["server"] = $value["server"];
|
||||
unset($value["server"]);
|
||||
}
|
||||
|
||||
if (isset($value["storage"]) && is_string($value["storage"])) {
|
||||
$config["storage"] = $value["storage"];
|
||||
unset($value["storage"]);
|
||||
}
|
||||
|
||||
if (!empty($value)) {
|
||||
throw new AcmeException("Provided YAML file had unknown options: " . implode(", ", array_keys($value)));
|
||||
}
|
||||
} catch (ParseException $e) {
|
||||
throw new AcmeException("Unable to parse the YAML file ({$configPath}): " . $e->getMessage());
|
||||
if (isset($config["server"]) && !is_string($config["server"])) {
|
||||
throw new ConfigException("'server' set, but not a string.");
|
||||
}
|
||||
|
||||
if (isset($config["storage"]) && !is_string($config["storage"])) {
|
||||
throw new ConfigException("'storage' set, but not a string.");
|
||||
}
|
||||
} catch (ParseException $e) {
|
||||
throw new AcmeException("Unable to parse the configuration ({$configPath}): " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +184,8 @@ function getArgumentDescription($argument) {
|
||||
return $argument;
|
||||
|
||||
case "storage":
|
||||
$isPhar = isPhar();
|
||||
|
||||
$argument = [
|
||||
"longPrefix" => "storage",
|
||||
"description" => "Storage directory for account keys and certificates.",
|
||||
|
||||
Reference in New Issue
Block a user