security/acme-client: add support for new ACME CA's, closes #2361

This commit is contained in:
Frank Wall
2021-07-31 14:30:57 +02:00
parent b3d6071534
commit ac4febaa93
10 changed files with 120 additions and 27 deletions
+9
View File
@@ -8,6 +8,15 @@ WWW: https://github.com/acmesh-official/acme.sh
Plugin Changelog
================
3.0
Added:
* add support for new ACME CA's: buypass, buypass_test, sslcom, zerossl (#2361)
Changed:
* rename "Let's Encrypt Environment" to "ACME CA" (#2361)
* preserve old LE accounts/certs by adding a compatibility layer (#2361)
2.6
Added:
@@ -12,10 +12,10 @@
<help><![CDATA[Enable automatic renewal for certificates to prevent expiration. This will add a cron job to the system. You may want to customize the cron job schedule to your needs, because re-issueing a certificate may lead to a short downtime, depending on the selected challenge type and service.]]></help>
</field>
<field>
<id>acmeclient.settings.environment</id>
<label>Let's Encrypt Environment</label>
<id>acmeclient.settings.ca</id>
<label>ACME CA</label>
<type>dropdown</type>
<help><![CDATA[Choose Let's Encrypts staging environment when using it for the first time or while testing new challenge types. The staging environment offers <a href="https://letsencrypt.org/docs/staging-environment/">relaxed rate limits</a>.<br/><div class="text-info"><b>NOTE:</b>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.</div>]]></help>
<help><![CDATA[The ACME CA that should be used to issue or renew certificates. Note that some of them offer paid services and may require a subscription. Check the <a href="https://github.com/acmesh-official/acme.sh/wiki/Server">acme.sh documentation</a> for a list of supported CAs.]]></help>
</field>
<field>
<id>acmeclient.settings.haproxyIntegration</id>
@@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2020 Frank Wall
* Copyright (C) 2020-2021 Frank Wall
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -53,8 +53,8 @@ class LeAccount extends LeCommon
// Set log level
$this->setLoglevel();
// Set Let's Encrypt environment
$this->setEnvironment();
// Set ACME CA
$this->setCa();
// Store acme filenames
$this->acme_args[] = LeUtils::execSafe('--home %s', self::ACME_HOME_DIR);
@@ -66,7 +66,7 @@ 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';
@@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2020 Frank Wall
* Copyright (C) 2020-2021 Frank Wall
* Copyright (C) 2018 Deciso B.V.
* Copyright (C) 2018 Franco Fichtner <franco@opnsense.org>
* 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();
return true;
}
@@ -74,8 +74,8 @@ class LeCertificate extends LeCommon
// Set log level
$this->setLoglevel();
// Set Let's Encrypt environment
$this->setEnvironment();
// Set ACME CA
$this->setCa();
// Handle special key types
if ($this->config->keyLength == 'key_ec256' || $this->config->keyLength == 'key_ec384') {
@@ -528,7 +528,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
@@ -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
@@ -136,12 +137,29 @@ abstract class LeCommon
}
/**
* set Let's Encrypt environment for acme.sh
* set ACME CA for acme.sh
*/
public function setEnvironment()
public function setCa()
{
$this->environment = (string)$this->model->getNodeByReference('settings.environment');
$this->acme_args[] = $this->environment == 'stg' ? '--staging' : null;
$this->ca = (string)$this->model->getNodeByReference('settings.ca');
$this->acme_args[] = LeUtils::execSafe('--server %s', $this->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 ($this->ca) {
case 'letsencrypt':
$ca_compat = 'prod';
break;
case 'letsencrypt_test':
$ca_compat = 'stg';
break;
default:
$ca_compat = $this->ca;
break;
}
$this->ca_compat = $ca_compat;
}
/**
@@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2020 Frank Wall
* Copyright (C) 2020-2021 Frank Wall
* Copyright (C) 2018 Deciso B.V.
* Copyright (C) 2018 Franco Fichtner <franco@opnsense.org>
* 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();
// 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
@@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/AcmeClient</mount>
<version>2.1.0</version>
<version>3.0.0</version>
<description>A secure Let's Encrypt plugin</description>
<items>
<settings>
@@ -27,13 +27,25 @@
<Required>N</Required>
</UpdateCron>
<environment type="OptionField">
<Required>Y</Required>
<Required>N</Required>
<default>prod</default>
<OptionValues>
<prod>Production Environment [default]</prod>
<stg>Staging Environment</stg>
</OptionValues>
</environment>
<ca type="OptionField">
<Required>Y</Required>
<default></default>
<OptionValues>
<buypass>Buypass</buypass>
<buypass_test>Buypass Test CA</buypass_test>
<letsencrypt>Let's Encrypt [default]</letsencrypt>
<letsencrypt_test>Let's Encrypt Test CA</letsencrypt_test>
<sslcom>SSL.com</sslcom>
<zerossl>ZeroSSL</zerossl>
</OptionValues>
</ca>
<challengePort type="IntegerField">
<default>43580</default>
<MinimumValue>1024</MinimumValue>
@@ -0,0 +1,54 @@
<?php
/**
* Copyright (C) 2021 Frank Wall
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
namespace OPNsense\AcmeClient\Migrations;
use OPNsense\Base\BaseModelMigration;
class M3_0_0 extends BaseModelMigration
{
public function run($model)
{
// Get old LE environment and map to new value
$env = (string)$model->settings->environment;
switch ($env) {
case 'prod':
$new_ca = 'letsencrypt';
break;
case 'stg':
$new_ca = 'letsencrypt_test';
break;
}
// Set new CA
$model->settings->ca = $new_ca;
$model->settings->environment = null; // clear old value
}
}
@@ -1,6 +1,6 @@
{#
Copyright (C) 2017-2019 Frank Wall
Copyright (C) 2017-2021 Frank Wall
OPNsense® is Copyright © 2014-2015 by Deciso B.V.
All rights reserved.
@@ -247,7 +247,7 @@ POSSIBILITY OF SUCH DAMAGE.
<br/>
</div>
<div class="col-md-12">
<b>{{ lang._("Please read the official %sLet's Encrypt documentation%s before using this plugin. Otherwise you will easily hit its %srate limits%s and thus all your attempts to issue a certificate will fail.") | format('<a href="https://letsencrypt.org/how-it-works/">', '</a>', '<a href="https://letsencrypt.org/docs/rate-limits/">', '</a>') }}</b>{{ lang._("Please use Let's Encrypt's %sstaging servers%s when using this plugin for the first time or while testing a new challenge type. You will have to reissue your certificates when switching from staging to production servers to get valid certificates.") | format('<a href="https://letsencrypt.org/docs/staging-environment/">', '</a>') }}
<b>{{ lang._("Please read the official %sLet's Encrypt documentation%s before using this plugin. It should give you a good overview about how the various ACME CAs work, so you do not hit their %srate limits%s and avoid common misconfigurations, which would let all your attempts to issue a certificate fail.") | format('<a href="https://letsencrypt.org/how-it-works/">', '</a>', '<a href="https://letsencrypt.org/docs/rate-limits/">', '</a>') }}</b>{{ lang._("Please use a %stest CA%s when using this plugin for the first time or while testing a new challenge type. Note that you will have to reissue your certificates when switching from a test to a production CA to get valid certificates.") | format('<a href="https://letsencrypt.org/docs/staging-environment/">', '</a>') }}
<br/>
{{ lang._('Please use the %sissue tracker%s to report bugs or request new features.') | format('<a href="https://github.com/opnsense/plugins/issues">', '</a>') }}
<br/>