public function doExecute(Manager $args) : Generator { if (posix_geteuid() !== 0) { throw new AcmeException("Please run this script as root!"); } $email = $args->get("email"); (yield resolve($this->checkEmail($email))); $server = $args->get("server"); $protocol = substr($server, 0, strpos("://", $server)); if (!$protocol || $protocol === $server) { $server = "https://" . $server; } elseif ($protocol !== "https") { throw new \InvalidArgumentException("Invalid server protocol, only HTTPS supported"); } $identity = str_replace(["/", "%"], "-", substr($server, 8)); $path = __DIR__ . "/../../data/accounts"; $pathPrivate = "{$path}/{$identity}.private.key"; $pathPublic = "{$path}/{$identity}.public.key"; if ((yield exists($pathPrivate)) && (yield exists($pathPublic))) { $this->logger->info("Loading existing keys ..."); $private = file_get_contents($pathPrivate); $public = file_get_contents($pathPublic); $keyPair = new KeyPair($private, $public); } else { $this->logger->info("Generating key keys ..."); $keyPair = (new OpenSSLKeyGenerator())->generate(4096); if (!file_exists($path) && !mkdir($path, 0700, true)) { throw new AcmeException("Couldn't create account directory"); } file_put_contents($pathPrivate, $keyPair->getPrivate()); file_put_contents($pathPublic, $keyPair->getPublic()); chmod($pathPrivate, 600); chmod($pathPrivate, 600); } $acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair); $this->logger->info("Registering with ACME server " . substr($server, 8) . " ..."); /** @var Registration $registration */ $registration = (yield $acme->register($email)); $this->logger->notice("Registration successful with contact " . json_encode($registration->getContact())); }
private function loadFile(string $filePath) : \Generator { if (isset($this->loadPromises[$filePath])) { (yield $this->loadPromises[$filePath]); return $this->dataCache[$filePath]; } $deferred = new Deferred(); $this->loadPromises[$filePath] = $deferred->promise(); $this->lockMutexes[$filePath] = new QueuedExclusiveMutex(); return (yield $this->lockMutexes[$filePath]->withLock(function () use($filePath, $deferred) { try { // we may have been waiting on a lock and it's been populated by now if (!isset($this->dataCache[$filePath])) { $this->dataCache[$filePath] = (yield exists($filePath)) ? json_try_decode((yield get($filePath)), true) : []; } } catch (\Throwable $e) { $this->dataCache[$filePath] = []; } finally { $deferred->succeed(); unset($this->loadPromises[$filePath]); } return $this->dataCache[$filePath]; })); }
private function doLoadKeyPair(string $path) : Generator { $privateExists = (yield exists("{$path}/private.pem")); $publicExists = (yield exists("{$path}/public.pem")); $lockExists = (yield exists("{$path}/key.lock")); if ($privateExists && $publicExists) { while ($lockExists) { (yield new Pause(500)); $lockExists = (yield exists("{$path}/key.lock")); } return new KeyPair((yield get("{$path}/private.pem")), (yield get("{$path}/public.pem"))); } $lock = new Lock("{$path}/key.lock"); try { $lock->acquire(); $gen = new OpenSSLKeyGenerator(); $keyPair = $gen->generate(4096); (yield put("{$path}/private.pem", $keyPair->getPrivate())); (yield put("{$path}/public.pem", $keyPair->getPublic())); return $keyPair; } catch (Exception $e) { do { (yield new Pause(500)); $lockExists = (yield exists("{$path}/key.lock")); } while ($lockExists); return new KeyPair((yield get("{$path}/private.pem")), (yield get("{$path}/public.pem"))); } finally { $lock->release(); unlink("{$path}/key.lock"); // do not yield in finally! } }