system: remove GDrive backup from core

Still needs migration glue, but mechanics are done.

PR: https://github.com/opnsense/core/issues/8343
This commit is contained in:
Franco Fichtner 2025-03-12 08:11:25 +01:00
parent 717bf17dae
commit d77bd0a8fb
5 changed files with 0 additions and 462 deletions

View File

@ -161,7 +161,6 @@ CORE_DEPENDS?= ca_root_nss \
php${CORE_PHP}-dom \
php${CORE_PHP}-filter \
php${CORE_PHP}-gettext \
php${CORE_PHP}-google-api-php-client \
php${CORE_PHP}-ldap \
php${CORE_PHP}-pcntl \
php${CORE_PHP}-pdo \

2
plist
View File

@ -526,7 +526,6 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardClient.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/dialogEditWireguardServer.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Wireguard/forms/general.xml
/usr/local/opnsense/mvc/app/library/Google/API/Drive.php
/usr/local/opnsense/mvc/app/library/OPNsense/Auth/API.php
/usr/local/opnsense/mvc/app/library/OPNsense/Auth/AuthenticationFactory.php
/usr/local/opnsense/mvc/app/library/OPNsense/Auth/Base.php
@ -545,7 +544,6 @@
/usr/local/opnsense/mvc/app/library/OPNsense/Autoload/Loader.php
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/BackupFactory.php
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/Base.php
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/GDrive.php
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/IBackupProvider.php
/usr/local/opnsense/mvc/app/library/OPNsense/Backup/Local.php
/usr/local/opnsense/mvc/app/library/OPNsense/Base/UIModelGrid.php

View File

@ -413,11 +413,6 @@ function core_xmlrpc_sync()
'id' => 'webgui',
'services' => ["webgui"],
);
$result[] = array(
'description' => gettext('Backup - Google Drive'),
'section' => 'system.remotebackup',
'id' => 'remotebackup'
);
return $result;
}

View File

@ -1,148 +0,0 @@
<?php
/*
* Copyright (C) 2015-2023 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 Google\API;
/**
* Class Drive wrapper around Google API for Drive support
* @package Google\API
*/
class Drive
{
/**
* @var null|\Google_Service_Drive service pointer
*/
private $service = null;
/**
* @var null|\Google_Client pointer to client
*/
private $client = null;
/**
* construct a new Drive object
*/
public function __construct()
{
// hook in Google's autoloader
require_once("/usr/local/share/google-api-php-client/vendor/autoload.php");
}
/**
* login to google drive
* @param $client_id
* @param $privateKeyB64 P12 key placed in a base64 container
*/
public function login($client_id, $privateKeyB64)
{
openssl_pkcs12_read(base64_decode($privateKeyB64), $certinfo, "notasecret");
if (empty($certinfo)) {
throw new \Exception("Invalid P12 key, openssl_pkcs12_read() failed");
}
$this->client = new \Google_Client();
$service_account = [
"type" => "service_account",
"private_key" => $certinfo['pkey'],
"client_email" => $client_id,
"client_id" => $client_id,
"auth_uri" => "https://accounts.google.com/o/oauth2/auth",
"token_uri" => "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url" => "https://www.googleapis.com/oauth2/v1/certs"
];
$this->client->setAuthConfig($service_account);
$this->client->addScope("https://www.googleapis.com/auth/drive");
$this->client->setApplicationName("OPNsense");
$this->service = new \Google_Service_Drive($this->client);
}
/**
* retrieve directory listing
* @param $directoryId parent directory id
* @param $filename title/filename of object
* @return mixed list of files
*/
public function listFiles($directoryId, $filename = null)
{
$query = "'" . $directoryId . "' in parents ";
if ($filename != null) {
$query .= " and title in '" . $filename . "'";
}
return $this->service->files->listFiles(['q' => $query, 'supportsAllDrives' => true]);
}
/**
* download a file by given GDrive file handle
* @param $fileHandle (object from listFiles)
* @return null|string
*/
public function download($fileHandle)
{
$response = $this->service->files->get($fileHandle->id, ['alt' => 'media', 'supportsAllDrives' => true]);
return $response->getBody()->getContents();
}
/**
* Upload file
* @param string $directoryId (parent id)
* @param string $filename
* @param string $content
* @param string $mimetype
* @return \Google_Service_Drive_DriveFile handle
*/
public function upload($directoryId, $filename, $content, $mimetype = 'text/plain')
{
$file = new \Google_Service_Drive_DriveFile();
$file->setName($filename);
$file->setDescription($filename);
$file->setMimeType('text/plain');
$file->setParents([$directoryId]);
$createdFile = $this->service->files->create($file, [
'data' => $content,
'mimeType' => $mimetype,
'uploadType' => 'media',
'supportsAllDrives' => true
]);
return $createdFile;
}
/**
* delete file
* @param $fileHandle (object from listFiles)
*/
public function delete($fileHandle)
{
$this->service->files->delete($fileHandle['id'], ['supportsAllDrives' => true]);
}
}

