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