diff options
-rw-r--r-- | inc/taskmanagercallback.inc.php | 13 | ||||
-rw-r--r-- | inc/user.inc.php | 7 | ||||
-rw-r--r-- | modules-available/webinterface/api.inc.php | 3 | ||||
-rw-r--r-- | modules-available/webinterface/inc/acme.inc.php | 11 | ||||
-rw-r--r-- | modules-available/webinterface/inc/webinterface.inc.php | 38 | ||||
-rw-r--r-- | modules-available/webinterface/page.inc.php | 14 | ||||
-rw-r--r-- | modules-available/webinterface/templates/https.html | 3 |
7 files changed, 73 insertions, 16 deletions
diff --git a/inc/taskmanagercallback.inc.php b/inc/taskmanagercallback.inc.php index 9f276020..536c899e 100644 --- a/inc/taskmanagercallback.inc.php +++ b/inc/taskmanagercallback.inc.php @@ -240,13 +240,22 @@ class TaskmanagerCallback SystemStatus::setUpgradableData($task); } - public static function acmeErrors(array $task): void + public static function acmeErrors(array $task, $args): void { $mod = Module::get('webinterface'); if ($mod === false) return; $mod->activate(1, false); - Acme::callbackErrorCheck($task); + Acme::callbackErrorCheck($task, $args); + } + + public static function webifCert(array $task, $args): void + { + $mod = Module::get('webinterface'); + if ($mod === false) + return; + $mod->activate(1, false); + WebInterface::certTaskFinishedCallback($task, $args); } } diff --git a/inc/user.inc.php b/inc/user.inc.php index 9ef27cd0..dd94ab71 100644 --- a/inc/user.inc.php +++ b/inc/user.inc.php @@ -30,6 +30,13 @@ class User return self::$user['fullname']; } + public static function getLogin(): ?string + { + if (!self::isLoggedIn()) + return null; + return self::$user['login']; + } + public static function hasPermission(string $permission, ?int $locationid = NULL): bool { if (!self::isLoggedIn()) diff --git a/modules-available/webinterface/api.inc.php b/modules-available/webinterface/api.inc.php index be374ed5..e368b56d 100644 --- a/modules-available/webinterface/api.inc.php +++ b/modules-available/webinterface/api.inc.php @@ -20,7 +20,8 @@ if (empty($newCert)) { } // Import will try to validate the certificate too -$task = WebInterface::tmImportCustomCert($newKey, $newCert); +$task = WebInterface::tmImportCustomCert($newKey, $newCert, 'api', + 'New HTTPS certificate uploaded via API from ' . $_SERVER['REMOTE_ADDR']); $task = Taskmanager::waitComplete($task, 10000); if (!Taskmanager::isTask($task)) { http_send_status(500); diff --git a/modules-available/webinterface/inc/acme.inc.php b/modules-available/webinterface/inc/acme.inc.php index c23578cc..f12ceb2e 100644 --- a/modules-available/webinterface/inc/acme.inc.php +++ b/modules-available/webinterface/inc/acme.inc.php @@ -81,21 +81,26 @@ class Acme if (!is_array($task) || !Taskmanager::isTask($task)) return; $task = Taskmanager::waitComplete($task, 250); + $args = ['user' => User::getLogin()]; if (Taskmanager::isFinished($task)) { - self::callbackErrorCheck($task); + self::callbackErrorCheck($task, $args); } else { Property::set(self::PROP_ERROR, false); - TaskmanagerCallback::addCallback($task, 'acmeErrors'); + TaskmanagerCallback::addCallback($task, 'acmeErrors', $args); } } - public static function callbackErrorCheck(array $task): void + public static function callbackErrorCheck(array $task, $args): void { if (!Taskmanager::isFinished($task)) return; if (Taskmanager::isFailed($task)) { + if (($args['user'] ?? null) === null) { + EventLog::warning('Automatic ACME renewal of HTTPS certificate failed', json_encode($task, JSON_PRETTY_PRINT)); + } Property::set(self::PROP_ERROR, $task['data']['error'] ?? 'Unknown error'); } else { + EventLog::info('ACME issue/renewal of HTTPS certificate by ' . ($args['user'] ?? 'automatic cronjob')); Property::set(self::PROP_ERROR, false); } } diff --git a/modules-available/webinterface/inc/webinterface.inc.php b/modules-available/webinterface/inc/webinterface.inc.php index 20be6545..035b94e6 100644 --- a/modules-available/webinterface/inc/webinterface.inc.php +++ b/modules-available/webinterface/inc/webinterface.inc.php @@ -69,33 +69,61 @@ class WebInterface return Property::get(self::PROP_REDIRECT) === 'True'; } + private static function registerCallback($task, string $newState, string $logMessage): void + { + if (!Taskmanager::isTask($task)) + return; + TaskmanagerCallback::addCallback($task, 'webifCert', [ + 'state' => $newState, + 'message' => $logMessage . ' by ' . (User::getLogin() ?? 'system'), + ]); + } + + public static function certTaskFinishedCallback(array $task, $data): void + { + if (!Taskmanager::isFinished($task)) + return; + if (!isset($data['state']) || !isset($data['message'])) { + error_log('Invalid certTaskFinishedCallback: Missing fields'); + return; + } + if (Taskmanager::isFailed($task)) { + EventLog::failure($data['message'], json_encode($task, JSON_PRETTY_PRINT)); + return; + } + EventLog::info($data['message']); + Property::set(self::PROP_TYPE, $data['state']); + } + public static function tmDisableHttps(): ?string { - Property::set(WebInterface::PROP_TYPE, 'off'); Property::set(WebInterface::PROP_HSTS, 'off'); $task = Taskmanager::submit('LighttpdHttps', []); + self::registerCallback($task, 'off', 'HTTPS disabled'); return $task['id'] ?? null; } public static function tmGenerateRandomCert(): ?string { - Property::set(WebInterface::PROP_TYPE, 'generated'); $task = Taskmanager::submit('LighttpdHttps', [ 'proxyip' => Property::getServerIp(), 'redirect' => self::isHttpsRedirectEnabled(), ]); + self::registerCallback($task, 'generated', 'Self-signed HTTPS certificate generated'); return $task['id'] ?? null; } - public static function tmImportCustomCert(string $key, string $cert, ?string $chain = null): ?string + public static function tmImportCustomCert(string $key, string $cert, string $type, string $logMessage): ?string { - Property::set(WebInterface::PROP_TYPE, 'supplied'); + $key = preg_replace('/[\r\n]+/', "\n", $key); + $cert = preg_replace('/[\r\n]+/', "\n", $cert); + Property::set(WebInterface::PROP_TYPE, $type); $task = Taskmanager::submit('LighttpdHttps', [ 'importcert' => $cert, 'importkey' => $key, - 'importchain' => $chain, 'redirect' => self::isHttpsRedirectEnabled(), ]); + self::registerCallback($task, $type, $logMessage); return $task['id'] ?? null; } diff --git a/modules-available/webinterface/page.inc.php b/modules-available/webinterface/page.inc.php index d21c627e..a2123ac5 100644 --- a/modules-available/webinterface/page.inc.php +++ b/modules-available/webinterface/page.inc.php @@ -132,7 +132,7 @@ class Page_WebInterface extends Page // Admin might have modified web server config in another way Message::addWarning('https-used-without-cert'); } - } elseif ($type === 'generated' || $type === 'supplied' || $type === 'acme') { + } elseif ($type === 'generated' || $type === 'supplied' || $type === 'acme' || $type === 'api') { $data['httpsEnabled'] = true; if ($force && !$https) { Message::addWarning('https-want-redirect-is-plain'); @@ -250,10 +250,14 @@ class Page_WebInterface extends Page private function setHttpsCustomCert(): ?string { - $cert = Request::post('certificate', Request::REQUIRED, 'string'); - $key = Request::post('privatekey', Request::REQUIRED, 'string'); - $chain = Request::post('cachain', '', 'string'); - return WebInterface::tmImportCustomCert($key, $cert, $chain); + $cert = trim(Request::post('certificate', Request::REQUIRED, 'string')); + $key = trim(Request::post('privatekey', Request::REQUIRED, 'string')); + $chain = trim(Request::post('cachain', '', 'string')); + if (!empty($chain)) { + $cert .= "\n" . $chain; + } + return WebInterface::tmImportCustomCert($key . "\n", $cert . "\n", 'supplied', + 'New certificate uploaded by ' . User::getLogin()); } private function setAcmeMode(): ?string diff --git a/modules-available/webinterface/templates/https.html b/modules-available/webinterface/templates/https.html index dbffa9b7..b109fa9d 100644 --- a/modules-available/webinterface/templates/https.html +++ b/modules-available/webinterface/templates/https.html @@ -24,6 +24,9 @@ {{#acmeSelected}} <p>{{lang_acmeSelected}}</p> {{/acmeSelected}} + {{#apiSelected}} + <p>{{lang_apiSelected}}</p> + {{/apiSelected}} </div> <table class="slx-table"> {{#certIssuer}} |