Last active
September 4, 2024 20:13
-
-
Save cretl/9399900b4e623de4fcaab76592508ed0 to your computer and use it in GitHub Desktop.
OPNsense SFTP backup plugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| ///usr/local/opnsense/mvc/app/library/OPNsense/Backup/SFTP.php | |
| /* | |
| * Copyright (C) 2018 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. | |
| */ | |
| namespace OPNsense\Backup; | |
| use OPNsense\Core\Config; | |
| use OPNsense\Backup\SFTPSettings; | |
| /** | |
| * Class SFTP backup | |
| * @package OPNsense\Backup | |
| */ | |
| class SFTP extends Base implements IBackupProvider | |
| { | |
| /** | |
| * get required (user interface) fields for backup connector | |
| * @return array configuration fields, types and description | |
| */ | |
| public function getConfigurationFields() | |
| { | |
| $fields = array( | |
| array( | |
| "name" => "enabled", | |
| "type" => "checkbox", | |
| "label" => gettext("Enable"), | |
| "value" => null | |
| ), | |
| array( | |
| "name" => "host", | |
| "type" => "text", | |
| "label" => gettext("SFTP Host"), | |
| "help" => gettext("The hostname or IP address of the SFTP server"), | |
| "value" => null | |
| ), | |
| array( | |
| "name" => "port", | |
| "type" => "text", | |
| "label" => gettext("Port"), | |
| "help" => gettext("The port of the SFTP server, usually 22"), | |
| "value" => '22' | |
| ), | |
| array( | |
| "name" => "user", | |
| "type" => "text", | |
| "label" => gettext("User Name"), | |
| "help" => gettext("The username to log into the SFTP server"), | |
| "value" => null | |
| ), | |
| array( | |
| "name" => "private_key", | |
| "type" => "textarea", | |
| "label" => gettext("Private Key"), | |
| "help" => gettext("The private key for SFTP authentication in OpenSSH format"), | |
| "value" => null | |
| ), | |
| array( | |
| "name" => "backupdir", | |
| "type" => "text", | |
| "label" => gettext("Remote Backup Directory"), | |
| "value" => 'OPNsense-Backup' | |
| ) | |
| ); | |
| $sftp = new SFTPSettings(); | |
| foreach ($fields as &$field) { | |
| $field['value'] = (string)$sftp->getNodeByReference($field['name']); | |
| } | |
| return $fields; | |
| } | |
| /** | |
| * backup provider name | |
| * @return string user friendly name | |
| */ | |
| public function getName() | |
| { | |
| return gettext("SFTP"); | |
| } | |
| /** | |
| * validate and set configuration | |
| * @param array $conf configuration array | |
| * @return array of validation errors when not saved | |
| * @throws \OPNsense\Base\ModelException | |
| * @throws \ReflectionException | |
| */ | |
| public function setConfiguration($conf) | |
| { | |
| $sftp = new SFTPSettings(); | |
| $this->setModelProperties($sftp, $conf); | |
| $validation_messages = $this->validateModel($sftp); | |
| if (empty($validation_messages)) { | |
| $sftp->serializeToConfig(); | |
| Config::getInstance()->save(); | |
| } | |
| return $validation_messages; | |
| } | |
| /** | |
| * perform backup | |
| * @return array filelist | |
| * @throws \OPNsense\Base\ModelException | |
| * @throws \ReflectionException | |
| */ | |
| public function backup() | |
| { | |
| $cnf = Config::getInstance(); | |
| $sftp = new SFTPSettings(); | |
| if ($cnf->isValid() && !empty((string)$sftp->enabled)) { | |
| $config = $cnf->object(); | |
| $host = (string)$sftp->host; | |
| $port = (string)$sftp->port; | |
| $username = (string)$sftp->user; | |
| $private_key = (string)$sftp->private_key; | |
| $backupdir = (string)$sftp->backupdir; | |
| $hostname = $config->system->hostname . '.' . $config->system->domain; | |
| $configname = 'config-' . $hostname . '-' . date('Y-m-d_H_i_s') . '.xml'; | |
| // backup source data to local strings | |
| $confdata = file_get_contents('/conf/config.xml'); | |
| if (!empty((string)$sftp->password_encryption)) { | |
| $confdata = $this->encrypt($confdata, (string)$sftp->password_encryption); | |
| } | |
| // Remove carriage return from private key string | |
| $private_key = str_replace("\r", "", $private_key); | |
| // Create temporary files for private key and config data | |
| $private_key_file = tempnam(sys_get_temp_dir(), 'sftp_key'); | |
| $confdata_file = tempnam(sys_get_temp_dir(), 'sftp_conf'); | |
| file_put_contents($private_key_file, $private_key); | |
| file_put_contents($confdata_file, $confdata); | |
| // SFTP command | |
| $sftp_command = sprintf( | |
| 'sftp -o StrictHostKeyChecking=no -i %s -P %s %s@%s << EOF | |
| mkdir %s | |
| put %s %s/%s | |
| EOF', | |
| escapeshellarg($private_key_file), | |
| escapeshellarg($port), | |
| escapeshellarg($username), | |
| escapeshellarg($host), | |
| escapeshellarg($backupdir), | |
| escapeshellarg($confdata_file), | |
| escapeshellarg($backupdir), | |
| escapeshellarg($configname) | |
| ); | |
| exec($sftp_command, $fileputoutput, $return_var); | |
| if ($return_var !== 0) { | |
| syslog(LOG_ERR, "SFTP backup failed with command: $sftp_command"); | |
| return array(); | |
| } | |
| $sftp_command = sprintf( | |
| 'sftp -o StrictHostKeyChecking=no -i %s -P %s %s@%s << EOF | |
| ls -1 %s | |
| EOF', | |
| escapeshellarg($private_key_file), | |
| escapeshellarg($port), | |
| escapeshellarg($username), | |
| escapeshellarg($host), | |
| escapeshellarg($backupdir) | |
| ); | |
| exec($sftp_command, $filelistoutput, $return_var); | |
| if ($return_var !== 0) { | |
| syslog(LOG_ERR, "SFTP backup failed with command: $sftp_command"); | |
| return array(); | |
| } | |
| // Cleanup temporary files | |
| unlink($private_key_file); | |
| unlink($confdata_file); | |
| return array_filter( | |
| $filelistoutput, | |
| function ($filename) { | |
| $extension = pathinfo($filename, PATHINFO_EXTENSION); | |
| return strtolower($extension) === 'xml'; | |
| } | |
| ); | |
| } | |
| } | |
| /** | |
| * Is this provider enabled | |
| * @return boolean enabled status | |
| * @throws \OPNsense\Base\ModelException | |
| * @throws \ReflectionException | |
| */ | |
| public function isEnabled() | |
| { | |
| $sftp = new SFTPSettings(); | |
| return (string)$sftp->enabled === "1"; | |
| } | |
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| ///usr/local/opnsense/mvc/app/models/OPNsense/Backup/SFTPSettings.php | |
| /* | |
| * Copyright (C) 2018 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. | |
| */ | |
| namespace OPNsense\Backup; | |
| use OPNsense\Base\BaseModel; | |
| /** | |
| * Class SFTP | |
| * @package Backup | |
| */ | |
| class SFTPSettings extends BaseModel | |
| { | |
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <model> | |
| <mount>//system/backup/sftp</mount> | |
| <version>1.0.0</version> | |
| <description>OPNsense SFTP Backup Settings</description> | |
| <items> | |
| <enabled type="BooleanField"> | |
| <default>0</default> | |
| <Required>Y</Required> | |
| </enabled> | |
| <host type="TextField"> | |
| <Required>Y</Required> | |
| <ValidationMessage>The host for the SFTP server must be set.</ValidationMessage> | |
| <Constraints> | |
| <check001> | |
| <ValidationMessage>A host for the SFTP server must be set.</ValidationMessage> | |
| <type>DependConstraint</type> | |
| <addFields> | |
| <field1>enabled</field1> | |
| </addFields> | |
| </check001> | |
| </Constraints> | |
| </host> | |
| <port type="IntegerField"> | |
| <Required>Y</Required> | |
| <default>22</default> | |
| <ValidationMessage>The port for the SFTP server must be set.</ValidationMessage> | |
| </port> | |
| <user type="TextField"> | |
| <Required>Y</Required> | |
| <ValidationMessage>The user for the SFTP server must be set.</ValidationMessage> | |
| <Constraints> | |
| <check001> | |
| <ValidationMessage>A user for the SFTP server must be set.</ValidationMessage> | |
| <type>DependConstraint</type> | |
| <addFields> | |
| <field1>enabled</field1> | |
| </addFields> | |
| </check001> | |
| </Constraints> | |
| </user> | |
| <private_key type="TextField"> | |
| <Required>Y</Required> | |
| <ValidationMessage>The private key for the SFTP server must be set.</ValidationMessage> | |
| <Constraints> | |
| <check001> | |
| <ValidationMessage>A private key for the SFTP server must be set.</ValidationMessage> | |
| <type>DependConstraint</type> | |
| <addFields> | |
| <field1>enabled</field1> | |
| </addFields> | |
| </check001> | |
| </Constraints> | |
| </private_key> | |
| <backupdir type="TextField"> | |
| <Required>Y</Required> | |
| <default>OPNsense-Backup</default> | |
| <mask>/^([\w%+\-]+\/)*[\w+%\-]+$/</mask> | |
| <ValidationMessage>The Backup Directory can only consist of alphanumeric characters, dash, underscores and slash. No leading or trailing slash.</ValidationMessage> | |
| </backupdir> | |
| </items> | |
| </model> |
Thank you for writing this! I have migrated from Nextcloud to using SFPTGo and was looking for a way to backup opnsense there.
One problem I did run into was with the generation of the temporary ssh key. It keeps appending the DOS newlines character '^M' to every line which in turn breaks the sftp commands. Not really a PHP guy but was able to add some sed commands to fix the file on the first sftp commands run. Not sure how else to approach it though.
Hi there,
Thanks for the info. I can reproduce this problem on my setup. But this doesn't break the script in my setup. I added a code line to remove the carriage return from the private key string.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
File locations:
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/SFTP.php
/usr/local/opnsense/mvc/app/models/OPNsense/Backup/SFTPSettings.php
/usr/local/opnsense/mvc/app/models/OPNsense/Backup/SFTPSettings.xml