Add remote borg backup support (#4804)

Signed-off-by: Tim Diels <tim@diels.me>
Signed-off-by: Simon L. <szaimen@e.mail.de>
Co-authored-by: Simon L. <szaimen@e.mail.de>
This commit is contained in:
Tim Diels 2024-11-07 22:19:56 +01:00 committed by GitHub
parent 34a264d945
commit 3eeda1ea91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 402 additions and 159 deletions

View file

@ -28,15 +28,17 @@ readonly class ConfigurationController {
$this->configurationManager->ChangeMasterPassword($currentMasterPassword, $newMasterPassword);
}
if (isset($request->getParsedBody()['borg_backup_host_location'])) {
if (isset($request->getParsedBody()['borg_backup_host_location']) || isset($request->getParsedBody()['borg_remote_repo'])) {
$location = $request->getParsedBody()['borg_backup_host_location'] ?? '';
$this->configurationManager->SetBorgBackupHostLocation($location);
$borgRemoteRepo = $request->getParsedBody()['borg_remote_repo'] ?? '';
$this->configurationManager->SetBorgLocationVars($location, $borgRemoteRepo);
}
if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_password'])) {
if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_remote_repo']) || isset($request->getParsedBody()['borg_restore_password'])) {
$restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? '';
$borgRemoteRepo = $request->getParsedBody()['borg_restore_remote_repo'] ?? '';
$borgPassword = $request->getParsedBody()['borg_restore_password'] ?? '';
$this->configurationManager->SetBorgRestoreHostLocationAndPassword($restoreLocation, $borgPassword);
$this->configurationManager->SetBorgRestoreLocationVarsAndPassword($restoreLocation, $borgRemoteRepo, $borgPassword);
}
if (isset($request->getParsedBody()['daily_backup_time'])) {
@ -132,8 +134,8 @@ readonly class ConfigurationController {
$this->configurationManager->SetCollaboraDictionaries($collaboraDictionaries);
}
if (isset($request->getParsedBody()['delete_borg_backup_host_location'])) {
$this->configurationManager->DeleteBorgBackupHostLocation();
if (isset($request->getParsedBody()['delete_borg_backup_location_vars'])) {
$this->configurationManager->DeleteBorgBackupLocationVars();
}
return $response->withStatus(201)->withHeader('Location', '/');

View file

@ -439,48 +439,61 @@ class ConfigurationManager
/**
* @throws InvalidSettingConfigurationException
*/
public function SetBorgBackupHostLocation(string $location) : void {
$isValidPath = false;
if (str_starts_with($location, '/') && !str_ends_with($location, '/')) {
$isValidPath = true;
} elseif ($location === 'nextcloud_aio_backupdir') {
$isValidPath = true;
}
if (!$isValidPath) {
throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!");
}
public function SetBorgLocationVars(string $location, string $repo) : void {
$this->ValidateBorgLocationVars($location, $repo);
$config = $this->GetConfig();
$config['borg_backup_host_location'] = $location;
$config['borg_remote_repo'] = $repo;
$this->WriteConfig($config);
}
public function DeleteBorgBackupHostLocation() : void {
$config = $this->GetConfig();
$config['borg_backup_host_location'] = '';
$this->WriteConfig($config);
}
/**
* @throws InvalidSettingConfigurationException
*/
public function SetBorgRestoreHostLocationAndPassword(string $location, string $password) : void {
if ($location === '') {
throw new InvalidSettingConfigurationException("Please enter a path!");
private function ValidateBorgLocationVars(string $location, string $repo) : void {
if ($location === '' && $repo === '') {
throw new InvalidSettingConfigurationException("Please enter a path or a remote repo url!");
} elseif ($location !== '' && $repo !== '') {
throw new InvalidSettingConfigurationException("Location and remote repo url are mutually exclusive!");
}
$isValidPath = false;
if (str_starts_with($location, '/') && !str_ends_with($location, '/')) {
$isValidPath = true;
} elseif ($location === 'nextcloud_aio_backupdir') {
$isValidPath = true;
}
if ($location !== '') {
$isValidPath = false;
if (str_starts_with($location, '/') && !str_ends_with($location, '/')) {
$isValidPath = true;
} elseif ($location === 'nextcloud_aio_backupdir') {
$isValidPath = true;
}
if (!$isValidPath) {
throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!");
if (!$isValidPath) {
throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!");
}
} else {
$this->ValidateBorgRemoteRepo($repo);
}
}
private function ValidateBorgRemoteRepo(string $repo) : void {
$commonMsg = "For valid urls, see the remote examples at https://borgbackup.readthedocs.io/en/stable/usage/general.html#repository-urls";
if ($repo === "") {
// Ok, remote repo is optional
} elseif (!str_contains($repo, "@")) {
throw new InvalidSettingConfigurationException("The remote repo must contain '@'. $commonMsg");
} elseif (!str_contains($repo, ":")) {
throw new InvalidSettingConfigurationException("The remote repo must contain ':'. $commonMsg");
}
}
public function DeleteBorgBackupLocationVars() : void {
$config = $this->GetConfig();
$config['borg_backup_host_location'] = '';
$config['borg_remote_repo'] = '';
$this->WriteConfig($config);
}
/**
* @throws InvalidSettingConfigurationException
*/
public function SetBorgRestoreLocationVarsAndPassword(string $location, string $repo, string $password) : void {
$this->ValidateBorgLocationVars($location, $repo);
if ($password === '') {
throw new InvalidSettingConfigurationException("Please enter the password!");
@ -488,6 +501,7 @@ class ConfigurationManager
$config = $this->GetConfig();
$config['borg_backup_host_location'] = $location;
$config['borg_remote_repo'] = $repo;
$config['borg_restore_password'] = $password;
$config['instance_restore_attempt'] = 1;
$this->WriteConfig($config);
@ -582,6 +596,23 @@ class ConfigurationManager
return $config['borg_backup_host_location'];
}
public function GetBorgRemoteRepo() : string {
$config = $this->GetConfig();
if(!isset($config['borg_remote_repo'])) {
$config['borg_remote_repo'] = '';
}
return $config['borg_remote_repo'];
}
public function GetBorgPublicKey() : string {
if (!file_exists(DataConst::GetBackupPublicKey())) {
return "";
}
return trim(file_get_contents(DataConst::GetBackupPublicKey()));
}
public function GetBorgRestorePassword() : string {
$config = $this->GetConfig();
if(!isset($config['borg_restore_password'])) {

View file

@ -23,6 +23,10 @@ class DataConst {
return self::GetDataDirectory() . '/configuration.json';
}
public static function GetBackupPublicKey() : string {
return self::GetDataDirectory() . '/id_borg.pub';
}
public static function GetBackupSecretFile() : string {
return self::GetDataDirectory() . '/backupsecret';
}

View file

@ -265,6 +265,8 @@ readonly class DockerActionManager {
$replacements[1] = $this->configurationManager->GetBaseDN();
} elseif ($out[1] === 'AIO_TOKEN') {
$replacements[1] = $this->configurationManager->GetToken();
} elseif ($out[1] === 'BORGBACKUP_REMOTE_REPO') {
$replacements[1] = $this->configurationManager->GetBorgRemoteRepo();
} elseif ($out[1] === 'BORGBACKUP_MODE') {
$replacements[1] = $this->configurationManager->GetBackupMode();
} elseif ($out[1] === 'AIO_URL') {