View File

@ -1,306 +0,0 @@
<?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;
/**
* Class google drive backup
* @package OPNsense\Backup
*/
class Gdrive extends Base implements IBackupProvider
{
/**
* get required (user interface) fields for backup connector
* @return array configuration fields, types and description
*/
public function getConfigurationFields()
{
$fields = array();
$fields[] = array(
"name" => "GDriveEnabled",
"type" => "checkbox",
"label" => gettext("Enable"),
"value" => null
);
$fields[] = array(
"name" => "GDriveEmail",
"type" => "text",
"label" => gettext("Email Address"),
"help" => gettext("Client-ID in the Google cloud console"),
"value" => null
);
$fields[] = array(
"name" => "GDriveP12key",
"type" => "file",
"label" => gettext("P12 key"),
"help" => sprintf(
gettext('You need a private key in p12 format to use Google Drive, ' .
'instructions on how to acquire one can be found %shere%s.'),
'<a href="https://docs.opnsense.org/manual/how-tos/cloud_backup.html" target="_blank">',
'</a>'
),
"value" => null
);
$fields[] = array(
"name" => "GDriveFolderID",
"type" => "text",
"label" => gettext("Folder ID"),
"value" => null
);
$fields[] = array(
"name" => "GDrivePrefixHostname",
"type" => "checkbox",
"label" => gettext("Prefix hostname to backupfile"),
"help" => gettext("Normally the config xml will be written as config-stamp.xml, with this option set " .
"the filename will use the systems host and domain name."),
"value" => null
);
$fields[] = array(
"name" => "GDriveBackupCount",
"type" => "text",
"label" => gettext("Backup Count"),
"value" => 60
);
$fields[] = array(
"name" => "GDrivePassword",
"type" => "password",
"label" => gettext("Password"),
"value" => null
);
$fields[] = array(
"name" => "GDrivePasswordConfirm",
"type" => "password",
"label" => gettext("Confirm"),
"value" => null
);
$cnf = Config::getInstance();
if ($cnf->isValid()) {
$config = $cnf->object();
foreach ($fields as &$field) {
$fieldname = $field['name'];
if (isset($config->system->remotebackup->$fieldname)) {
$field['value'] = (string)$config->system->remotebackup->$fieldname;
} elseif (
$fieldname == "GDrivePasswordConfirm" &&
isset($config->system->remotebackup->GDrivePassword)
) {
$field['value'] = (string)$config->system->remotebackup->GDrivePassword;
}
}
}
return $fields;
}
/**
* backup provider name
* @return string user friendly name
*/
public function getName()
{
return gettext("Google Drive");
}
/**
* validate and set configuration
* @param array $conf configuration array
* @return array of validation errors when not saved
*/
public function setConfiguration($conf)
{
$input_errors = array();
if ($conf['GDrivePasswordConfirm'] != $conf['GDrivePassword']) {
$input_errors[] = gettext("The supplied 'Password' and 'Confirm' field values must match.");
}
if (count($input_errors) == 0) {
$config = Config::getInstance()->object();
if (!isset($config->system->remotebackup)) {
$config->system->addChild('remotebackup');
}
foreach ($this->getConfigurationFields() as $field) {
$fieldname = $field['name'];
if ($field['type'] == 'file') {
if (!empty($conf[$field['name']])) {
$config->system->remotebackup->$fieldname = base64_encode($conf[$field['name']]);
}
} elseif ($field['name'] == 'GDrivePasswordConfirm') {
/* skip password confirm field */
} elseif (!empty($conf[$field['name']])) {
$config->system->remotebackup->$fieldname = $conf[$field['name']];
} else {
unset($config->system->remotebackup->$fieldname);
}
}
// remove private key when disabled
if (
empty($config->system->remotebackup->GDriveEnabled) &&
isset($config->system->remotebackup->GDriveP12key)
) {
unset($config->system->remotebackup->GDriveP12key);
}
Config::getInstance()->save();
}
return $input_errors;
}
/**
* @return array filelist
*/
public function backup()
{
$cnf = Config::getInstance();
if ($cnf->isValid()) {
$config = $cnf->object();
if (
isset($config->system->remotebackup) && isset($config->system->remotebackup->GDriveEnabled)
&& !empty($config->system->remotebackup->GDriveEnabled)
) {
if (!empty($config->system->remotebackup->GDrivePrefixHostname)) {
$fileprefix = (string)$config->system->hostname . "." . (string)$config->system->domain . "-";
} else {
$fileprefix = "config-";
}
try {
$client = new \Google\API\Drive();
$client->login(
(string)$config->system->remotebackup->GDriveEmail,
(string)$config->system->remotebackup->GDriveP12key
);
} catch (\Error | \Exception $e) {
syslog(LOG_ERR, "error connecting to Google Drive");
return array();
}
// backup source data to local strings (plain/encrypted)
$confdata = file_get_contents('/conf/config.xml');
$confdata_enc = $this->encrypt($confdata, (string)$config->system->remotebackup->GDrivePassword);
// read filelist ({prefix}*.xml)
try {
$files = $client->listFiles((string)$config->system->remotebackup->GDriveFolderID);
} catch (\Error | \Exception $e) {
syslog(LOG_ERR, "error while fetching filelist from Google Drive");
return array();
}
$configfiles = array();
foreach ($files as $file) {
if (fnmatch("{$fileprefix}*.xml", $file['name'])) {
$configfiles[$file['name']] = $file;
}
}
krsort($configfiles);
// backup new file if changed (or if first in backup)
$target_filename = $fileprefix . time() . ".xml";
if (count($configfiles) > 1) {
// compare last backup with current, only save new
try {
$bck_data_enc = $client->download($configfiles[array_keys($configfiles)[0]]);
if (strpos(substr($bck_data_enc, 0, 100), '---') !== false) {
// base64 string is wrapped into tags
$start_at = strpos($bck_data_enc, "---\n") + 4;
$end_at = strpos($bck_data_enc, "\n---");
$bck_data_enc = substr($bck_data_enc, $start_at, ($end_at - $start_at));
}
$bck_data = $this->decrypt(
$bck_data_enc,
(string)$config->system->remotebackup->GDrivePassword
);
if ($bck_data == $confdata) {
$target_filename = null;
}
} catch (\Error | \Exception $e) {
syslog(LOG_ERR, "unable to download " .
$configfiles[array_keys($configfiles)[0]]->description . " from Google Drive (" . $e . ")");
}
}
if (!is_null($target_filename)) {
syslog(LOG_NOTICE, "backup configuration as " . $target_filename);
try {
$configfiles[$target_filename] = $client->upload(
(string)$config->system->remotebackup->GDriveFolderID,
$target_filename,
$confdata_enc
);
} catch (\Error | \Exception $e) {
syslog(LOG_ERR, "unable to upload " . $target_filename . " to Google Drive (" . $e . ")");
return array();
}
krsort($configfiles);
}
// cleanup old files
if (
isset($config->system->remotebackup->GDriveBackupCount)
&& is_numeric((string)$config->system->remotebackup->GDriveBackupCount)
) {
$fcount = 0;
foreach ($configfiles as $filename => $file) {
if ($fcount >= (string)$config->system->remotebackup->GDriveBackupCount) {
syslog(LOG_NOTICE, "remove " . $filename . " from Google Drive");
try {
$client->delete($file);
} catch (Google_Service_Exception $e) {
syslog(LOG_ERR, "unable to remove " . $filename . " from Google Drive");
}
}
$fcount++;
}
}
// return filelist
return array_keys($configfiles);
}
}
// not configured / issue, return empty list
return array();
}
/**
* Is this provider enabled
* @return boolean enabled status
*/
public function isEnabled()
{
$cnf = Config::getInstance();
if ($cnf->isValid()) {
$config = $cnf->object();
return isset($config->system->remotebackup) && isset($config->system->remotebackup->GDriveEnabled)
&& !empty($config->system->remotebackup->GDriveEnabled);
}
return false;
}
}