summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2024-10-11 14:09:09 +0200
committerSimon Rettberg2024-10-11 14:09:09 +0200
commitbd8569e1c7e615541e41b8aa8f7aa6f22918dd06 (patch)
tree850fca09e6ceea00fd5c18e0dacadb7f788e1fc9
parent[webinterface] Add simple API to remotely supply a certificate (diff)
downloadslx-admin-bd8569e1c7e615541e41b8aa8f7aa6f22918dd06.tar.gz
slx-admin-bd8569e1c7e615541e41b8aa8f7aa6f22918dd06.tar.xz
slx-admin-bd8569e1c7e615541e41b8aa8f7aa6f22918dd06.zip
[webinterface] Add event log messages for cert changes
-rw-r--r--inc/taskmanagercallback.inc.php13
-rw-r--r--inc/user.inc.php7
-rw-r--r--modules-available/webinterface/api.inc.php3
-rw-r--r--modules-available/webinterface/inc/acme.inc.php11
-rw-r--r--modules-available/webinterface/inc/webinterface.inc.php38
-rw-r--r--modules-available/webinterface/page.inc.php14
-rw-r--r--modules-available/webinterface/templates/https.html3
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}}