From c73274fc6446a1ce5830dd5e5e9e3edd7113d2d0 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Tue, 6 Oct 2020 18:00:54 +0200 Subject: [PATCH] Git backup: work in progress for https://github.com/opnsense/plugins/issues/2049 o add missing dependancy in Makefile o add branch in Model o implement backup() method, which is responsible for setting up a git repo and pushing it upstream. The actual "git add+commit" is a responsibility of the syshook config event (todo) sponsored by : Modirum (https://www.modirum.com/) --- sysutils/git-backup/Makefile | 10 +-- .../mvc/app/library/OPNsense/Backup/Git.php | 74 ++++++++++++++++++- .../models/OPNsense/Backup/GitSettings.xml | 4 + 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/sysutils/git-backup/Makefile b/sysutils/git-backup/Makefile index 6b4d6919f..8ac82dd76 100644 --- a/sysutils/git-backup/Makefile +++ b/sysutils/git-backup/Makefile @@ -1,7 +1,7 @@ -PLUGIN_NAME= git-backup -PLUGIN_VERSION= 0.1 -PLUGIN_COMMENT= Track config changes using git -PLUGIN_MAINTAINER= ad@opnsense.org -PLUGIN_DEVEL= yes +PLUGIN_NAME= git-backup +PLUGIN_VERSION= 0.1 +PLUGIN_COMMENT= Track config changes using git +PLUGIN_DEPENDS= git +PLUGIN_MAINTAINER= ad@opnsense.org .include "../../Mk/plugins.mk" diff --git a/sysutils/git-backup/src/opnsense/mvc/app/library/OPNsense/Backup/Git.php b/sysutils/git-backup/src/opnsense/mvc/app/library/OPNsense/Backup/Git.php index 7bf0d47f1..f093a3b82 100644 --- a/sysutils/git-backup/src/opnsense/mvc/app/library/OPNsense/Backup/Git.php +++ b/sysutils/git-backup/src/opnsense/mvc/app/library/OPNsense/Backup/Git.php @@ -30,6 +30,7 @@ namespace OPNsense\Backup; +use OPNsense\Core\Backend; use OPNsense\Core\Config; use OPNsense\Backup\GitSettings; @@ -59,6 +60,13 @@ class Git extends Base implements IBackupProvider "help" => gettext("Target location, which defined transport protocol, such as ssh://server/project.git or https://server/project.git."), "value" => null ], + [ + "name" => "branch", + "type" => "text", + "label" => gettext("Branch"), + "help" => gettext("Target branch to push to."), + "value" => null + ], [ "name" => "privkey", "type" => "textarea", @@ -110,11 +118,73 @@ class Git extends Base implements IBackupProvider } /** - * @inheritdoc + * Backup is responsible for initialising the local repo and pusing it to upstream. + * To ensure initial content, we should trigger a 'system event config_changed' which should enforce a + * add + commit in our (newly created) repo. + * + * Since our backup method is also called from the userinterface directly, we should try to prevent the need + * for elevated rights. Since all actions are concentrated within the config directory, we only need read/exec + * access on git. (detaching this further would deviate the implementation from the existing ones) + * + * @return array filelist */ public function backup() { - return ['config.xml']; + $targetdir = "/conf/backup/git"; + $git = "/usr/local/bin/git"; + $mdl = new GitSettings(); + if (!is_dir($targetdir)) { + mkdir($targetdir); + } + if (!is_dir('{$targetdir}/.git')) { + exec("{$git} init {$targetdir}"); + } + // XXX: since our git backup is plain text and already contains the private key, it doesn't really matter + // to keep the same key in the git directory (we're not going to push it) + $ident_file = "{$targetdir}/identity"; + $privkey = trim(str_replace("\r", "", (string)$mdl->privkey)) . "\n"; + file_put_contents($ident_file, $privkey); + chmod("{$targetdir}/identity", 0600); + // When there are unprocessed config backups, flush them out. + (new Backend())->configdRun("system event config_changed"); + // configure upstream + exec("cd {$targetdir} && ". + "{$git} config core.sshCommand ". + "\"ssh -i {$ident_file} -o StrictHostKeyChecking=accept-new -o PasswordAuthentication=no\"" + ); + $url = (string)$mdl->url; + $pos = strpos($url, '//'); + // inject credentials in url (either username or username:password, depending on transport) + if (stripos(trim((string)$mdl->$url),'http')) { + $cred = urlencode((string)$mdl->user) . ":" . urlencode((string)$mdl->password); + $url = substr($url,0, $pos+2) . "{$cred}@" . substr($url, $pos+2); + } else { + $url = substr($url,0, $pos+2) . urlencode((string)$mdl->user) . "@" . substr($url, $pos+2); + } + exec("cd {$targetdir} && git remote remove origin"); + exec("cd {$targetdir} && git remote add origin ". escapeshellarg($url)); + $pushtxt = shell_exec( + "(cd {$targetdir} && git push origin " . escapeshellarg("master:{$mdl->branch}") . + " && echo '__exit_ok__') 2>&1" + ); + if (strpos($pushtxt, '__exit_ok__')) { + $error_type = null; + } elseif (strpos($pushtxt, 'Permission denied')) { + $error_type = "authentication failure"; + } elseif (strpos($pushtxt, 'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED')) { + $error_type = "ssh hostkey changed"; + } elseif (strpos($pushtxt, "remote contains work that you do")) { + $error_type = "git out of sync"; + } else { + $error_type = "unknown error, check log for details"; + } + if (!empty($error_type)) { + syslog(LOG_ERR, "git-backup {$error_type} (".str_replace("\n", " ", $pushtxt).")"); + throw new \Exception($error_type); + } else { + // return filelist in git + return explode("\n", shell_exec("cd {$targetdir} && git ls-files")); + } } /** diff --git a/sysutils/git-backup/src/opnsense/mvc/app/models/OPNsense/Backup/GitSettings.xml b/sysutils/git-backup/src/opnsense/mvc/app/models/OPNsense/Backup/GitSettings.xml index 84f9f0fc4..52c88d582 100644 --- a/sysutils/git-backup/src/opnsense/mvc/app/models/OPNsense/Backup/GitSettings.xml +++ b/sysutils/git-backup/src/opnsense/mvc/app/models/OPNsense/Backup/GitSettings.xml @@ -29,6 +29,10 @@ + + master + Y + N