diff --git a/security/acme-client/Makefile b/security/acme-client/Makefile index 97ee0b6ea..aa8f7cc86 100644 --- a/security/acme-client/Makefile +++ b/security/acme-client/Makefile @@ -1,6 +1,6 @@ PLUGIN_NAME= acme-client -PLUGIN_VERSION= 2.6 -PLUGIN_COMMENT= Let's Encrypt client +PLUGIN_VERSION= 3.0 +PLUGIN_COMMENT= ACME Client PLUGIN_MAINTAINER= opnsense@moov.de PLUGIN_DEPENDS= acme.sh py${PLUGIN_PYTHON}-dns-lexicon diff --git a/security/acme-client/pkg-descr b/security/acme-client/pkg-descr index 0f08eea38..a78db9bed 100644 --- a/security/acme-client/pkg-descr +++ b/security/acme-client/pkg-descr @@ -8,6 +8,30 @@ WWW: https://github.com/acmesh-official/acme.sh Plugin Changelog ================ +3.0 + +Added: +* add support for new ACME CAs: buypass, buypass_test, sslcom, zerossl (#2361) +* add CA setting to accounts, make it possible to use multiple CAs +* add introduction pages and an option to hide them +* add tooltips for account command buttons (#2188) +* add support for custom ACME EAB kid/hmac when registering accounts + +Fixed: +* properly set/get the UUID of LE objects + +Changed: +* rename plugin from "Let's Encrypt client" to "ACME Client" (#2361) +* change the suffix for imports to the certificate storage to "ACME Client" (#2361) +* rename "Let's Encrypt Environment" to "ACME CA" and move to account settings (#2361) +* preserve old LE accounts/certs by adding a compatibility layer (#2361) +* update tooltip style for 21.7 (#2188) +* show more options in list view for challenge types and automations + +Removed: +* remove the legacy log file and only rely on syslog logging (#2366) +* remove obsolete account parameters: certificateAuthority, lastUpdate + 2.6 Added: diff --git a/security/acme-client/src/etc/inc/plugins.inc.d/acmeclient.inc b/security/acme-client/src/etc/inc/plugins.inc.d/acmeclient.inc index 801a1fb07..92b8881e2 100644 --- a/security/acme-client/src/etc/inc/plugins.inc.d/acmeclient.inc +++ b/security/acme-client/src/etc/inc/plugins.inc.d/acmeclient.inc @@ -1,7 +1,7 @@ gettext('Let\'s Encrypt client'), + 'description' => gettext('ACME client'), 'pidfile' => '/var/run/lighttpd-acme-challenge.pid', 'configd' => array( 'restart' => array('acme-http-challenge restart'), diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/AccountsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/AccountsController.php index 374112c23..202b578fd 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/AccountsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/AccountsController.php @@ -1,7 +1,7 @@ view->formDialogAccount = $this->getForm("dialogAccount"); + // set additional view parameters + $mdlAcme = new \OPNsense\AcmeClient\AcmeClient(); + $this->view->showIntro = (string)$mdlAcme ->settings->showIntro; // choose template $this->view->pick('OPNsense/AcmeClient/accounts'); } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ActionsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ActionsController.php index 002446342..fb438bff6 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ActionsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ActionsController.php @@ -1,7 +1,7 @@ view->formDialogAction = $this->getForm("dialogAction"); + // set additional view parameters + $mdlAcme = new \OPNsense\AcmeClient\AcmeClient(); + $this->view->showIntro = (string)$mdlAcme ->settings->showIntro; // choose template $this->view->pick('OPNsense/AcmeClient/actions'); } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ActionsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ActionsController.php index 0dec0e4b9..2634c404d 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ActionsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ActionsController.php @@ -72,7 +72,7 @@ class ActionsController extends ApiMutableModelControllerBase public function searchAction() { - return $this->searchBase('actions.action', array('enabled', 'name', 'description'), 'name'); + return $this->searchBase('actions.action', array('enabled', 'name', 'type', 'description'), 'name'); } public function sftpGetIdentityAction() diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/SettingsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/SettingsController.php index 3dee7cfb2..c8bef9805 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/SettingsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/SettingsController.php @@ -1,7 +1,7 @@ newAcl( "find_acme_challenge", - "Added by Let's Encrypt plugin", + "Added by ACME Client plugin", "path_beg", "0", array("path_beg" => "/.well-known/acme-challenge/") @@ -273,7 +273,7 @@ class SettingsController extends ApiMutableModelControllerBase $backend_uuid = $mdlHAProxy->newBackend( "1", "acme_challenge_backend", - "Added by Let's Encrypt plugin", + "Added by ACME Client plugin", "http", "source", "", @@ -283,7 +283,7 @@ class SettingsController extends ApiMutableModelControllerBase // Add a new HAProxy action $action_uuid = $mdlHAProxy->newAction( "redirect_acme_challenges", - "Added by Let's Encrypt plugin", + "Added by ACME Client plugin", "if", "", "and", @@ -298,7 +298,7 @@ class SettingsController extends ApiMutableModelControllerBase // Add a new HAProxy server $server_uuid = $mdlHAProxy->newServer( "acme_challenge_host", - "Added by Let's Encrypt plugin", + "Added by ACME Client plugin", "127.0.0.1", $acme_port, "active", diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ValidationsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ValidationsController.php index 8dd689313..703a8fef3 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ValidationsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/Api/ValidationsController.php @@ -73,6 +73,6 @@ class ValidationsController extends ApiMutableModelControllerBase public function searchAction() { - return $this->searchBase('validations.validation', array('enabled', 'name', 'description'), 'name'); + return $this->searchBase('validations.validation', array('enabled', 'name', 'method', 'description'), 'name'); } } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/CertificatesController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/CertificatesController.php index 8e58c37ef..df9b8fd53 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/CertificatesController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/CertificatesController.php @@ -1,7 +1,7 @@ view->formDialogCertificate = $this->getForm("dialogCertificate"); + // set additional view parameters + $mdlAcme = new \OPNsense\AcmeClient\AcmeClient(); + $this->view->showIntro = (string)$mdlAcme ->settings->showIntro; // choose template $this->view->pick('OPNsense/AcmeClient/certificates'); } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/IndexController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/IndexController.php index f13b7e9c1..59a0f966c 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/IndexController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/IndexController.php @@ -1,7 +1,7 @@ view->settingsForm = $this->getForm("settings"); + // set additional view parameters + $mdlAcme = new \OPNsense\AcmeClient\AcmeClient(); + $this->view->showIntro = (string)$mdlAcme ->settings->showIntro; // pick the template to serve $this->view->pick('OPNsense/AcmeClient/settings'); } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ValidationsController.php b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ValidationsController.php index 8c2f2a365..32a8e613b 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ValidationsController.php +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/ValidationsController.php @@ -1,7 +1,7 @@ view->formDialogValidation = $this->getForm("dialogValidation"); + // set additional view parameters + $mdlAcme = new \OPNsense\AcmeClient\AcmeClient(); + $this->view->showIntro = (string)$mdlAcme ->settings->showIntro; // choose template $this->view->pick('OPNsense/AcmeClient/validations'); } diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogAccount.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogAccount.xml index 2800717bd..33854114a 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogAccount.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogAccount.xml @@ -17,6 +17,10 @@ text Description for this account. + + + header + account.email @@ -24,10 +28,25 @@ Optional e-mail address for this account. - account.certificateAuthority - + account.ca + dropdown - - true + acme.sh documentation for a list of supported CAs.]]> + + + + header + + + account.eab_kid + + text + + + + account.eab_hmac + + password + diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogCertificate.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogCertificate.xml index ee0eb8daf..a78b2f051 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogCertificate.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogCertificate.xml @@ -27,24 +27,24 @@ select_multiple true -
NOTE:You need to forcefully re-issue the certificate if you change "Alt Names" after the certificate was signed by the Let's Encrypt Authority! Use the "issue" button in the Commands column in this case.
]]>
+
NOTE:You need to forcefully re-issue the certificate if you change "Alt Names" after the certificate was signed by the ACME CA! Use the "issue" button in the Commands column in this case.
]]>
Enter FQDN here. Finish with TAB. - + header certificate.account dropdown - + certificate.validationMethod dropdown - + certificate.autoRenewal diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml index 527601cfd..4faa6b778 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/dialogValidation.xml @@ -21,7 +21,7 @@ validation.method dropdown - Set the Let's Encrypt challenge type. You'll have to add configuration for the selected challenge type below. + Set the ACME challenge type. You'll have to add configuration for the selected challenge type below. @@ -48,7 +48,7 @@ validation.http_opn_interface dropdown -
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
+
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
validation.http_opn_ipaddresses @@ -56,7 +56,7 @@ select_multiple true -
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
+
NOTE:This will ONLY work if the official IP addresses are LOCALLY configured on your OPNsense firewall.
]]>
Enter IP addresses here. Finish each with TAB.
diff --git a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml index ad0247d9e..4089801a0 100644 --- a/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml +++ b/security/acme-client/src/opnsense/mvc/app/controllers/OPNsense/AcmeClient/forms/settings.xml @@ -3,7 +3,7 @@ acmeclient.settings.enabled checkbox - + acmeclient.settings.autoRenewal @@ -11,12 +11,6 @@ checkbox - - acmeclient.settings.environment - - dropdown - relaxed rate limits.
NOTE:Certificates signed by the staging environment are NOT valid. You need to forcefully re-sign (or delete and re-create) them after switching from staging to production environment.
]]>
-
acmeclient.settings.haproxyIntegration @@ -29,11 +23,17 @@ dropdown + + acmeclient.settings.showIntro + + checkbox + + acmeclient.settings.challengePort text - + true diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAccount.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAccount.php index dc8f0d33f..94a91008d 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAccount.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAccount.php @@ -1,7 +1,7 @@ setLoglevel(); - // Set Let's Encrypt environment - $this->setEnvironment(); + // Set ACME CA + $this->setCa($uuid); // Store acme filenames $this->acme_args[] = LeUtils::execSafe('--home %s', self::ACME_HOME_DIR); @@ -66,14 +66,14 @@ class LeAccount extends LeCommon public function generateKey() { // Collect account information - $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . (string)$this->config->id . '_' . $this->environment; + $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . (string)$this->config->id . '_' . $this->ca_compat; $account_conf_file = $account_conf_dir . '/account.conf'; $account_key_file = $account_conf_dir . '/account.key'; $account_json_file = $account_conf_dir . '/account.json'; $account_ca_file = $account_conf_dir . '/ca.conf'; $acme_conf = array(); $acme_conf[] = "CERT_HOME='" . self::ACME_HOME_DIR . "'"; - $acme_conf[] = "LOG_FILE='" . self::ACME_LOG_FILE . "'"; + $acme_conf[] = "SYS_LOG='" . $this->acme_syslog . "'"; $acme_conf[] = "ACCOUNT_KEY_PATH='" . $account_key_file . "'"; $acme_conf[] = "ACCOUNT_JSON_PATH='" . $account_json_file . "'"; $acme_conf[] = "CA_CONF='" . $account_ca_file . "'"; @@ -192,7 +192,7 @@ class LeAccount extends LeCommon } /** - * register account with Let's Encrypt + * register account with configured ACME CA * @return bool */ public function register() @@ -212,6 +212,13 @@ class LeAccount extends LeCommon if (!($this->isRegistered())) { LeUtils::log_debug('starting account registration for ' . (string)$this->config->name, $this->debug); + // Check if ACME External Account Binding (EAB) is enabled + if (!empty((string)$this->config->eab_kid) && !empty((string)$this->config->eab_hmac)) { + LeUtils::log_debug('enabling ACME EAB for this account', $this->debug); + $this->acme_args[] = LeUtils::execSafe('--eab-kid %s', $this->config->eab_kid); + $this->acme_args[] = LeUtils::execSafe('--eab-hmac-key %s', $this->config->eab_hmac); + } + // Preparation to run acme client $proc_env = $this->acme_env; // env variables for proc_open() $proc_env['PATH'] = $this::ACME_ENV_PATH; diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomation/Base.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomation/Base.php index b6a07a727..349e22bff 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomation/Base.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomation/Base.php @@ -1,7 +1,7 @@ * All rights reserved. @@ -63,8 +63,8 @@ abstract class Base extends \OPNsense\AcmeClient\LeCommon // Set log level $this->setLoglevel(); - // Set Let's Encrypt environment - $this->setEnvironment(); + // Set ACME CA + $this->setCa($accountuuid); return true; } diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomationInterface.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomationInterface.php index 707b65aff..30607fd80 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomationInterface.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeAutomationInterface.php @@ -1,7 +1,7 @@ setLoglevel(); - // Set Let's Encrypt environment - $this->setEnvironment(); + // Set ACME CA + $this->setCa((string)$this->config->account); // Handle special key types if ($this->config->keyLength == 'key_ec256' || $this->config->keyLength == 'key_ec384') { @@ -169,7 +169,7 @@ class LeCertificate extends LeCommon // Collect required CA information $ca_cn = LeUtils::local_cert_get_cn($ca_content, false); - $ca['descr'] = (string)$ca_cn . ' (Let\'s Encrypt)'; + $ca['descr'] = (string)$ca_cn . ' (ACME Client)'; // Prepare CA for import LeUtils::local_ca_import($ca, $ca_content); @@ -186,7 +186,7 @@ class LeCertificate extends LeCommon } } else { // Create new CA - LeUtils::log("importing Let's Encrypt CA: ${ca_cn}"); + LeUtils::log("importing ACME CA: ${ca_cn}"); $newca = Config::getInstance()->object()->addChild('ca'); foreach (array_keys($ca) as $cacfg) { $newca->addChild($cacfg, (string)$ca[$cacfg]); @@ -251,7 +251,7 @@ class LeCertificate extends LeCommon // Collect required cert information $cert_cn = LeUtils::local_cert_get_cn($cert_content, false); - $cert['descr'] = (string)$cert_cn . ' (Let\'s Encrypt)'; + $cert['descr'] = (string)$cert_cn . ' (ACME Client)'; $cert['refid'] = $cert_refid; // Prepare certificate for import @@ -277,7 +277,7 @@ class LeCertificate extends LeCommon $newcert->addChild($certcfg, (string)$cert[$certcfg]); } } - LeUtils::log("${import_log_message} Let's Encrypt X.509 certificate: ${cert_cn}"); + LeUtils::log("${import_log_message} ACME X.509 certificate: ${cert_cn}"); /** * Step 3: update configuration @@ -300,7 +300,7 @@ class LeCertificate extends LeCommon } /** - * check if certificate is already issued by Let's Encrypt + * check if certificate is already issued by ACME CA * @return bool */ public function isIssued() @@ -351,6 +351,7 @@ class LeCertificate extends LeCommon return false; } LeUtils::log("${acme_action} certificate: " . (string)$this->config->name); + LeUtils::log('using CA: ' . $this->ca); // Ensure that account is registered. if (!($this->setAccount())) { @@ -528,7 +529,7 @@ class LeCertificate extends LeCommon LeUtils::log('revoking certificate: ' . (string)$this->config->name); // Collect account information - $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . $this->account_id . '_' . $this->environment; + $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . $this->account_id . '_' . $this->ca_compat; $account_conf_file = $account_conf_dir . '/account.conf'; // Preparation to run acme client diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeCommon.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeCommon.php index 151445d0c..d2097fe7a 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeCommon.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeCommon.php @@ -32,7 +32,7 @@ use OPNsense\Core\Config; use OPNsense\AcmeClient\LeUtils; /** - * Common constants and functions for all Let's Encrypt classes + * Common constants and functions for all ACME classes * @package OPNsense\AcmeClient */ abstract class LeCommon @@ -42,7 +42,6 @@ abstract class LeCommon public const ACME_BASE_CERT_DIR = '/var/etc/acme-client/certs'; public const ACME_BASE_CONFIG_DIR = '/var/etc/acme-client/configs'; public const ACME_HOME_DIR = '/var/etc/acme-client/home'; - public const ACME_LOG_FILE = '/var/log/acme.sh.log'; // Defaults for acme.sh public const ACME_ACCOUNT_KEY_LENGTH = 4096; @@ -61,6 +60,7 @@ abstract class LeCommon protected $acme_args = array(); # command line arguments to be passed to acme.sh protected $acme_env = array(); # environment variables to be used when running acme.sh protected $acme_keylength; # private key length in acme.sh compatible format + protected $acme_syslog; # syslog log level // Certificate details and configuration protected $cert_id; # AcmeClient certificate object ID @@ -83,7 +83,8 @@ abstract class LeCommon protected $cron; # Run from cron job protected $config; # AcmeClient config object protected $debug; # Debug logging (bool) - protected $environment; # Let's Encrypt environment (uses shortnames) + protected $ca; # ACME CA + protected $ca_compat; # ACME CA for compat with old LE CA names protected $force; # Force operation protected $model; # AcmeClient model object protected $uuid; # AcmeClient config object uuid @@ -104,7 +105,7 @@ abstract class LeCommon */ public function getUuid() { - return (string)$this->config->uuid; + return (string)$this->uuid; } /** @@ -123,6 +124,7 @@ abstract class LeCommon // Store config objects $this->config = $obj; $this->model = $model; + $this->uuid = $uuid; return true; } @@ -136,12 +138,41 @@ abstract class LeCommon } /** - * set Let's Encrypt environment for acme.sh + * set ACME CA for acme.sh */ - public function setEnvironment() + public function setCa(string $uuid) { - $this->environment = (string)$this->model->getNodeByReference('settings.environment'); - $this->acme_args[] = $this->environment == 'stg' ? '--staging' : null; + // Get account config object + $model = new \OPNsense\AcmeClient\AcmeClient(); + $obj = $model->getNodeByReference("accounts.account.${uuid}"); + if (empty($obj) || $obj == null) { + LeUtils::log_error("unable to set CA, account not found: ${uuid}"); + return false; + } + + // Extract ACME CA from account config + $acme_ca = (string)$obj->ca; + $this->ca = $acme_ca; + + // Add CA to acme arguments + $this->acme_args[] = LeUtils::execSafe('--server %s', $acme_ca); + + // Evaluate how the CA should be represented in filenames. + // This is a compatibility layer. It ensures that old files that + // were generated for the Let's Encrypt Production/Staging CA + // can still be used. + switch ($acme_ca) { + case 'letsencrypt': + $ca_compat = 'prod'; + break; + case 'letsencrypt_test': + $ca_compat = 'stg'; + break; + default: + $ca_compat = $acme_ca; + break; + } + $this->ca_compat = $ca_compat; } /** @@ -155,34 +186,34 @@ abstract class LeCommon case 'extended': $this->acme_args[] = '--syslog 6'; $this->acme_args[] = '--log-level 2'; + $this->acme_syslog = 6; $this->debug = false; break; case 'debug': $this->acme_args[] = '--syslog 7'; $this->acme_args[] = '--debug'; + $this->acme_syslog = 7; $this->debug = true; break; case 'debug2': $this->acme_args[] = '--syslog 7'; $this->acme_args[] = '--debug 2'; + $this->acme_syslog = 7; $this->debug = true; break; case 'debug3': $this->acme_args[] = '--syslog 7'; $this->acme_args[] = '--debug 3'; + $this->acme_syslog = 7; $this->debug = true; break; default: $this->acme_args[] = '--syslog 6'; $this->acme_args[] = '--log-level 1'; + $this->acme_syslog = 6; $this->debug = false; break; } - - // Set log file - // NOTE: This log file is no longer exposed to the GUI. However, it may - // still turn out to be useful for debug purposes in rare egde cases. - $this->acme_args[] = LeUtils::execSafe('--log %s', self::ACME_LOG_FILE); } /** diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php index a69443228..d50292c66 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidation/Base.php @@ -1,7 +1,7 @@ * All rights reserved. @@ -73,8 +73,8 @@ abstract class Base extends \OPNsense\AcmeClient\LeCommon // Set log level $this->setLoglevel(); - // Set Let's Encrypt environment - $this->setEnvironment(); + // Set ACME CA + $this->setCa($accountuuid); // Store acme hook switch ((string)$this->config->method) { @@ -141,8 +141,8 @@ abstract class Base extends \OPNsense\AcmeClient\LeCommon } } - // Use individual account config for each environment - $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . $this->account_id . '_' . $this->environment; + // Use individual account config for each CA + $account_conf_dir = self::ACME_BASE_ACCOUNT_DIR . '/' . $this->account_id . '_' . $this->ca_compat; $account_conf_file = $account_conf_dir . '/account.conf'; // Preparation to run acme client diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationInterface.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationInterface.php index 903486ffa..86c6be451 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationInterface.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/LeValidationInterface.php @@ -1,7 +1,7 @@ - - Services: Let's Encrypt + + Services: ACME Client ui/acmeclient/* api/acmeclient/* - + diff --git a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml index 29a998e7b..47d3ba221 100644 --- a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml +++ b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/AcmeClient.xml @@ -1,7 +1,7 @@ //OPNsense/AcmeClient - 2.1.0 - A secure Let's Encrypt plugin + 3.0.0 + A secure ACME Client plugin @@ -27,7 +27,7 @@ N - Y + N prod Production Environment [default] @@ -109,6 +109,10 @@ debug 3 + + Y + 1 + @@ -132,21 +136,32 @@ N - + Y letsencrypt - Let's Encrypt CA + Buypass + Buypass Test CA + Let's Encrypt [default] + Let's Encrypt Test CA + SSL.com + ZeroSSL - + + + N + /^.{1,8192}$/u + Should be a string between 1 and 8192 characters. + + + N + /^.{1,8192}$/u + Should be a string between 1 and 8192 characters. + N - - - N - N @@ -1019,7 +1034,7 @@ Restart Nginx (OPNsense plugin) Upload certificate to Highwinds CDN Upload certificate via SFTP - System or Plugin Command (select below) + System or Plugin Command diff --git a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Menu/Menu.xml b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Menu/Menu.xml index 33a92cfb5..07b758fd0 100644 --- a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Menu/Menu.xml +++ b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Menu/Menu.xml @@ -1,7 +1,6 @@ - - + @@ -13,6 +12,6 @@ - + diff --git a/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Migrations/M3_0_0.php b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Migrations/M3_0_0.php new file mode 100644 index 000000000..1ef4c2f66 --- /dev/null +++ b/security/acme-client/src/opnsense/mvc/app/models/OPNsense/AcmeClient/Migrations/M3_0_0.php @@ -0,0 +1,57 @@ +settings->environment; + switch ($env) { + case 'prod': + $new_ca = 'letsencrypt'; + break; + case 'stg': + $new_ca = 'letsencrypt_test'; + break; + } + $model->settings->environment = null; // clear old value + + // Search accounts + foreach ($model->getNodeByReference('accounts.account')->iterateItems() as $account) { + // Set CA + $account->ca = $new_ca; + } + } +} diff --git a/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/accounts.volt b/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/accounts.volt index 7a95eef4b..dfa7eb6cf 100644 --- a/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/accounts.volt +++ b/security/acme-client/src/opnsense/mvc/app/views/OPNsense/AcmeClient/accounts.volt @@ -1,6 +1,6 @@ {# -Copyright (C) 2017 Frank Wall +Copyright (C) 2017-2021 Frank Wall OPNsense® is Copyright © 2014-2015 by Deciso B.V. All rights reserved. @@ -53,10 +53,10 @@ POSSIBILITY OF SUCH DAMAGE. url: '/api/acmeclient/accounts/search', formatters: { "commands": function (column, row) { - return " " + - "" + - "" + - ""; + return " " + + "" + + "" + + ""; }, "rowtoggle": function (column, row) { if (parseInt(row[column.id], 2) == 1) { @@ -117,6 +117,9 @@ POSSIBILITY OF SUCH DAMAGE. */ var grid_accounts = $("#grid-accounts").bootgrid(gridopt).on("loaded.rs.jquery.bootgrid", function (e) { + // toggle all rendered tooltips (once for all) + $('.bootgrid-tooltip').tooltip(); + // scale footer on resize $(this).find("tfoot td:first-child").attr('colspan',$(this).find("th").length - 1); $(this).find('tr[data-row-id]').each(function(){ @@ -300,7 +303,7 @@ POSSIBILITY OF SUCH DAMAGE. if (gridParams['register'] != undefined) { var uuid=$(this).data("row-id"); stdDialogConfirm('{{ lang._('Confirmation Required') }}', - '{{ lang._('Register the selected account with Lets Encrypt?') }}', + '{{ lang._('Register the selected account with the configured ACME CA?') }}', '{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() { ajaxCall(url=gridParams['register'] + uuid,sendData={},callback=function(data,status){ // reload grid afterwards @@ -318,12 +321,28 @@ POSSIBILITY OF SUCH DAMAGE. -