private function doExecute(Manager $args) { $keyStore = new KeyStore(\Kelunik\AcmeClient\normalizePath($args->get("storage"))); $server = \Kelunik\AcmeClient\resolveServer($args->get("server")); $keyFile = \Kelunik\AcmeClient\serverToKeyname($server); $keyPair = (yield $keyStore->get("accounts/{$keyFile}.pem")); $acme = $this->acmeFactory->build($server, $keyPair); $this->climate->br(); $this->climate->whisper(" Revoking certificate ..."); $path = \Kelunik\AcmeClient\normalizePath($args->get("storage")) . "/certs/" . $keyFile . "/" . $args->get("name") . "/cert.pem"; try { $pem = (yield \Amp\File\get($path)); $cert = new Certificate($pem); } catch (FilesystemException $e) { throw new \RuntimeException("There's no such certificate (" . $path . ")"); } if ($cert->getValidTo() < time()) { $this->climate->comment(" Certificate did already expire, no need to revoke it."); } $names = $cert->getNames(); $this->climate->whisper(" Certificate was valid for " . count($names) . " domains."); $this->climate->whisper(" - " . implode(PHP_EOL . " - ", $names) . PHP_EOL); (yield $acme->revokeCertificate($pem)); $this->climate->br(); $this->climate->info(" Certificate has been revoked."); (yield (new CertificateStore(\Kelunik\AcmeClient\normalizePath($args->get("storage")) . "/certs/" . $keyFile))->delete($args->get("name"))); (yield new CoroutineResult(0)); }
private function doGet($name) { Assert::string($name, "Name must be a string. Got: %s"); try { $contents = (yield \Amp\File\get($this->root . "/" . $name . "/cert.pem")); (yield new CoroutineResult($contents)); } catch (FilesystemException $e) { throw new CertificateStoreException("Failed to load certificate.", 0, $e); } }
private function doGet($path) { if (!is_string($path)) { throw new InvalidArgumentException(sprintf("\$root must be of type string, %s given.", gettype($path))); } $file = $this->root . "/" . $path; $realpath = realpath($file); if (!$realpath) { throw new KeyStoreException("File not found: '{$file}'"); } $privateKey = (yield \Amp\File\get($realpath)); $res = openssl_pkey_get_private($privateKey); if ($res === false) { throw new KeyStoreException("Invalid private key: '{$file}'"); } $publicKey = openssl_pkey_get_details($res)["key"]; (yield new CoroutineResult(new KeyPair($privateKey, $publicKey))); }
/** * @param Manager $args * @return \Generator */ private function doExecute(Manager $args) { $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 ($args->defined("server")) { $config["server"] = $args->get("server"); } else { if (!isset($config["server"]) && $args->exists("server")) { $config["server"] = $args->get("server"); } } if ($args->defined("storage")) { $config["storage"] = $args->get("storage"); } else { if (!isset($config["storage"]) && $args->exists("storage")) { $config["storage"] = $args->get("storage"); } } if (!isset($config["server"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'server' set nor was it passed as command line argument."); (yield new CoroutineResult(self::EXIT_CONFIG_ERROR)); return; } if (!isset($config["storage"])) { $this->climate->error("Config file ({$configPath}) didn't have a 'storage' set nor was it passed as command line argument."); (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", $config["server"], "--storage", $config["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, $config["server"], $config["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; } }
function __loadHostsFile($path = null) { $data = []; if (empty($path)) { $path = \stripos(PHP_OS, "win") === 0 ? 'C:\\Windows\\system32\\drivers\\etc\\hosts' : '/etc/hosts'; } try { $contents = (yield \Amp\File\get($path)); } catch (\Exception $e) { (yield new CoroutineResult($data)); return; } $lines = \array_filter(\array_map("trim", \explode("\n", $contents))); foreach ($lines as $line) { if ($line[0] === "#") { continue; } $parts = \preg_split('/\\s+/', $line); if (!($ip = @\inet_pton($parts[0]))) { continue; } elseif (isset($ip[4])) { $key = Record::AAAA; } else { $key = Record::A; } for ($i = 1, $l = \count($parts); $i < $l; $i++) { if (__isValidHostName($parts[$i])) { $data[$key][strtolower($parts[$i])] = $parts[0]; } } } (yield new CoroutineResult($data)); }
private function loadHostsFile($path = null) { $data = []; if (empty($path)) { $path = \stripos(PHP_OS, "win") === 0 ? 'C:\\Windows\\system32\\drivers\\etc\\hosts' : '/etc/hosts'; } try { $contents = (yield \Amp\File\get($path)); } catch (\Exception $e) { (yield new CoroutineResult($data)); return; } $lines = \array_filter(\array_map("trim", \explode("\n", $contents))); foreach ($lines as $line) { if ($line[0] === "#") { continue; } $parts = \preg_split('/\\s+/', $line); if (!($ip = @\inet_pton($parts[0]))) { continue; } elseif (isset($ip[4])) { $key = Record::AAAA; } else { $key = Record::A; } for ($i = 1, $l = \count($parts); $i < $l; $i++) { if ($this->isValidHostName($parts[$i])) { $data[$key][strtolower($parts[$i])] = $parts[0]; } } } // Windows does not include localhost in its host file. Fetch it from the system instead if (!isset($data[Record::A]["localhost"]) && !isset($data[Record::AAAA]["localhost"])) { // PHP currently provides no way to **resolve** IPv6 hostnames (not even with fallback) $local = gethostbyname("localhost"); if ($local !== "localhost") { $data[Record::A]["localhost"] = $local; } else { $data[Record::AAAA]["localhost"] = "::1"; } } (yield new CoroutineResult($data)); }