From fb650dd8cd66458e72371697a5d946f97046fef7 Mon Sep 17 00:00:00 2001 From: David Harrigan Date: Sun, 31 Dec 2017 17:07:22 +0000 Subject: [PATCH] A new plugin to allow SCP config backups to a remote host. This simple plugin takes a few parameters, such as hostname and username and schedules a cron job to remotely scp the config.xml file at regular intervals. It uses the public/private keypair of the built-in root user as the source of the key exchange. This means that the public key must be copied to the remote host and added to the authorized_keys file (for the defined user). The remote file is backed up as `config-YYYY-DD-MM-HH-MM.xml`, for example: `config-2018-01-02-15-40.xml`. It's possible to change the remote location of where the config file is backed up to. The cron job can be modified under System/Settings/Cron and the schedule adjusted to suit the backup frequency requirements. -=david=- closes #457 --- sysutils/scp-backup/Makefile | 7 ++ sysutils/scp-backup/pkg-descr | 1 + .../ScpBackup/Api/GeneralController.php | 113 ++++++++++++++++++ .../OPNsense/ScpBackup/GeneralController.php | 42 +++++++ .../OPNsense/ScpBackup/forms/general.xml | 39 ++++++ .../app/models/OPNsense/ScpBackup/ACL/ACL.xml | 9 ++ .../app/models/OPNsense/ScpBackup/General.php | 40 +++++++ .../app/models/OPNsense/ScpBackup/General.xml | 44 +++++++ .../models/OPNsense/ScpBackup/Menu/Menu.xml | 8 ++ .../app/views/OPNsense/ScpBackup/general.volt | 55 +++++++++ .../scripts/OPNsense/ScpBackup/ScpBackup.php | 65 ++++++++++ .../conf/actions.d/actions_scpbackup.conf | 6 + 12 files changed, 429 insertions(+) create mode 100644 sysutils/scp-backup/Makefile create mode 100644 sysutils/scp-backup/pkg-descr create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/Api/GeneralController.php create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/GeneralController.php create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/forms/general.xml create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/ACL/ACL.xml create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/General.php create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/General.xml create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/Menu/Menu.xml create mode 100644 sysutils/scp-backup/src/opnsense/mvc/app/views/OPNsense/ScpBackup/general.volt create mode 100755 sysutils/scp-backup/src/opnsense/scripts/OPNsense/ScpBackup/ScpBackup.php create mode 100644 sysutils/scp-backup/src/opnsense/service/conf/actions.d/actions_scpbackup.conf diff --git a/sysutils/scp-backup/Makefile b/sysutils/scp-backup/Makefile new file mode 100644 index 000000000..2cb6b262b --- /dev/null +++ b/sysutils/scp-backup/Makefile @@ -0,0 +1,7 @@ +PLUGIN_NAME= scp-backup +PLUGIN_VERSION= 0.1.0 +PLUGIN_COMMENT= Perform config backups using SCP. +PLUGIN_MAINTAINER= dharrigan@gmail.com +PLUGIN_DEVEL= yes + +.include "../../Mk/plugins.mk" diff --git a/sysutils/scp-backup/pkg-descr b/sysutils/scp-backup/pkg-descr new file mode 100644 index 000000000..e2c3d466a --- /dev/null +++ b/sysutils/scp-backup/pkg-descr @@ -0,0 +1 @@ +A simple plugin to perform config backups using SCP. diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/Api/GeneralController.php b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/Api/GeneralController.php new file mode 100644 index 000000000..e989fad11 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/Api/GeneralController.php @@ -0,0 +1,113 @@ +request->isGet()) { + $mdlGeneral = $this->getModel(); + $publicKey = fopen("/conf/sshd/ssh_host_rsa_key.pub", "r"); + if($publicKey) { + $mdlGeneral->publickey = fread($publicKey, filesize("/conf/sshd/ssh_host_rsa_key.pub")); + fclose($publicKey); + } + $result['general'] = $mdlGeneral->getNodes(); + } + return $result; + } + + public function setAction() + { + $result = array("result"=>"failed"); + if ($this->request->isPost()) { + $mdlGeneral = $this->getModel(); + $backend = new Backend(); + $mdlCron = new Cron(); + $mdlGeneral->setNodes($this->request->getPost("general")); + $mdlGeneral->publickey = null; + $valMsgs = $mdlGeneral->performValidation(); + foreach ($valMsgs as $field => $msg) { + if (!array_key_exists("validation", $result)) { + $result["validations"] = array(); + } + $result["validations"][$msg->getField()] = $msg->getMessage(); + } + if($valMsgs->count() == 0) { + if($mdlGeneral->cronuuid->__toString() == "" and $mdlGeneral->enabled->__toString() == "1") { + // First Time Save + $cronUuid = $mdlCron->newDailyJob("ScpBackup", "scpbackup perform", "Backup config using SCP", "*", "1"); + if ($mdlCron->performValidation()->count() == 0) { + $mdlCron->serializeToConfig(); + // save data to config, do not validate because the current in memory model doesn't know about the cron item just created. + $mdlGeneral->cronuuid = $cronUuid; + $mdlGeneral->serializeToConfig($validateFullModel = false, $disable_validation = true); + Config::getInstance()->save(); + $backend->configdRun('template reload OPNsense/Cron'); + $result["result"] = "cron job [" . $cronUuid . "] created to backup config file daily using SCP."; + } + } elseif ($mdlGeneral->cronuuid->__toString() != "" and $mdlGeneral->enabled->__toString() == "0") { + // Removal of Cron Job and deactivation of the backup + $cronUuid = $mdlGeneral->cronuuid->__toString(); + if($mdlCron->jobs->job->del($cronUuid)) { + $mdlCron->serializeToConfig(); + $mdlGeneral->cronuuid = null; + $mdlGeneral->serializeToConfig($validateFullModel = false, $disable_validation = true); + Config::getInstance()->save(); + $backend->configdRun('template reload OPNsense/Cron'); + $result["result"] = "cron job [" . $cronUuid . "] to backup config file deleted."; + } else { + $result["result"] = "unable to delete cron job [". $cronUuid . "]"; + } + } else { + // Update the backup configuration + $mdlGeneral->serializeToConfig(); + Config::getInstance()->save(); + $result["result"] = "SCP backup configuration updated."; + } + } + } + return $result; + } + +} diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/GeneralController.php b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/GeneralController.php new file mode 100644 index 000000000..bde85aadf --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/GeneralController.php @@ -0,0 +1,42 @@ +view->pick('OPNsense/ScpBackup/general'); + $this->view->generalForm = $this->getForm("general"); + } +} + diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/forms/general.xml b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/forms/general.xml new file mode 100644 index 000000000..7dd9591b0 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/controllers/OPNsense/ScpBackup/forms/general.xml @@ -0,0 +1,39 @@ +
+ + general.enabled + + checkbox + This will activate the SCP backup plugin. + + + general.hostname + + text + Set the remote hostname. + + + general.port + + text + Set the remote port. + + + general.username + + text + Set the remote username. + + + general.remotedirectory + + text + Set the remote directory to backup the config file to. + + + general.publickey + + textbox + The public key of the local root user. This public key must be added to the remote user's authorized_keys - otherwise backups will fail. + true + +
diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/ACL/ACL.xml b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/ACL/ACL.xml new file mode 100644 index 000000000..d31898bd9 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/ACL/ACL.xml @@ -0,0 +1,9 @@ + + + System: Configuration: SCP Backup + + ui/ScpBackup/* + api/ScpBackup/* + + + diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/General.php b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/General.php new file mode 100644 index 000000000..4e5f1004f --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/General.php @@ -0,0 +1,40 @@ + + //OPNsense/ScpBackup + + scp-backup - a simple plugin to backup the config using SCP. + + 0.1.0 + + + 0 + Y + + + + Y + /\S*/ + Please provide a hostname (no spaces allowed) + + + 22 + Y + 1 + 65535 + Please provide a valid port number between 1 and 65535. Port 22 is the default. + + + root + Y + /^[a-z0-9_-]{3,32}$/ + Please provide a valid username ([a-z0-9_-]{3,32}). + + + ./ + Y + Please provide a remote directory. + + + N + + + N + + + + diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/Menu/Menu.xml b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/Menu/Menu.xml new file mode 100644 index 000000000..91002c4a3 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/models/OPNsense/ScpBackup/Menu/Menu.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sysutils/scp-backup/src/opnsense/mvc/app/views/OPNsense/ScpBackup/general.volt b/sysutils/scp-backup/src/opnsense/mvc/app/views/OPNsense/ScpBackup/general.volt new file mode 100644 index 000000000..2e4b0a3e8 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/mvc/app/views/OPNsense/ScpBackup/general.volt @@ -0,0 +1,55 @@ +{# + +Copyright (C) 2018 David Harrigan +OPNsense® is Copyright © 2015 – 2017 by Deciso B.V. + +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. + +#} + + +
+ {{ partial("layout_partials/base_form", ['fields':generalForm,'id':'frm_general_settings']) }} +
+
+ +
+
diff --git a/sysutils/scp-backup/src/opnsense/scripts/OPNsense/ScpBackup/ScpBackup.php b/sysutils/scp-backup/src/opnsense/scripts/OPNsense/ScpBackup/ScpBackup.php new file mode 100755 index 000000000..3193998a0 --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/scripts/OPNsense/ScpBackup/ScpBackup.php @@ -0,0 +1,65 @@ +#!/usr/local/bin/php +object(); +$scpBackup = $config->OPNsense->ScpBackup; + +if(isset($scpBackup) && isset($scpBackup->enabled) && $scpBackup->enabled == 1) { + $hostname = escapeshellarg($scpBackup->hostname); + $username = escapeshellarg($scpBackup->username); + $port = $scpBackup->port; + $remoteDirectory = empty(trim($scpBackup->remotedirectory)) ? "./" : $scpBackup->remotedirectory; + $identifyFile = "/conf/sshd/ssh_host_rsa_key"; + $configFile = "/conf/config.xml"; + + if(!substr($remoteDirectory, -1) == "/") { + $remoteDirectory = $remoteDirectory . "/"; + } + + $remoteDirectoryFullPath = escapeshellarg($remoteDirectory . "config-" . date('Y-m-d-H-i') . ".xml"); + + $command = "scp -P $port -i $identifyFile $configFile $username@$hostname:$remoteDirectoryFullPath"; + + syslog(LOG_WARNING, "scp_backup command: $command"); + + exec(escapeshellcmd($command), $output, $returnCode); + + if($returnCode != 0) { + syslog(LOG_ERR, "scp_backup command: return code [$returnCode]"); + } +} diff --git a/sysutils/scp-backup/src/opnsense/service/conf/actions.d/actions_scpbackup.conf b/sysutils/scp-backup/src/opnsense/service/conf/actions.d/actions_scpbackup.conf new file mode 100644 index 000000000..79ef68ebe --- /dev/null +++ b/sysutils/scp-backup/src/opnsense/service/conf/actions.d/actions_scpbackup.conf @@ -0,0 +1,6 @@ +[perform] +command: /usr/local/opnsense/scripts/OPNsense/ScpBackup/ScpBackup.php;exit 0 +parameters: +type: script_output +message: cronjob running to backup config to remote location +description: Backup config using SCP