diff options
author | Simon Rettberg | 2024-10-08 16:22:17 +0200 |
---|---|---|
committer | Simon Rettberg | 2024-10-08 16:22:17 +0200 |
commit | 882b694e06acd389dd74f7a7d9b70ada0fd218d5 (patch) | |
tree | 11c95683e56645c498a08378dadf2422cd2d27fb /modules-available/webinterface/inc/acme.inc.php | |
parent | Update phpdoc (diff) | |
download | slx-admin-882b694e06acd389dd74f7a7d9b70ada0fd218d5.tar.gz slx-admin-882b694e06acd389dd74f7a7d9b70ada0fd218d5.tar.xz slx-admin-882b694e06acd389dd74f7a7d9b70ada0fd218d5.zip |
[webinterface] Add support for ACME, add option to redirect to cert domain
Diffstat (limited to 'modules-available/webinterface/inc/acme.inc.php')
-rw-r--r-- | modules-available/webinterface/inc/acme.inc.php | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/modules-available/webinterface/inc/acme.inc.php b/modules-available/webinterface/inc/acme.inc.php new file mode 100644 index 00000000..c23578cc --- /dev/null +++ b/modules-available/webinterface/inc/acme.inc.php @@ -0,0 +1,164 @@ +<?php + +class Acme +{ + + const PROP_ERROR = 'acme.error-string'; + const PROP_PROVIDER = 'acme.provider'; + const PROP_KEY_ID = 'acme.key-id'; + const PROP_HMAC_KEY = 'acme.hmac-key'; + const PROP_DOMAINS = 'acme.domains'; + const PROP_MAIL = 'acme.mail'; + const VALID_PROVIDERS = [ + 'letsencrypt' => "Let's Encrypt", + 'zerossl' => 'ZeroSSL.com', + 'buypass' => 'BuyPass.com', + 'geant/sectigo' => 'GEANT via Sectigo', + ]; + + const PROVIDER_ALIASES = [ + 'geant/sectigo' => 'https://acme.sectigo.com/v2/GEANTOV', + ]; + + public static function getLastError(): ?string + { + return Property::get(self::PROP_ERROR, null); + } + + public static function getProvider(): ?string + { + return Property::get(self::PROP_PROVIDER, null); + } + + public static function getKeyId(): ?string + { + return Property::get(self::PROP_KEY_ID, null); + } + + public static function getHmacKey(): ?string + { + return Property::get(self::PROP_HMAC_KEY, null); + } + + /** + * @return string[] list of [id] => friendly name + */ + public static function getProviders(): array + { + return self::VALID_PROVIDERS; + } + + public static function getMail(): ?string + { + return Property::get(self::PROP_MAIL, null); + } + + public static function getDomains(): array + { + return explode(' ', Property::get(self::PROP_DOMAINS)); + } + + public static function setConfig(string $provider, string $mail, ?string $keyId = null, ?string $hmacKey = null): bool + { + if (!isset(self::VALID_PROVIDERS[$provider])) { + Message::addError('webinterface.acme-invalid-provider', $provider); + return false; + } + Property::set(self::PROP_PROVIDER, $provider); + Property::set(self::PROP_MAIL, $mail); + Property::set(self::PROP_KEY_ID, $keyId); + Property::set(self::PROP_HMAC_KEY, $hmacKey); + return true; + } + + public static function setDomains(array $list): void + { + Property::set(self::PROP_DOMAINS, implode(' ', $list)); + } + + private static function handleErrorAsync($task): void + { + if (!is_array($task) || !Taskmanager::isTask($task)) + return; + $task = Taskmanager::waitComplete($task, 250); + if (Taskmanager::isFinished($task)) { + self::callbackErrorCheck($task); + } else { + Property::set(self::PROP_ERROR, false); + TaskmanagerCallback::addCallback($task, 'acmeErrors'); + } + } + + public static function callbackErrorCheck(array $task): void + { + if (!Taskmanager::isFinished($task)) + return; + if (Taskmanager::isFailed($task)) { + Property::set(self::PROP_ERROR, $task['data']['error'] ?? 'Unknown error'); + } else { + Property::set(self::PROP_ERROR, false); + } + } + + public static function issueNewCertificate(bool $wipeAll = false): ?string + { + $provider = self::getProvider(); + if ($provider === null) { + Message::addError('webinterface.acme-no-provider'); + return null; + } + $mail = self::getMail(); + if (empty($mail)) { + Message::addError('webinterface.acme-no-mail'); + return null; + } + $domains = self::getDomains(); + if (empty($domains)) { + Message::addError('webinterface.acme-no-domains'); + return null; + } + $redirect = Property::get(WebInterface::PROP_REDIRECT); + $task = Taskmanager::submit('LighttpdHttps', [ + 'redirect' => $redirect, + 'acmeMode' => 'issue', + 'acmeMail' => $mail, + 'acmeDomains' => $domains, + 'acmeProvider' => self::PROVIDER_ALIASES[$provider] ?? $provider, + 'acmeKeyId' => self::getKeyId(), + 'acmeHmacKey' => self::getHmacKey(), + 'acmeWipeAll' => $wipeAll, + ]); + self::handleErrorAsync($task); + return $task['id'] ?? null; + } + + public static function renew(): ?string + { + error_log("Renew called"); + $domains = self::getDomains(); + if (empty($domains)) { + Message::addError('webinterface.acme-no-domains'); + return null; + } + $redirect = Property::get(WebInterface::PROP_REDIRECT); + $task = Taskmanager::submit('LighttpdHttps', [ + 'redirect' => $redirect, + 'acmeMode' => 'renew', + 'acmeDomains' => $domains, + ]); + self::handleErrorAsync($task); + return $task['id'] ?? null; + } + + public static function tryEnable(): bool + { + $redirect = Property::get(WebInterface::PROP_REDIRECT); + $task = Taskmanager::submit('LighttpdHttps', [ + 'redirect' => $redirect, + 'acmeMode' => 'try-enable', + ]); + $task = Taskmanager::waitComplete($task, 10000); + return Taskmanager::isFinished($task) && !Taskmanager::isFailed($task); + } + +}
\ No newline at end of file |