Merge pull request #1399 from fraenki/acme_125

security/acme-client: release 1.24 (additions)
This commit is contained in:
Frank Wall
2019-07-11 23:42:28 +02:00
committed by GitHub
5 changed files with 155 additions and 11 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
PLUGIN_NAME= acme-client
PLUGIN_VERSION= 1.23
PLUGIN_VERSION= 1.24
PLUGIN_COMMENT= Let's Encrypt client
PLUGIN_MAINTAINER= opnsense@moov.de
PLUGIN_DEPENDS= acme.sh
@@ -62,6 +62,16 @@ class CertificatesController extends ApiMutableModelControllerBase
public function delAction($uuid)
{
# Remove the cert from list of certs known to acme.sh.
$mdlAcme = new AcmeClient();
if ($uuid != null) {
$node = $mdlAcme->getNodeByReference('certificates.certificate.' . $uuid);
if ($node != null) {
$cert_id = $node->id;
$backend = new Backend();
$response = $backend->configdRun("acmeclient remove-cert {$cert_id}");
}
}
return $this->delBase('certificates.certificate', $uuid);
}
@@ -99,6 +109,26 @@ class CertificatesController extends ApiMutableModelControllerBase
return $result;
}
/**
* remove private key from certificate by uuid
* @param $uuid item unique id
* @return array status
*/
public function removekeyAction($uuid)
{
$result = array("result"=>"failed");
$mdlAcme = new AcmeClient();
if ($uuid != null) {
$node = $mdlAcme->getNodeByReference('certificates.certificate.' . $uuid);
if ($node != null) {
$cert_id = $node->id;
$backend = new Backend();
$response = $backend->configdRun("acmeclient remove-key {$cert_id}");
}
}
return $result;
}
/**
* revoke certificate by uuid
* @param $uuid item unique id
@@ -47,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
toggle:'/api/acmeclient/certificates/toggle/',
sign:'/api/acmeclient/certificates/sign/',
revoke:'/api/acmeclient/certificates/revoke/',
removekey:'/api/acmeclient/certificates/removekey/',
};
var gridopt = {
@@ -61,7 +62,8 @@ POSSIBILITY OF SUCH DAMAGE.
"<button type=\"button\" class=\"btn btn-xs btn-default command-copy\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-clone\"></span></button>" +
"<button type=\"button\" class=\"btn btn-xs btn-default command-delete\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-trash-o\"></span></button>" +
"<button type=\"button\" class=\"btn btn-xs btn-default command-sign\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-repeat\"></span></button>" +
"<button type=\"button\" class=\"btn btn-xs btn-default command-revoke\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-power-off\"></span></button>";
"<button type=\"button\" class=\"btn btn-xs btn-default command-revoke\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-power-off\"></span></button>" +
"<button type=\"button\" class=\"btn btn-xs btn-default command-removekey\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-history\"></span></button>";
},
"rowtoggle": function (column, row) {
if (parseInt(row[column.id], 2) == 1) {
@@ -314,7 +316,7 @@ POSSIBILITY OF SUCH DAMAGE.
});
// sign cert
// TODO: this should block other sign/revoke actions
// TODO: this should block other acme.sh actions
grid_certificates.find(".command-sign").on("click", function(e)
{
if (gridParams['sign'] != undefined) {
@@ -336,7 +338,7 @@ POSSIBILITY OF SUCH DAMAGE.
});
// revoke cert
// TODO: this should block other sign/revoke actions
// TODO: this should block other acme.sh actions
grid_certificates.find(".command-revoke").on("click", function(e)
{
if (gridParams['revoke'] != undefined) {
@@ -355,6 +357,26 @@ POSSIBILITY OF SUCH DAMAGE.
}
});
// remove private key
// TODO: this should block other acme.sh actions
grid_certificates.find(".command-removekey").on("click", function(e)
{
if (gridParams['removekey'] != undefined) {
var uuid=$(this).data("row-id");
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
'{{ lang._('Really remove the private key?%s%sThe certificate will be completely reset. This is useful when the private key has been compromised or when you have changed the key options and want to regenerate the private key.%sNote that you have to revalidate the certificate afterwards in order to create a new private key and a matching certificate.') | format('<br/>', '<br/>', '<br/>') }}',
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
ajaxCall(url=gridParams['removekey'] + uuid,
sendData={},callback=function(data,status){
// reload grid after sign
$("#"+gridId).bootgrid("reload");
});
}, 'danger');
} else {
console.log("[grid] action removekey missing")
}
});
});
/***********************************************************************
@@ -363,7 +385,7 @@ POSSIBILITY OF SUCH DAMAGE.
/**
* Sign or renew ALL certificates
* TODO: this should block other sign/revoke actions
* TODO: this should block other acme.sh actions
*/
$("#signallcertsAct").click(function(){
//$("#signallcertsAct_progress").addClass("fa fa-spinner fa-pulse");
@@ -77,20 +77,25 @@ if (isset($options["C"])) {
// Run the specified action
switch ($options["a"]) {
case 'sign':
//$result = sign_or_renew_cert($options["c"]);
$result = cert_action_validator($options["c"]);
echo json_encode(array('status'=>$result));
break;
case 'renew':
//$result = sign_or_renew_cert($options["c"]);
$result = cert_action_validator($options["c"]);
echo json_encode(array('status'=>$result));
break;
case 'remove':
$result = cert_action_validator($options["c"]);
echo json_encode(array('status'=>$result));
break;
case 'removekey':
$result = cert_action_validator($options["c"]);
echo json_encode(array('status'=>$result));
break;
case 'revoke':
//$result = revoke_cert($options["c"]);
$result = cert_action_validator($options["c"]);
echo json_encode(array('status'=>$result));
exit(1);
break;
default:
echo "ERROR: invalid argument specified\n";
log_error("invalid argument specified");
@@ -126,10 +131,14 @@ function cert_action_validator($opt_cert_id)
if (isset($options["A"]) or ((string)$cert_id == (string)$opt_cert_id)) {
// Ignore disabled certificates
if ($certObj->enabled == 0) {
// Always ignore disabled certs when working on ALL certs.
if (isset($options["A"])) {
continue; // skip to next item
}
return(1); // Cert is disabled, skip it.
// Allow only "revoke", "remove" and "removekey" for disabled certs.
if (!in_array($options["a"], ['remove','removekey','revoke'])) {
return(1); // Cert is disabled, skip it.
}
}
// Extract Account from referenced obj
@@ -179,6 +188,37 @@ function cert_action_validator($opt_cert_id)
}
}
// Cert is being removed from the GUI, delete all traces.
if ($options["a"] == "remove") {
// Start acme client to remove the certificate
$rev_result = remove_cert($certObj);
if (!$rev_result) {
log_error("AcmeClient: successfully removed acme.sh certificate configuration for " . (string)$certObj->name);
return(0); // Success!
} else {
log_error("AcmeClient: failed to remove acme.sh certificate configuration for " . (string)$certObj->name);
return(1);
}
}
// Remove private key
// NOTE: Although the user requested to remove the private key,
// we simply perform a full cert removal because without the
// matching private key the cert is useless.
if ($options["a"] == "removekey") {
// Start acme client to remove the certificate
$rev_result = remove_cert($certObj);
if (!$rev_result) {
log_error("AcmeClient: successfully removed the private key and reset certificate " . (string)$certObj->name);
// Reset certificate state, treat it like a new certificate.
log_cert_acme_status($certObj, $modelObj, '100');
return(0); // Success!
} else {
log_error("AcmeClient: failed to remove the private key and reset certificate " . (string)$certObj->name);
return(1);
}
}
// Make sure we found the configured validation method
if ($ref_found == true) {
// Was a revocation requested?
@@ -945,7 +985,47 @@ function revoke_cert($certObj, $valObj, $acctObj)
. $ecc_param;
$result = mwexec($acmecmd);
// TODO: maybe clear lastUpdate value?
// Simply return acme clients exit code
return($result);
}
// Remove a cert from list of certs known to acme.sh.
function remove_cert($certObj)
{
// Prepare optional parameters for acme-client
$acme_args = eval_optional_acme_args();
// Generate certificate filenames
$cert_id = (string)$certObj->id;
// Check if EC certificate is used, if yes add the --ecc parameter to acme client
$key_length = (string) $certObj->keyLength;
$ecc_param = " ";
if ($key_length == 'key_ec256' || $key_length == 'key_ec384') {
$ecc_param = "--ecc";
}
// Run acme client
$acmecmd = "/usr/local/sbin/acme.sh "
. implode(" ", $acme_args) . " "
. "--remove "
. "--domain " . (string)$certObj->name . " "
. "--home /var/etc/acme-client/home "
. $ecc_param;
$result = mwexec($acmecmd);
$cert_files = [
"/var/etc/acme-client/keys/${cert_id}/private.key",
"/var/etc/acme-client/certs/${cert_id}/cert.pem",
"/var/etc/acme-client/certs/${cert_id}/chain.pem",
"/var/etc/acme-client/certs/${cert_id}/fullchain.pem",
];
foreach ($cert_files as $_file) {
if (file_exists($_file)) {
unlink($_file);
}
}
// Simply return acme clients exit code
return($result);
@@ -53,6 +53,18 @@ parameters:%s
type:script
message:revoking a certificate
[remove-cert]
command:/usr/local/opnsense/scripts/OPNsense/AcmeClient/certhelper.php -a remove -c
parameters:%s
type:script
message:removing a certificate
[remove-key]
command:/usr/local/opnsense/scripts/OPNsense/AcmeClient/certhelper.php -a removekey -c
parameters:%s
type:script
message:removing a certificate private key
[sign-all-certs]
command:/usr/sbin/daemon -f /usr/local/opnsense/scripts/OPNsense/AcmeClient/certhelper.php -a sign -A
parameters: