diff options
Diffstat (limited to 'modules/by-name/ba/backup')
4 files changed, 158 insertions, 38 deletions
diff --git a/modules/by-name/ba/backup/module.nix b/modules/by-name/ba/backup/module.nix index 3e07fbd1..8d7d6bbb 100644 --- a/modules/by-name/ba/backup/module.nix +++ b/modules/by-name/ba/backup/module.nix @@ -16,40 +16,66 @@ cfg = config.soispha.services.backup; in { options.soispha.services.backup = { - enable = lib.mkEnableOption "backups via restic to a storagebox"; + storagebox = { + enable = lib.mkEnableOption "remote backups"; + user = lib.mkOption { + type = lib.types.str; + description = "The storagebox-user to use"; + example = "u384702-sub2"; + }; - user = lib.mkOption { - type = lib.types.str; - description = "The storagebox-user to use"; - example = "u384702-sub2"; - }; - privateSshKey = lib.mkOption { - type = lib.types.path; - description = "The age-encrypted ssh-key, passed to agenix"; + sshKey = lib.mkOption { + type = lib.types.path; + description = "The age-encrypted ssh-key, passed to agenix"; + default = ./secrets/storagebox/ssh_key.age; + }; + + repositoryPassword = lib.mkOption { + type = lib.types.path; + description = "The age-encrypted restic password, passed to agenix"; + default = ./secrets/storagebox/repository_password.age; + }; }; - privatePassword = lib.mkOption { - type = lib.types.path; - description = "The age-encrypted restic password, passed to agenix"; + + local = { + enable = lib.mkEnableOption "local backups"; + + repositoryPassword = lib.mkOption { + type = lib.types.path; + description = "The age-encrypted restic password, passed to agenix"; + default = ./secrets/local/repository_password.age; + }; + + backupDiskUuid = lib.mkOption { + example = lib.literalExpression "d1d20ae7-3d8a-44da-86da-677dbbb10c89"; + description = "The UUID of the backup disk"; + }; }; }; - config = lib.mkIf cfg.enable { + config = { age.secrets = { - resticpass = { - file = cfg.privatePassword; + resticStorageboxSshKey = lib.mkIf cfg.storagebox.enable { + file = cfg.storagebox.sshKey; + mode = "0700"; + owner = "root"; + group = "root"; + }; + resticStorageboxRepositoryPassword = lib.mkIf cfg.storagebox.enable { + file = cfg.storagebox.repositoryPassword; mode = "0700"; owner = "root"; group = "root"; }; - resticssh = { - file = cfg.privateSshKey; + resticLocalRepositoryPassword = lib.mkIf cfg.local.enable { + file = cfg.local.repositoryPassword; mode = "0700"; owner = "root"; group = "root"; }; }; - soispha.programs.ssh = { + soispha.programs.ssh = lib.mkIf cfg.storagebox.enable { enable = true; rootKnownHosts = { "[u459143-sub1.your-storagebox.de]:23" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIICf9svRenC/PLKIL9nk6K/pxQgoiFC41wTNvoIncOxs"; @@ -59,30 +85,68 @@ in { services.restic.backups = let snapshotDir = "/srv/last_snapshot"; homeDir = "${snapshotDir}/home"; + + paths = [ + snapshotDir + ]; + exclude = [ + "${homeDir}/soispha/.cache" + ]; + extraBackupArgs = [ + "--verbose=2" + ]; + + backupPrepareCommand = extra: + # bash + extra + + '' + [ -d "${snapshotDir}" ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete "${snapshotDir}" + + # -r := Make the snapshot read-only + ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /srv "${snapshotDir}"; + ''; in { - storagebox = { + local = let + backupMountPoint = "/run/media/${cfg.local.backupDiskUuid}"; + in + lib.mkIf cfg.local.enable { + inhibitsSleep = true; + initialize = true; + + inherit paths exclude extraBackupArgs; + + # TODO: We could maybe use systemd's built-in system for this mounting. <2025-05-01> + backupPrepareCommand = + backupPrepareCommand + # bash + '' + set -xeu + ${lib.getExe' pkgs.util-linux "mount"} --mkdir "/dev/disk/by-uuid/${cfg.local.backupDiskUuid}" "${backupMountPoint}" + ''; + backupCleanupCommand = + # bash + '' + ${lib.getExe' pkgs.util-linux "umount"} "${backupMountPoint}" + ''; + + passwordFile = config.age.secrets.resticLocalRepositoryPassword.path; + + repository = "${backupMountPoint}/restic-backup-data/"; + + # Start on demand. + timerConfig = null; + }; + + storagebox = lib.mkIf cfg.storagebox.enable { + inhibitsSleep = true; initialize = true; - backupPrepareCommand = - # bash - '' - [ -d "${snapshotDir}" ] && ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume delete "${snapshotDir}" - - # -r := Make the snapshot read-only - ${lib.getExe' pkgs.btrfs-progs "btrfs"} subvolume snapshot -r /srv "${snapshotDir}"; - ''; - paths = [ - snapshotDir - ]; - exclude = [ - "${homeDir}/soispha/.cache" - ]; - extraBackupArgs = [ - "--verbose=2" - ]; - passwordFile = config.age.secrets.resticpass.path; + inherit paths exclude extraBackupArgs; + backupPrepareCommand = backupPrepareCommand ""; + + passwordFile = config.age.secrets.resticStorageboxRepositoryPassword.path; extraOptions = [ - "rclone.program='ssh -p 23 ${cfg.user}@${cfg.user}.your-storagebox.de -i ${config.age.secrets.resticssh.path} command_forced_on_remote'" + "rclone.program='ssh -p 23 ${cfg.storagebox.user}@${cfg.storagebox.user}.your-storagebox.de -i ${config.age.secrets.resticStorageboxSshKey.path} command_forced_on_remote'" ]; # This setting is normally passed to rclone, but we force diff --git a/modules/by-name/ba/backup/secrets/local/repository_password.age b/modules/by-name/ba/backup/secrets/local/repository_password.age new file mode 100644 index 00000000..b1508e49 --- /dev/null +++ b/modules/by-name/ba/backup/secrets/local/repository_password.age @@ -0,0 +1,20 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqVCtBZzNBWFlxQnhlb3c2 +NG0xMjV3V2hQUWhNb01ROGpFbzM0c3NjRWlvCjRqMTdxNlNsY1lDM2VCUGRjcldZ +SUhScGRSejY3TFlwVjlweTRERkU4ZTQKLT4gc3NoLWVkMjU1MTkgelpFb25nIGtj +cVN4RzFseTJCVXhOc0tEWi96ckpGQjhNdUZrb1FVWnJ6TU1vSHZCMUkKc1JwM2Ir +TzNzZEV5VEE4QUp4cDdTNWZvTFlZR0tUbUFZTnNEZE1McnhISQotPiBzc2gtZWQy +NTUxOSA3SGZGVXcgYVQzRHdaa1NYUUpLODUyL0orV3d4Z1o1V2hhekZXVWFzNW50 +enhSYnlWbwpzbnZwK1dSczJ5SGZsRjNyRWZraUcreWlBakZOZkVyZ01CWml5V0E4 +TzY4Ci0+ICpMUC1ncmVhc2UgV0IKUTZVMzR1QTVGdWRyNWprVVpjaWhCTTFSRzY3 +bHVtdHpJQXlWQVFHZ3FzbkZncHRsQnJIaHlEekl5ZE15Sko0YQpSVEUKLS0tIGFy +N1Y2OUREY1NOL2xPODd4Y3Y4Wk83NFJ5ZVBpNnJYelo3RmZPL1V4Q28KcA+6qt2d +LrL0FS279XL1uqSeXiDdBBBh5i951dRx9ML33g7IoQxaGqSWU0yP/y7fcSp3B/rT +/alQGRL/uYnCS2z8zCngr2YCQBXAI8bOgf+th7fzc15FXNB14WMGvrCVRB8Rr6JZ +itTxcAebY192xeINJG269iL6Ef0YrmZLUufKQ9lcVob/G32tG0vxSbSBBx3asf7y +toiNdlXpxJPrIpFx5mc3dnkaXCTSRuXjiAqnncW/HNxovCI90sY3dyL+XRC8iEOD +sql7C7jUUvLubptk1gGW5pHmGvqeCLr+fJ6XrVqXE5VxOSbzvSUsdS84WOBInLiO +V36aUtthEzdRXd81P/n7U26O93GWpIwzctu0WwSHHHsPKhxqFY4RuWa3mO4Zk6fu +O6fsYnpAqYiCUd/k2zKrDtLosTnwuwc7Vm1glm4DEEtvteVibw47SR1LLfUds5XL +j1OR7Z6/ljIEKPjE1a81pQjYRfHDUtceXUvrlL6iCID30zU= +-----END AGE ENCRYPTED FILE----- diff --git a/modules/by-name/ba/backup/secrets/storagebox/repository_password.age b/modules/by-name/ba/backup/secrets/storagebox/repository_password.age new file mode 100644 index 00000000..a2aa984a --- /dev/null +++ b/modules/by-name/ba/backup/secrets/storagebox/repository_password.age @@ -0,0 +1,14 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzbnJ5Z3NXRFRtdEdCNk1z +NG9Cb2FUTEZ2U2hTdUptWGpneHd2RGxGQlY0ClhqcHlMSUZlLzBPZko4a2hKdnE1 +Q1hnc3ErWlJiSk1HVXFLcSswQlV0WTAKLT4gc3NoLWVkMjU1MTkgelpFb25nIG5I +cFpMY21ZdU9TUHY5VksraG1xeFByQlM1WE1INEk0SWFJQzIvSXhZbXMKSWxiNlIr +NjZ6bEgySUJGdnpoZTY2MmxxajJMaUVTM3RkTmhpdkxaRnNCQQotPiBzc2gtZWQy +NTUxOSA3SGZGVXcgRGtLMHFORm9mUDUwZnBXM0djcFlxTExJbWVDeDJmbVVSRi9l +QTFmekpROApZY3Z0Zlc1SUdCcllKQ01ZNmdFQkI3UnViVGxzNTJNUkhqc2hOejY3 +MndrCi0+IG1Hbi1ncmVhc2UgUDJJdgpGUmVHa1NPWGtzUmZmMXM3eWkxVnJkek1h +MUxZeS9qMlArendXZ1ltdFgvMTBabTlPM2hzSjhuVXBQNktHKzc1ClF6bjZob09B +Ci0tLSB3dEZpbnpVR0pPdGpFVmNyM0JzUGl6eGcreXpkTG9xQUc5S09ndUthaEpR +CuHgXCIq/AUCuIQWepBRapAab6zczOwxEBpDv5R2kw2mw34UVRLuFs7hB1cHE79y +7Fc= +-----END AGE ENCRYPTED FILE----- diff --git a/modules/by-name/ba/backup/secrets/storagebox/ssh_key.age b/modules/by-name/ba/backup/secrets/storagebox/ssh_key.age new file mode 100644 index 00000000..a7f30c7c --- /dev/null +++ b/modules/by-name/ba/backup/secrets/storagebox/ssh_key.age @@ -0,0 +1,22 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJOTFaQVpnQ2VJN1pJbmdu +STF2UTF1Y3lyU2p3OUJlcWRFWlg3Mm9vSEFFCjg4RklaZ0hqdWdZSlJ0dGx0ckJN +eHE4R1QyTEJEVkZudnVQV2VpblBYTW8KLT4gc3NoLWVkMjU1MTkgelpFb25nIDR0 +Z2RwTXJGV1VYT2Y1OUcxbXpnOGNEbG9BWmFxeWUzZ1huNW1YQk5BM3cKK1hLNnYx +VnNvRDZWM3Y4VUpCQTRtbHJOcDJLdTBOUXJ1ak52Mkp3TE9OcwotPiBzc2gtZWQy +NTUxOSA3SGZGVXcgSHFSOXM1Vm1lY0ErRkZobndEU0xvQjVXbnhDbUlGNmE2dStY +SEFqSk9TMAo2VU5UbW9rc29kendvcHNuU3hFQ2lBQWNjRUxUSE10RmtjbkdwWGlv +cTRVCi0+IDwtZ3JlYXNlIHNPT3N1IHNkZzcoQzUgLUM3Ml1Tcgorb3FQcWlDSkJQ +L1g4WisydkYxdnJMeDNISVVDTGlZdmUyRkV0TkFUdW4ycXZBSlF3UWM2R3FTYjRs +ckEyaTg2ClVrbwotLS0gdHF5Q3BqV3h2TzEySCs4OFNIYmx1U0hsTWo1RDlTV3VC +T1RmelZaWXh4YwoeRkYbirDGX9aZj6OVxZ8sVsm2Sz23ikcBhAdTIXKErM7eQcdp +Y2nA5kUebPVznbSq/XSQNbqvCfSv9wVVTZWww7AqkrltjFS8yxQVMKB37IfnJsZI +92OCjJKArxAbvS7ERKY51Cf9oY/VqGk5WqxExH5ZvdbL144UI8JzrxhDopVXBWNd +fCrHirRAdlu3wgVILXV3vk1kmE69sspuV4cer5f/kOBKT1w09s2NFFHH4TO3cLTJ +Obgi/HWYHBjqGSgwd4bDtNOW2UDE1LN3YeUhloutWhuX8yt+NFzZJtl+CBrRqLKA +0CK+ZPKPWWKYPCNlzKT4vDF9BZeyCfoEj9dTXvxrNPEB6YYi16aQ8DhI8wxADl41 +ShrULlEmB8rfz7V+8TFmERKISBRSSH0p+XJi8e2DWh2dSco16rizmbFFNDdcYiGT +WWbXYCFNzj+kNFI/vEtt9dEWmfXlgg01khnyoOuRLedtoCDSuF+8z7YYy6oYt/2h +gmf4NHOUgyxK56c17JMQ3xT97d2odgqmwB+YZQ13IJCjt3M3pmSssD4sTgmA5alH +vtUlci6dZdO801h5vOesbAJplXCEyw== +-----END AGE ENCRYPTED FILE----- |