Skip to content

Commit

Permalink
Merge pull request #2647 from fraenki/acme_350
Browse files Browse the repository at this point in the history
security/acme-client: release 3.5
  • Loading branch information
fraenki committed Nov 15, 2021
2 parents 7ce6c2b + 5d54ac6 commit df1b283
Show file tree
Hide file tree
Showing 19 changed files with 434 additions and 32 deletions.
2 changes: 1 addition & 1 deletion security/acme-client/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PLUGIN_NAME= acme-client
PLUGIN_VERSION= 3.4
PLUGIN_VERSION= 3.5
PLUGIN_COMMENT= ACME Client
PLUGIN_MAINTAINER= opnsense@moov.de
PLUGIN_DEPENDS= acme.sh py${PLUGIN_PYTHON}-dns-lexicon
Expand Down
12 changes: 12 additions & 0 deletions security/acme-client/pkg-descr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ WWW: https://github.com/acmesh-official/acme.sh
Plugin Changelog
================

3.5

Added:
* new automation: cert upload to Synology DSM (#2236)
* new automation: cert upload to FRITZ!Box router

Fixed:
* fix logging when clog is disabled (#2555)

Changed:
* refactor code to support acme.sh deploy hooks

3.4

Changed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<field>
<label>Required Parameters</label>
<type>header</type>
<style>method_table method_table_upload_highwinds</style>
<style>method_table method_table_configd_upload_highwinds</style>
</field>
<field>
<id>action.highwinds_account_hash</id>
Expand All @@ -43,7 +43,7 @@
<field>
<label>Required Parameters</label>
<type>header</type>
<style>method_table method_table_upload_sftp</style>
<style>method_table method_table_configd_upload_sftp</style>
</field>
<field>
<id>action.sftp_host</id>
Expand Down Expand Up @@ -145,13 +145,79 @@
<field>
<label>Required Parameters</label>
<type>header</type>
<style>method_table method_table_configd</style>
<style>method_table method_table_configd_generic</style>
</field>
<field>
<id>action.configd</id>
<id>action.configd_generic_command</id>
<label>System Command</label>
<type>dropdown</type>
<help>Select a pre-defined system command which should be run.</help>
<style>table_optional table_optional_configd</style>
</field>
<field>
<label>Required Parameters</label>
<type>header</type>
<style>method_table method_table_acme_synology_dsm</style>
</field>
<field>
<id>action.acme_synology_dsm_hostname</id>
<label>Synology Hostname</label>
<type>text</type>
<help>Hostname of IP adress of the Synology DSM, i.e. synology.example.com or 192.168.0.1.</help>
</field>
<field>
<id>action.acme_synology_dsm_port</id>
<label>Synology Port</label>
<type>text</type>
<help>Port that will be used when connecting to Synology DSM.</help>
</field>
<field>
<id>action.acme_synology_dsm_scheme</id>
<label>Scheme</label>
<type>dropdown</type>
<help>Connection scheme that will be used when uploading certificates to Synology DSM.</help>
</field>
<field>
<id>action.acme_synology_dsm_username</id>
<label>Username</label>
<type>text</type>
<help>Username to login, must be an administrator.</help>
</field>
<field>
<id>action.acme_synology_dsm_password</id>
<label>Password</label>
<type>password</type>
</field>
<field>
<id>action.acme_synology_dsm_deviceid</id>
<label>Device ID</label>
<type>text</type>
<help>If Synology DSM has OTP enabled, then the device ID has to be provided so that no OTP is required when running the automation.</help>
</field>
<field>
<id>action.acme_synology_dsm_create</id>
<label>Create certificates</label>
<type>checkbox</type>
<help>This option ensures that a new certificate is created in Synology DSM if it does not exist yet. If unchecked only existing certificates will be updated.</help>
</field>
<field>
<label>Required Parameters</label>
<type>header</type>
<style>method_table method_table_acme_fritzbox</style>
</field>
<field>
<id>action.acme_fritzbox_url</id>
<label>FRITZ!Box URL</label>
<type>text</type>
<help>URL of the router, i.e. https://fritzbox.example.com.</help>
</field>
<field>
<id>action.acme_fritzbox_username</id>
<label>Username</label>
<type>text</type>
</field>
<field>
<id>action.acme_fritzbox_password</id>
<label>Password</label>
<type>password</type>
</field>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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\LeAutomation;

use OPNsense\AcmeClient\LeAutomationInterface;

/**
* Run acme.sh deploy hook fritzbox
* @package OPNsense\AcmeClient
*/
class AcmeFritzbox extends Base implements LeAutomationInterface
{
public function prepare()
{
$this->acme_env['DEPLOY_FRITZBOX_URL'] = (string)$this->config->acme_fritzbox_url;
$this->acme_env['DEPLOY_FRITZBOX_USERNAME'] = (string)$this->config->acme_fritzbox_username;
$this->acme_env['DEPLOY_FRITZBOX_PASSWORD'] = (string)$this->config->acme_fritzbox_password;
$this->acme_args[] = '--deploy-hook fritzbox';
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?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\LeAutomation;

use OPNsense\AcmeClient\LeAutomationInterface;

/**
* Run acme.sh deploy hook synology_dsm
* @package OPNsense\AcmeClient
*/
class AcmeSynologyDsm extends Base implements LeAutomationInterface
{
public function prepare()
{
$this->acme_env['SYNO_Certificate'] = 'OPNsense ACME cert ' . $this->cert_id;
$this->acme_env['SYNO_Hostname'] = (string)$this->config->acme_synology_dsm_hostname;
$this->acme_env['SYNO_Port'] = (string)$this->config->acme_synology_dsm_port;
$this->acme_env['SYNO_Scheme'] = (string)$this->config->acme_synology_dsm_scheme;
$this->acme_env['SYNO_Username'] = (string)$this->config->acme_synology_dsm_username;
$this->acme_env['SYNO_Password'] = (string)$this->config->acme_synology_dsm_password;
if (!empty((string)$this->config->acme_synology_dsm_create)) {
$this->acme_env['SYNO_Create'] = (string)$this->config->acme_synology_dsm_create;
}
if (!empty((string)$this->config->acme_synology_dsm_deviceid)) {
$this->acme_env['SYNO_DID'] = (string)$this->config->acme_synology_dsm_deviceid;
}
$this->acme_args[] = '--deploy-hook synology_dsm';
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ abstract class Base extends \OPNsense\AcmeClient\LeCommon
* Initialize LeAutomation object by adding the required configuration.
* @return boolean
*/
public function init(string $certid, string $accountuuid)
public function init(string $certid, string $certname, string $accountuuid)
{
// Get config object
$this->loadConfig(self::CONFIG_PATH, $this->uuid);
Expand All @@ -60,12 +60,25 @@ public function init(string $certid, string $accountuuid)
$this->account_id = (string)$account->id;
$this->account_uuid = (string)$account->uuid;

// Teach acme.sh about DNS API hook location
$this->acme_env['_SCRIPT_HOME'] = self::ACME_SCRIPT_HOME;

// Set log level
$this->setLoglevel();

// Set ACME CA
$this->setCa($accountuuid);

// Store acme filenames
$this->acme_args[] = LeUtils::execSafe('--home %s', self::ACME_HOME_DIR);
$this->acme_args[] = LeUtils::execSafe('--certpath %s', sprintf(self::ACME_CERT_FILE, $this->cert_id));
$this->acme_args[] = LeUtils::execSafe('--keypath %s', sprintf(self::ACME_KEY_FILE, $this->cert_id));
$this->acme_args[] = LeUtils::execSafe('--capath %s', sprintf(self::ACME_CHAIN_FILE, $this->cert_id));
$this->acme_args[] = LeUtils::execSafe('--fullchainpath %s', sprintf(self::ACME_FULLCHAIN_FILE, $this->cert_id));

// Main domain for acme
$this->acme_args[] = LeUtils::execSafe('--domain %s', $certname);

return true;
}

Expand All @@ -80,7 +93,72 @@ public function run()
return true; // not an error
}

LeUtils::log('running automation: ' . $this->config->name);
// The prefix determines which automation flavour is being used.
if (preg_match('/acme.*/i', $this->getType())) {
$this->runAcme();
} elseif (preg_match('/configd_.*/i', $this->getType())) {
$this->runConfigd();
} else {
LeUtils::log_error('unsupported automation flavour: ' . $this->getType());
return false;
}
}

/**
* run acme.sh deploy hooks commands
* @return boolean
*/
public function runAcme()
{
LeUtils::log('running automation (acme.sh): ' . $this->config->name);

// Preparation to run acme client
$proc_env = $this->acme_env; // env variables for proc_open()
$proc_env['PATH'] = $this::ACME_ENV_PATH;
$proc_desc = array( // descriptor array for proc_open()
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$proc_pipes = array();

// Run acme client
$acmecmd = self::ACME_CMD
. ' '
. '--deploy '
. implode(' ', $this->acme_args);
LeUtils::log_debug('running acme.sh command: ' . (string)$acmecmd, $this->debug);
$proc = proc_open($acmecmd, $proc_desc, $proc_pipes, null, $proc_env);

// Make sure the resource could be setup properly
if (is_resource($proc)) {
// Close all pipes
fclose($proc_pipes[0]);
fclose($proc_pipes[1]);
fclose($proc_pipes[2]);
// Get exit code
$result = proc_close($proc);
} else {
LeUtils::log_error('unable to start acme client process');
return false;
}

// Check validation result
if ($result) {
LeUtils::log_error('running acme.sh deploy hook failed (' . $this->getMethod() . ')');
return false;
}

return true;
}

/**
* run configd commands
* @return boolean
*/
public function runConfigd()
{
LeUtils::log('running automation (configd): ' . $this->config->name);
$backend = new \OPNsense\Core\Backend();
$response = $backend->configdRun((string)$this->command, $this->command_args);
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -35,17 +35,17 @@
* Run selected configd command
* @package OPNsense\AcmeClient
*/
class Configd extends Base implements LeAutomationInterface
class ConfigdGeneric extends Base implements LeAutomationInterface
{
public function prepare()
{
// Make sure a configd command was specified.
if (empty((string)$this->config->configd)) {
if (empty((string)$this->config->configd_generic_command)) {
LeUtils::log_error('no configd command specified for automation: ' . $this->config->name);
return false;
}

$this->command = (string)$this->config->configd;
$this->command = (string)$this->config->configd_generic_command;
return true;
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -34,7 +34,7 @@
* Restart OPNsense WebGUI
* @package OPNsense\AcmeClient
*/
class RestartGui extends Base implements LeAutomationInterface
class ConfigdRestartGui extends Base implements LeAutomationInterface
{
public function prepare()
{
Expand Down

0 comments on commit df1b283

Please sign in to comment.