summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2025-08-12 14:52:40 +0200
committerSimon Rettberg2025-08-12 14:52:40 +0200
commit32ec0505afae781a44466621ebbd8e83cf68fdb8 (patch)
treeb4f3db53319de6112bafaf99dd5999858e88e0e2
parent[locationinfo] Update HisInOne title cleanup regex (diff)
downloadslx-admin-32ec0505afae781a44466621ebbd8e83cf68fdb8.tar.gz
slx-admin-32ec0505afae781a44466621ebbd8e83cf68fdb8.tar.xz
slx-admin-32ec0505afae781a44466621ebbd8e83cf68fdb8.zip
[webinterface] Add support for HARICA and custom URLs
-rw-r--r--modules-available/webinterface/inc/acme.inc.php48
-rw-r--r--modules-available/webinterface/lang/de/messages.json1
-rw-r--r--modules-available/webinterface/lang/de/template-tags.json6
-rw-r--r--modules-available/webinterface/lang/en/messages.json1
-rw-r--r--modules-available/webinterface/lang/en/template-tags.json6
-rw-r--r--modules-available/webinterface/page.inc.php5
-rw-r--r--modules-available/webinterface/templates/https.html21
7 files changed, 80 insertions, 8 deletions
diff --git a/modules-available/webinterface/inc/acme.inc.php b/modules-available/webinterface/inc/acme.inc.php
index 3f5e76a0..bc26b7b2 100644
--- a/modules-available/webinterface/inc/acme.inc.php
+++ b/modules-available/webinterface/inc/acme.inc.php
@@ -9,15 +9,19 @@ class Acme
const PROP_HMAC_KEY = 'acme.hmac-key';
const PROP_DOMAINS = 'acme.domains';
const PROP_MAIL = 'acme.mail';
+ const PROP_CUSTOM_ACME_URL = 'acme.server-url';
const VALID_PROVIDERS = [
'letsencrypt' => "Let's Encrypt",
'zerossl' => 'ZeroSSL.com',
'buypass' => 'BuyPass.com',
- 'geant/sectigo' => 'GEANT via Sectigo',
+ //'geant/sectigo' => 'GEANT via Sectigo',
+ 'harica' => 'HARICA',
+ 'custom' => '...',
];
const PROVIDER_ALIASES = [
'geant/sectigo' => 'https://acme.sectigo.com/v2/GEANTOV',
+ 'harica' => 'https://acme.harica.gr/acme/directory',
];
public static function getLastError(): ?string
@@ -30,6 +34,11 @@ class Acme
return Property::get(self::PROP_PROVIDER, null);
}
+ public static function getServerUrl(): ?string
+ {
+ return Property::get(self::PROP_CUSTOM_ACME_URL, null);
+ }
+
public static function getKeyId(): ?string
{
return Property::get(self::PROP_KEY_ID, null);
@@ -58,9 +67,27 @@ class Acme
return explode(' ', Property::get(self::PROP_DOMAINS));
}
- public static function setConfig(string $provider, string $mail, ?string $keyId = null, ?string $hmacKey = null): bool
+ /**
+ * Sets the configuration to the specified provider with optional server URL and authentication keys.
+ *
+ * @param string $provider The provider identifier, either 'custom' or a key in the valid providers list.
+ * @param string $mail The email address associated with the provider.
+ * @param string|null $serverUrl The custom server URL for the provider, required for the 'custom' provider and must use HTTPS.
+ * @param string|null $keyId The optional key ID used for authentication.
+ * @param string|null $hmacKey The optional HMAC key for authentication.
+ *
+ * @return bool Returns true if the configuration is successfully set, false otherwise.
+ */
+ public static function setConfig(string $provider, string $mail, ?string $serverUrl = null,
+ ?string $keyId = null, ?string $hmacKey = null): bool
{
- if (!isset(self::VALID_PROVIDERS[$provider])) {
+ if ($provider === 'custom') {
+ if (substr($serverUrl, 0, 6) !== 'https:') {
+ Message::addError('webinterface.acme-invalid-url', $serverUrl);
+ return false;
+ }
+ Property::set(self::PROP_CUSTOM_ACME_URL, $serverUrl); // Only update if custom is selected
+ } elseif (!isset(self::VALID_PROVIDERS[$provider])) {
Message::addError('webinterface.acme-invalid-provider', $provider);
return false;
}
@@ -116,9 +143,18 @@ class Acme
}
}
+ /**
+ * Issues a new certificate using the configured ACME provider and other relevant details.
+ *
+ * @param bool $wipeAll Indicates whether all existing certificates and accounts should be wiped before issuing a new one.
+ * @return ?string The task ID of the certificate issuance process, or null if an error occurred.
+ */
public static function issueNewCertificate(bool $wipeAll = false): ?string
{
$provider = self::getProvider();
+ if ($provider === 'custom') {
+ $provider = Property::get(self::PROP_CUSTOM_ACME_URL, null);
+ }
if ($provider === null) {
Message::addError('webinterface.acme-no-provider');
return null;
@@ -148,6 +184,12 @@ class Acme
return $task['id'] ?? null;
}
+ /**
+ * Renews certificates based on available domains.
+ * This expects a valid configuration and existing account.
+ *
+ * @return ?string ID of the submitted task for the renewal process or null if no domains are available
+ */
public static function renew(): ?string
{
error_log("Renew called");
diff --git a/modules-available/webinterface/lang/de/messages.json b/modules-available/webinterface/lang/de/messages.json
index f1d0d6af..f87f3d3a 100644
--- a/modules-available/webinterface/lang/de/messages.json
+++ b/modules-available/webinterface/lang/de/messages.json
@@ -1,5 +1,6 @@
{
"acme-invalid-provider": "Ung\u00fcltiger ACME-Anbieter: {{0}}",
+ "acme-invalid-url": "Ung\u00fcltige URL: {{0}}",
"acme-no-domains": "Keine Domains angegeben",
"acme-no-mail": "Keine administrative Mailadresse angegeben",
"acme-no-provider": "Kein ACME-Anbieter ausgew\u00e4hlt",
diff --git a/modules-available/webinterface/lang/de/template-tags.json b/modules-available/webinterface/lang/de/template-tags.json
index ed7e9f00..ae174e91 100644
--- a/modules-available/webinterface/lang/de/template-tags.json
+++ b/modules-available/webinterface/lang/de/template-tags.json
@@ -1,12 +1,14 @@
{
- "lang_acmeCreateNewHint": "Hier k\u00f6nnen Sie ein Zertifikat via ACMEv2 erzeugen lassen. Daf\u00fcr ist es erforderlich, dass der Satellitenserver auf Port 80 erreichbar ist, genauer gesagt der Pfad \"\/.well-known\/acme-challenge\". Eine Ausnahme bietet hier GEANT\/Sectigo, welches durch die Verwendung eines Accounts an bestimmte Domains gebunden werden kann, und dann keine HTTP-Verifizierung erfordert.",
+ "lang_acmeCreateNewHint": "Hier k\u00f6nnen Sie ein Zertifikat via ACMEv2 erzeugen lassen. Daf\u00fcr ist es ggf. erforderlich, dass der Satellitenserver auf Port 80 erreichbar ist, genauer gesagt der Pfad \"\/.well-known\/acme-challenge\". Bei Verwendung einiger Anbieter f\u00e4llt diese Anforderung ggf. weg, falls mit Zugangsdaten gearbeitet wird.",
"lang_acmeDomains": "Anzufordernde Domains (eine pro Zeile)",
"lang_acmeHmacKey": "HMAC-Key (optional)",
"lang_acmeKeyId": "Key ID (optional)",
- "lang_acmeKidKeyHint": "Sofern der Anbieter die Verwendung eines Kontos (\"external account binding\") erfordert (GEANT), geben Sie hier die Daten in Form der Key ID und des HMAC-Keys ein.",
+ "lang_acmeKidKeyHint": "Sofern der Anbieter die Verwendung eines Kontos (\"external account binding\") erfordert (Harica\/GEANT), geben Sie hier die Daten in Form der Key ID und des HMAC-Keys ein.",
"lang_acmeMail": "Die E-Mail-Adresse des Zust\u00e4ndigen",
"lang_acmeProvider": "Zu verwendender Anbieter",
"lang_acmeSelected": "Das aktuelle Zertifikat wurde via ACME erstellt.",
+ "lang_acmeServerUrl": "ACME Server",
+ "lang_acmeServerUrlHint": "Die URL zum ACME-Endpoint des zu verwendenden ACME Service Providers.",
"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.",
diff --git a/modules-available/webinterface/lang/en/messages.json b/modules-available/webinterface/lang/en/messages.json
index f938552a..b4083ec3 100644
--- a/modules-available/webinterface/lang/en/messages.json
+++ b/modules-available/webinterface/lang/en/messages.json
@@ -1,5 +1,6 @@
{
"acme-invalid-provider": "Invalid ACME provider: {{0}}",
+ "acme-invalid-url": "Invalid URL: {{0}}",
"acme-no-domains": "No domains specified",
"acme-no-mail": "No technical mail contact specified",
"acme-no-provider": "No ACME provider selected",
diff --git a/modules-available/webinterface/lang/en/template-tags.json b/modules-available/webinterface/lang/en/template-tags.json
index b3afb43f..d282c582 100644
--- a/modules-available/webinterface/lang/en/template-tags.json
+++ b/modules-available/webinterface/lang/en/template-tags.json
@@ -1,12 +1,14 @@
{
- "lang_acmeCreateNewHint": "Here you can create a certificate using an ACMEv2 provider. This requires making this server accessible on port 80, more specifically the path \"\/.well-known\/acme-challenge\" needs to be accessible via HTTP. An exeption is GEANT\/Sectigo, which uses accounts that are verified for certain domains and don't require any verification via HTTP.",
+ "lang_acmeCreateNewHint": "Here you can create a certificate using an ACMEv2 provider. This usually requires making this server accessible on port 80, more specifically the path \"\/.well-known\/acme-challenge\" needs to be accessible via HTTP. When using a provider that requires access credentials, this requirement might not apply.",
"lang_acmeDomains": "Domains to request (one per line)",
"lang_acmeHmacKey": "HMAC-KEY (options)",
"lang_acmeKeyId": "Key ID (optional)",
- "lang_acmeKidKeyHint": "If the provider requires an account (\"external account binding\"), i.e. GEANT, please specify it here.",
+ "lang_acmeKidKeyHint": "If the provider requires an account (\"external account binding\"), i.e. Harica\/GEANT, please specify it here.",
"lang_acmeMail": "Technical contact e-mail address",
"lang_acmeProvider": "Provider to use",
"lang_acmeSelected": "Current certificate was generated via ACME.",
+ "lang_acmeServerUrl": "ACME server",
+ "lang_acmeServerUrlHint": "URL of ACME endpoint of SSL prodiver to use.",
"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.",
diff --git a/modules-available/webinterface/page.inc.php b/modules-available/webinterface/page.inc.php
index fb982616..880a67b8 100644
--- a/modules-available/webinterface/page.inc.php
+++ b/modules-available/webinterface/page.inc.php
@@ -153,6 +153,7 @@ class Page_WebInterface extends Page
$data['acmeMail'] = Acme::getMail();
$data['acmeDomains'] = $domains;
if (User::hasPermission("edit.https")) {
+ $data['acmeServerUrl'] = Acme::getServerUrl();
$data['acmeKeyId'] = Acme::getKeyId();
$data['acmeHmacKey'] = Acme::getHmacKey();
$data['httpsApiKey'] = WebInterface::getApiKey();
@@ -246,6 +247,7 @@ class Page_WebInterface extends Page
$wipeAll = Request::post('acme-wipe-all', false, 'bool');
// Get params
$provider = Request::post('acme-provider', Request::REQUIRED, 'string');
+ $serverUrl = Request::post('acme-server-url', null, 'string');
$mail = Request::post('acme-mail', Request::REQUIRED, 'string');
$domains = Request::post('acme-domains', Request::REQUIRED, 'string');
$kid = Request::post('acme-kid', null, 'string');
@@ -265,6 +267,7 @@ class Page_WebInterface extends Page
// First, try to revive existing config/certs if parameters didn't change
if (!$wipeAll
&& $provider === Acme::getProvider()
+ && ($provider !== 'custom' || $serverUrl === Acme::getServerUrl())
&& $mail === Acme::getMail()
&& $kid === Acme::getKeyId()
&& $hmac === Acme::getHmacKey()
@@ -274,7 +277,7 @@ class Page_WebInterface extends Page
return null; // Nothing to do, old setup works
return Acme::renew(); // Hope for the best, otherwise user needs to check "force reissue"
}
- if (!Acme::setConfig($provider, $mail, $kid, $hmac))
+ if (!Acme::setConfig($provider, $mail, $serverUrl, $kid, $hmac))
return null; // Will generate error messages in this case
Acme::setDomains($domains);
return Acme::issueNewCertificate($wipeAll);
diff --git a/modules-available/webinterface/templates/https.html b/modules-available/webinterface/templates/https.html
index a93b8fb8..0580965a 100644
--- a/modules-available/webinterface/templates/https.html
+++ b/modules-available/webinterface/templates/https.html
@@ -179,6 +179,27 @@ MIIFfTCCA...
{{/acmeProviders}}
</select>
</div>
+ <div class="form-group collapse" id="acme-server-url-group">
+ <label for="acme-server-url">{{lang_acmeServerUrl}}</label>
+ <input class="form-control" name="acme-server-url" id="acme-server-url" value="{{acmeServerUrl}}">
+ <i>{{lang_acmeServerUrlHint}}</i>
+ </div>
+ <script>
+ document.addEventListener('DOMContentLoaded', function () {
+ var $ap = $('#acme-provider');
+ var $serverUrlGroup = $('#acme-server-url-group');
+ var cf = function () {
+ var provider = $ap.val();
+ if (provider === 'custom') {
+ $serverUrlGroup.show();
+ } else {
+ $serverUrlGroup.hide();
+ }
+ };
+ $ap.change(cf);
+ cf();
+ });
+ </script>
<div class="form-group">
<label for="acme-mail">{{lang_acmeMail}}</label>
<input class="form-control" name="acme-mail" id="acme-mail" value="{{acmeMail}}">