summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2024-10-11 15:43:17 +0200
committerSimon Rettberg2024-10-11 15:43:17 +0200
commitccc7cf78a7ca8b3349c45706b8b03e1ed6bc44d6 (patch)
tree261b535912030ac41667ce954093dbe48ea5bc85
parent[webinterface] API: Check callbacks immediately on success (diff)
downloadslx-admin-ccc7cf78a7ca8b3349c45706b8b03e1ed6bc44d6.tar.gz
slx-admin-ccc7cf78a7ca8b3349c45706b8b03e1ed6bc44d6.tar.xz
slx-admin-ccc7cf78a7ca8b3349c45706b8b03e1ed6bc44d6.zip
[webinterface] Remove the 'off' option for HTTPS
As we always use a self-signed certificate for client communication, you cannot really turn off HTTPS, and explicitly generating another self-signed certificate for slx-admin is rather pointless. So internally, we actually remove the option for a self-signed certificate, and rename the "off" option to using a self-signed one.
-rw-r--r--modules-available/webinterface/api.inc.php2
-rw-r--r--modules-available/webinterface/inc/acme.inc.php2
-rw-r--r--modules-available/webinterface/inc/webinterface.inc.php16
-rw-r--r--modules-available/webinterface/lang/de/messages.json2
-rw-r--r--modules-available/webinterface/lang/de/template-tags.json9
-rw-r--r--modules-available/webinterface/lang/en/messages.json2
-rw-r--r--modules-available/webinterface/lang/en/template-tags.json7
-rw-r--r--modules-available/webinterface/page.inc.php78
-rw-r--r--modules-available/webinterface/templates/https.html19
9 files changed, 42 insertions, 95 deletions
diff --git a/modules-available/webinterface/api.inc.php b/modules-available/webinterface/api.inc.php
index 0b4fabdb..271ccc60 100644
--- a/modules-available/webinterface/api.inc.php
+++ b/modules-available/webinterface/api.inc.php
@@ -21,7 +21,7 @@ if (empty($newCert)) {
// Import will try to validate the certificate too
$task = WebInterface::tmImportCustomCert($newKey, $newCert, 'api',
- 'New HTTPS certificate uploaded via API from ' . $_SERVER['REMOTE_ADDR']);
+ 'Applying 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 f12ceb2e..446c0e45 100644
--- a/modules-available/webinterface/inc/acme.inc.php
+++ b/modules-available/webinterface/inc/acme.inc.php
@@ -96,7 +96,7 @@ class Acme
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));
+ EventLog::warning('Automatic ACME renewal of HTTPS certificate failed', print_r($task, true));
}
Property::set(self::PROP_ERROR, $task['data']['error'] ?? 'Unknown error');
} else {
diff --git a/modules-available/webinterface/inc/webinterface.inc.php b/modules-available/webinterface/inc/webinterface.inc.php
index 035b94e6..d50acd50 100644
--- a/modules-available/webinterface/inc/webinterface.inc.php
+++ b/modules-available/webinterface/inc/webinterface.inc.php
@@ -88,7 +88,7 @@ class WebInterface
return;
}
if (Taskmanager::isFailed($task)) {
- EventLog::failure($data['message'], json_encode($task, JSON_PRETTY_PRINT));
+ EventLog::failure('TASK FAILED: ' . $data['message'], print_r($task, true));
return;
}
EventLog::info($data['message']);
@@ -97,19 +97,10 @@ class WebInterface
public static function tmDisableHttps(): ?string
{
- 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
- {
$task = Taskmanager::submit('LighttpdHttps', [
- 'proxyip' => Property::getServerIp(),
'redirect' => self::isHttpsRedirectEnabled(),
]);
- self::registerCallback($task, 'generated', 'Self-signed HTTPS certificate generated');
+ self::registerCallback($task, 'off', 'Disabling HTTPS, switching to self-signed certificate');
return $task['id'] ?? null;
}
@@ -117,7 +108,6 @@ class WebInterface
{
$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,
@@ -129,8 +119,6 @@ class WebInterface
public static function tmSetHttpRedirectMode(): ?string
{
- if (Property::get(WebInterface::PROP_TYPE) === 'off')
- return null;
$task = Taskmanager::submit('LighttpdHttps', array(
'redirectOnly' => true,
'redirect' => self::isHttpsRedirectEnabled(),
diff --git a/modules-available/webinterface/lang/de/messages.json b/modules-available/webinterface/lang/de/messages.json
index 3f1d5ba4..f1d0d6af 100644
--- a/modules-available/webinterface/lang/de/messages.json
+++ b/modules-available/webinterface/lang/de/messages.json
@@ -4,8 +4,6 @@
"acme-no-mail": "Keine administrative Mailadresse angegeben",
"acme-no-provider": "Kein ACME-Anbieter ausgew\u00e4hlt",
"https-on-cert-missing": "HTTPS ist aktiviert, das Zertifikat ist jedoch nicht vorhanden. Bitte nehmen Sie die HTTPS-Konfiguration erneut vor.",
- "https-used-without-cert": "HTTPS wird gerade verwendet, obwohl kein Zertifikat installiert ist. Falls Sie die Webserver-Konfiguration manuell angepasst haben, um HTTPS zu aktivieren beachten Sie bitte, dass die Konfiguration bei einem zuk\u00fcnftigen Server-Update ohne Nachfrage \u00fcberschrieben werden k\u00f6nnte.",
- "https-want-off-is-used": "HTTPS wird gerade verwendet, obwohl es laut Einstellungen deaktiviert ist. Merkw\u00fcrdig.",
"https-want-redirect-is-plain": "Weiterleitung von HTTP auf HTTPS ist aktiviert, trotzdem scheint die Verbindung Ihres Browsers mit dem Server unverschl\u00fcsselt zu sein. Nehmen Sie die Konfiguration erneut vor und wenden Sie sich an den Support, wenn das Problem weiterhin besteht.",
"invalid-domain": "Ung\u00fcltige Domain: {{0}}",
"mw-acme-errors": "Fehler beim erneuern\/abrufen des Zertifikats via ACME"
diff --git a/modules-available/webinterface/lang/de/template-tags.json b/modules-available/webinterface/lang/de/template-tags.json
index 05cd88b0..ed7e9f00 100644
--- a/modules-available/webinterface/lang/de/template-tags.json
+++ b/modules-available/webinterface/lang/de/template-tags.json
@@ -9,6 +9,7 @@
"lang_acmeSelected": "Das aktuelle Zertifikat wurde via ACME erstellt.",
"lang_acmeWipeAll": "Alle hinterlegten Daten l\u00f6schen und Account etc. von neuem anfordern",
"lang_acmeWipeAllHint": "W\u00e4hlen Sie diese Option aus, wenn das Zertifikat nicht verl\u00e4ndert werden kann, oder es Probleme beim \u00c4ndern und Speichern der Daten gibt. Beachten Sie, dass einige Anbieter ein Ratelimit haben, Sie also nicht zu oft\/schnell hintereinander ein neues Zertifikat anfordern sollten.",
+ "lang_apiSelected": "Das aktuelle Zertifikat wurde mittels API-Zugriff eingespielt.",
"lang_applyingSettings": "Anwenden der Einstellungen",
"lang_caChain": "Optional k\u00f6nnen Sie hier die zum Zertifikat geh\u00f6rende Zertifikatkette (CA-Chain) einf\u00fcgen. Dies wird ben\u00f6tigt, wenn das Zertifikat nicht direkt von einer der in Browsern mitgeliferten CAs signiert wurde. Die Datei enth\u00e4lt ein oder meherere Zertifikatsbl\u00f6cke, im gleichen Format wie das oben gezeigte Zertifikat.",
"lang_certExpireTime": "Zertifikat l\u00e4uft ab",
@@ -27,25 +28,23 @@
"lang_httpsApiKeyDescription": "Hier k\u00f6nnen Sie ein API-Token generieren, mit dem Sie das Zertifikat von einem anderen Rechner aus aktualisieren k\u00f6nnen. Um die Funktion zu deaktivieren, l\u00f6schen Sie das Token wieder.",
"lang_httpsApiKeyRegenerateConfirm": "Aktuelles Token verwerfen und neu generieren?",
"lang_httpsApiPostExample": "Ein Beispiel mittels curl",
- "lang_httpsApiPostMaybeInsecure": "Es ist ggf. sinnvoll, zum \u00fcbermitteln die Zertifikatsverifikation abzuschalten, wenn z.B. zu erwarten ist, dass das Zertifikat des Satellitenservers bereits abgelaufen ist, oder selbstsigniert. Bei curl geht das unter Verwendung des Paramters -k",
+ "lang_httpsApiPostMaybeInsecure": "Es ist ggf. sinnvoll, zum \u00fcbermitteln die Zertifikatsverifikation abzuschalten, wenn z.B. zu erwarten ist, dass das Zertifikat des Satellitenservers bereits abgelaufen ist, oder selbst signiert. Bei curl geht das unter Verwendung des Paramters -k",
"lang_httpsApiPostText": "Um ein neues Zertifikat einzuspielen, senden Sie einen POST-Request an die folgende URL, mit den POST-Feldern \"token\" (obiges Token), sowie \"privkey\" (privater Schl\u00fcssel des Zertifikats) und \"cert\" (Zertifikat, ggf. mit angeh\u00e4ngten Intermediates, aka fullchain), beides im PEM-Format.",
"lang_httpsCurrentApiKey": "Aktuelles Token",
"lang_httpsDescription": "Hier k\u00f6nnen Sie festlegen, ob das Web-Interface auch per HTTPS erreichbar sein soll, und welches Zertifikat daf\u00fcr verwendet werden soll.",
"lang_httpsOptionNoChange": "Nichts \u00e4ndern",
- "lang_httpsRedirect": "Anfragen per HTTP immer auf HTTPS umleiten (sofern aktiviert)",
+ "lang_httpsRedirect": "Anfragen per HTTP immer auf HTTPS umleiten",
"lang_httpsSettings": "HTTPS-Konfiguration",
"lang_installAndRestart": "Zertifikat installieren und Webserver neustarten",
"lang_logoBackground": "Hintergrundfarbe des Logos",
"lang_moduleHeading": "Web-Schnittstelle",
"lang_msgAcmeFailed": "ACME-Abruf fehlgeschlagen",
- "lang_noHttps": "HTTPS wieder deaktivieren, aktuelles Zertifikat l\u00f6schen",
- "lang_offSelected": "HTTPS ist derzeit deaktiviert.",
+ "lang_noHttps": "Aktuelles Zertifikat l\u00f6schen, nur internes, selbst signiertes Zertifikat nutzen",
"lang_optionAcme": "ACMEv2",
"lang_pageTitlePrefix": "Pr\u00e4fix f\u00fcr den Seitentitel",
"lang_passwordFields": "Passwortfelder",
"lang_passwordsDescription": "Legen Sie fest, ob Passwortfelder in der Web-Schnittstelle maskiert werden, oder ob Ihr Inhalt sichtbar sein soll. Wenn Sie die Schnittstelle in einer sicheren Umgebung nutzen (keine neugierigen Augen), kann dies den Komfort erh\u00f6hen. Das Passwortfeld der Anmeldemaske ist von dieser Einstellung ausgenommen.",
"lang_privateKey": "Bitte f\u00fcgen Sie hier den privaten Schl\u00fcssel ein, der zum obigen Zertifikat geh\u00f6rt. Er muss ebenfalls im \"pem\"-Format vorliegen, und sieht wie folgt aus:",
- "lang_randomCert": "Neues selbstsigniertes Zertifikat generieren",
"lang_redirectDomain": "Bei Browserzugriff \u00fcber eine andere Domain oder per IP auf die Prim\u00e4rdomain des Zertifikats umleiten",
"lang_regenerate": "(Re)generieren",
"lang_showPasswords": "Passw\u00f6rter anzeigen",
diff --git a/modules-available/webinterface/lang/en/messages.json b/modules-available/webinterface/lang/en/messages.json
index edccbd47..f938552a 100644
--- a/modules-available/webinterface/lang/en/messages.json
+++ b/modules-available/webinterface/lang/en/messages.json
@@ -4,8 +4,6 @@
"acme-no-mail": "No technical mail contact specified",
"acme-no-provider": "No ACME provider selected",
"https-on-cert-missing": "HTTPS is enabled, but the certificate is missing. Please redo the configuration steps.",
- "https-used-without-cert": "HTTPS is currently used, but there is no certificate installed. If you tweaked the web server's configuration manually to enable HTTPS bear in mind that a future server update might overwrite your modified configuration without asking.",
- "https-want-off-is-used": "HTTPS is currently in use although it is disabled in the settings. Very weird indeed.",
"https-want-redirect-is-plain": "HTTP to HTTPS redirects are enabled, but the connection from your browser appears to be unencrypted. Please redo the HTTPS configuration and contact support if the problem persists.",
"invalid-domain": "Invalid domain: {{0}}",
"mw-acme-errors": "Error renewing\/requesting certificate via ACME"
diff --git a/modules-available/webinterface/lang/en/template-tags.json b/modules-available/webinterface/lang/en/template-tags.json
index 9c290083..b3afb43f 100644
--- a/modules-available/webinterface/lang/en/template-tags.json
+++ b/modules-available/webinterface/lang/en/template-tags.json
@@ -9,6 +9,7 @@
"lang_acmeSelected": "Current certificate was generated via ACME.",
"lang_acmeWipeAll": "Wipe all existing data and request everything anew",
"lang_acmeWipeAllHint": "Select this option if you experience trouble renewing an existing certificate, or if a previous registration attempt left stale data. Please be aware that rate limits apply with some providers, so you shouldn't issue too many requests over a short period of time.",
+ "lang_apiSelected": "Current certificate was supplied via API access.",
"lang_applyingSettings": "Applying settings",
"lang_caChain": "Here you can paste an optional certificate chain. It should only be required if you have a certificate that was not directly signed by a certificate authority known by the browsers. It should contain one or more certificate blocks, looking just like the certificate above.",
"lang_certExpireTime": "Certificate expires",
@@ -32,20 +33,18 @@
"lang_httpsCurrentApiKey": "Current token",
"lang_httpsDescription": "Here you can set whether the web interface should be accessible via https. You can choose if you want to use a random self signed certificate, or supply your own.",
"lang_httpsOptionNoChange": "Don't change",
- "lang_httpsRedirect": "Redirect incoming HTTP requests to HTTPS (if enabled).",
+ "lang_httpsRedirect": "Redirect incoming HTTP requests to HTTPS",
"lang_httpsSettings": "HTTPS settings",
"lang_installAndRestart": "Installing certificate and restarting web server",
"lang_logoBackground": "Logo background color",
"lang_moduleHeading": "Web Interface",
"lang_msgAcmeFailed": "ACME failed",
- "lang_noHttps": "Disable HTTPS, delete current certificate",
- "lang_offSelected": "HTTPS is currently disabled.",
+ "lang_noHttps": "Delete current certificate, only use internal self-signed certificate",
"lang_optionAcme": "ACMEv2",
"lang_pageTitlePrefix": "Page title prefix",
"lang_passwordFields": "Password fields",
"lang_passwordsDescription": "Set whether password fields should be masked or not. The password field of the login page to the web interface is always masked.",
"lang_privateKey": "Please paste the private key belonging to the certificate here. It has to be in \"pem\" format too, which should look like this:",
- "lang_randomCert": "Generate new self-signed certificate",
"lang_redirectDomain": "Redirect browser to certificate's primary domain if accessed via unknown domain name or IP address",
"lang_regenerate": "(Re)generate",
"lang_showPasswords": "Show passwords",
diff --git a/modules-available/webinterface/page.inc.php b/modules-available/webinterface/page.inc.php
index a2123ac5..fb982616 100644
--- a/modules-available/webinterface/page.inc.php
+++ b/modules-available/webinterface/page.inc.php
@@ -45,12 +45,10 @@ class Page_WebInterface extends Page
$this->setRedirectFromPost();
$mode = Request::post('mode');
switch ($mode) {
+ case 'random':
case 'off':
$taskId = $this->setHttpsOff();
break;
- case 'random':
- $taskId = $this->setHttpsRandomCert();
- break;
case 'custom':
$taskId = $this->setHttpsCustomCert();
break;
@@ -67,7 +65,7 @@ class Page_WebInterface extends Page
}
if ($taskId !== null) {
Session::set('https-id', $taskId, 1);
- Util::redirect('?do=WebInterface&show=httpsupdate');
+ Util::redirect('?do=WebInterface&show=httpsupdate&mode=' . $mode);
}
Util::redirect('?do=WebInterface');
}
@@ -96,9 +94,17 @@ class Page_WebInterface extends Page
// HTTPS
//
if (Request::get('show') === 'httpsupdate') {
- Render::addTemplate('httpd-restart', array('taskid' => Session::get('https-id')));
+ Render::addTemplate('httpd-restart', [
+ 'taskid' => Session::get('https-id'),
+ 'mode' => Request::get('mode', '', 'string'),
+ ]);
}
$type = Property::get(WebInterface::PROP_TYPE);
+ if ($type === 'off') {
+ // Not really possible anymore to disable HTTPS since we use it for client communication
+ $type = 'generated';
+ Property::set(WebInterface::PROP_TYPE, $type);
+ }
$force = Property::get(WebInterface::PROP_REDIRECT) === 'True';
$hsts = Property::get(WebInterface::PROP_HSTS) === 'True';
$redirdomain = WebInterface::getDomainRedirect();
@@ -117,36 +123,19 @@ class Page_WebInterface extends Page
Render::addTemplate('acme-error', ['error' => $err]);
}
}
- if ($type === 'off') {
- if ($exists) {
- // HTTPS is set to off, but a certificate exists
- if ($https) {
- // User is using https, just warn to prevent lockout
- Message::addWarning('https-want-off-is-used');
- } else {
- // User is not using https, try to delete stray certificate
- $this->setHttpsOff();
- }
- } elseif ($https) {
- // Set to off, no cert found, but still using HTTPS apparently
- // Admin might have modified web server config in another way
- Message::addWarning('https-used-without-cert');
- }
- } elseif ($type === 'generated' || $type === 'supplied' || $type === 'acme' || $type === 'api') {
- $data['httpsEnabled'] = true;
+ if ($type === 'generated' || $type === 'supplied' || $type === 'acme' || $type === 'api') {
if ($force && !$https) {
Message::addWarning('https-want-redirect-is-plain');
}
- if (!$exists) {
+ if ($type !== 'generated' && !$exists) {
Message::addWarning('https-on-cert-missing');
}
} else {
// Unknown config - maybe upgraded old install that doesn't keep track
if ($exists || $https) {
$type = 'unknown'; // Legacy fallback
- $data['httpsEnabled'] = true;
} else {
- $type = 'off';
+ $type = 'generated';
}
}
$domains = implode("\n", Acme::getDomains());
@@ -171,23 +160,21 @@ class Page_WebInterface extends Page
// $type might have changed in above block
$data[$type . 'Selected'] = true;
// Show cert info if possible
- if ($type !== 'off') {
- $data['certDomains'] = [];
- $exp = 0;
- $iss = '';
- if (WebInterface::extractCurrentCertData($data['certDomains'], $exp, $iss)) {
- $data['certExpire'] = Util::prettyTime($exp);
- $data['certIssuer'] = $iss;
- $diff = $exp - time();
- $class = [];
- if ($diff < 86400 * 3) {
- $class[] = 'text-danger';
- }
- if ($diff < 86400 * 10) {
- $class[] = 'slx-bold';
- }
- $data['certExpireClass'] = implode(' ', $class);
+ $data['certDomains'] = [];
+ $exp = 0;
+ $iss = '';
+ if (WebInterface::extractCurrentCertData($data['certDomains'], $exp, $iss)) {
+ $data['certExpire'] = Util::prettyTime($exp);
+ $data['certIssuer'] = $iss;
+ $diff = $exp - time();
+ $class = [];
+ if ($diff < 86400 * 3) {
+ $class[] = 'text-danger';
+ }
+ if ($diff < 86400 * 10) {
+ $class[] = 'slx-bold';
}
+ $data['certExpireClass'] = implode(' ', $class);
}
$data['httpsApiKeyPostUrl'] = ($https ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/slx-admin/api.php?do=webinterface';
Permission::addGlobalTags($data['perms'], null, ['edit.https']);
@@ -238,16 +225,9 @@ class Page_WebInterface extends Page
private function setHttpsOff(): ?string
{
- Header('Strict-Transport-Security: max-age=0', true);
- Session::deleteCookie();
return WebInterface::tmDisableHttps();
}
- private function setHttpsRandomCert(): ?string
- {
- return WebInterface::tmGenerateRandomCert();
- }
-
private function setHttpsCustomCert(): ?string
{
$cert = trim(Request::post('certificate', Request::REQUIRED, 'string'));
@@ -257,7 +237,7 @@ class Page_WebInterface extends Page
$cert .= "\n" . $chain;
}
return WebInterface::tmImportCustomCert($key . "\n", $cert . "\n", 'supplied',
- 'New certificate uploaded by ' . User::getLogin());
+ 'Applying uploaded HTTPS certificate');
}
private function setAcmeMode(): ?string
diff --git a/modules-available/webinterface/templates/https.html b/modules-available/webinterface/templates/https.html
index b109fa9d..a93b8fb8 100644
--- a/modules-available/webinterface/templates/https.html
+++ b/modules-available/webinterface/templates/https.html
@@ -9,9 +9,6 @@
<p>{{lang_youreUsingHttps}}</p>
{{/httpsUsed}}
<div class="text-info slx-bold">
- {{#offSelected}}
- <p>{{lang_offSelected}}</p>
- {{/offSelected}}
{{#unknownSelected}}
<p>{{lang_unknownSelected}}</p>
{{/unknownSelected}}
@@ -111,7 +108,7 @@
</span>
</div>
- {{#httpsEnabled}}
+ {{^generatedSelected}}
<div class="input-group row-select">
<span class="input-group-addon">
<span class="radio">
@@ -123,19 +120,7 @@
{{lang_noHttps}}
</span>
</div>
- {{/httpsEnabled}}
-
- <div class="input-group row-select">
- <span class="input-group-addon">
- <span class="radio">
- <input id="mrandom" type="radio" name="mode" value="random" {{perms.edit.https.disabled}}>
- <label></label>
- </span>
- </span>
- <span class="form-control">
- {{lang_randomCert}}
- </span>
- </div>
+ {{/generatedSelected}}
<div class="input-group row-select">
<span class="input-group-addon">