From 23fc74efa686b3fb451b8e6662fbd49520dc1098 Mon Sep 17 00:00:00 2001 From: Julien Malka Date: Tue, 3 Sep 2024 22:03:09 +0200 Subject: [PATCH] feat: init backup module --- machines/akhaten/stalwart.nix | 2 + machines/gustave/borg.nix | 16 ++++ machines/gustave/default.nix | 1 + modules/backup/default.nix | 135 +++++++++++++++++++++++++++++ secrets/borg-encryption-secret.age | 7 ++ secrets/borg-ssh-priv.age | Bin 0 -> 721 bytes secrets/secrets.nix | 8 ++ 7 files changed, 169 insertions(+) create mode 100644 machines/gustave/borg.nix create mode 100644 modules/backup/default.nix create mode 100644 secrets/borg-encryption-secret.age create mode 100644 secrets/borg-ssh-priv.age diff --git a/machines/akhaten/stalwart.nix b/machines/akhaten/stalwart.nix index 8a5b288..62b7cf9 100644 --- a/machines/akhaten/stalwart.nix +++ b/machines/akhaten/stalwart.nix @@ -57,6 +57,8 @@ }; }; + services.backup.includes = [ "/var/lib/stalwart-mail/db" ]; + age.secrets.stalwart-admin-hash = { file = ../../secrets/stalwart-admin.age; path = "/var/lib/stalwart-mail/admin-hash"; diff --git a/machines/gustave/borg.nix b/machines/gustave/borg.nix new file mode 100644 index 0000000..97ac2c8 --- /dev/null +++ b/machines/gustave/borg.nix @@ -0,0 +1,16 @@ +{ pkgs, ... }: +{ + users.users.borg = { + home = "/home/borg"; + group = "borg"; + isNormalUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAziNbLEO9D69xUGPGEq3eXYauFuOlvhqQTwpLNKjFqs julien@tower" + ]; + + }; + users.groups.borg = { }; + + environment.systemPackages = with pkgs; [ borgbackup ]; + +} diff --git a/machines/gustave/default.nix b/machines/gustave/default.nix index 7de9cdb..78e2e73 100644 --- a/machines/gustave/default.nix +++ b/machines/gustave/default.nix @@ -9,6 +9,7 @@ ./hardware.nix ./home-julien.nix ./nsd.nix + ./borg.nix ]; machine.meta = { diff --git a/modules/backup/default.nix b/modules/backup/default.nix new file mode 100644 index 0000000..8b2a6a5 --- /dev/null +++ b/modules/backup/default.nix @@ -0,0 +1,135 @@ +{ + lib, + config, + ... +}: + +let + cfg = config.services.backup; + # We backup on gustave + host = "gustave.luj"; + port = "45"; + hostPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJrHUzjPX0v2FX5gJALCjEJaUJ4sbfkv8CBWc6zm0Oe"; + sshKey = config.age.secrets."borg-ssh-key".path; + secretPath = config.age.secrets."borg-encryption-secret".path; + +in +{ + options.services.backup = + with lib; + with types; + { + quota = mkOption { + type = nullOr str; + default = null; + example = "90G"; + description = '' + Quota for the borg repository. Useful to prevent the target disk from running full and ensuring borg keeps some space to work with. + ''; + }; + + includes = mkOption { + type = listOf path; + default = [ ]; + description = '' + Paths to include in the backup. + ''; + }; + + excludes = mkOption { + type = listOf path; + default = [ ]; + description = '' + Paths to exclude in the backup. + ''; + }; + + preHook = mkOption { + type = lines; + default = ""; + description = '' + Shell commands to run before the backup. + ''; + }; + + postHook = mkOption { + type = lines; + default = ""; + description = '' + Shell commands to run after the backup. + ''; + }; + + wantedUnits = mkOption { + type = listOf str; + default = [ ]; + description = '' + List of units to require before starting the backup. + ''; + }; + }; + + config = lib.mkIf (cfg.includes != [ ]) { + + age.secrets."borg-ssh-key" = { + file = ../../secrets/borg-ssh-priv.age; + owner = "root"; + mode = "0600"; + }; + + age.secrets."borg-encryption-secret".file = ../../secrets/borg-encryption-secret.age; + + programs.ssh.knownHosts."${if port != 22 then "[${host}]:${port}" else host}" = { + publicKey = "${hostPublicKey}"; + }; + + systemd.services.borgbackup-job-state = { + wants = cfg.wantedUnits; + after = cfg.wantedUnits; + }; + + systemd.timers.borgbackup-job-state.timerConfig = { + # Spread all backups over the day + RandomizedDelaySec = "30m"; + FixedRandomDelay = true; + }; + + services.borgbackup.jobs.state = { + inherit (cfg) preHook postHook; + + # Create the repo + doInit = true; + + # Create daily backups, but prune to a reasonable amount + startAt = [ "hourly" ]; + prune.keep = { + daily = 7; + weekly = 4; + monthly = 3; + }; + + # What to backup + paths = cfg.includes; + exclude = cfg.excludes; + + # Where to backup it to + repo = "borg@gustave.luj:${config.networking.hostName}"; + environment.BORG_RSH = "ssh -p ${port} -i ${sshKey}"; + + # Ensure we don't fill up the destination disk + extraInitArgs = lib.optionalString (cfg.quota != null) "--storage-quota ${cfg.quota}"; + + # Authenticated & encrypted, key resides in the repository + encryption = { + mode = "repokey-blake2"; + passCommand = "cat ${secretPath}"; + }; + + # Reduce the backup size + compression = "auto,zstd"; + + # Show summary detailing data usage once completed + extraCreateArgs = "--stats"; + }; + }; +} diff --git a/secrets/borg-encryption-secret.age b/secrets/borg-encryption-secret.age new file mode 100644 index 0000000..0bde513 --- /dev/null +++ b/secrets/borg-encryption-secret.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 81O5Zw WCIdHHk7nehDc0n0u4Df17oVi2OIg+WT2FxM9uUMBTM +ZufMHxTCazJycMSqHYKiGoRQUR+mPwXi/xsZQxIkjUM +-> ssh-ed25519 AqX2tg kYJ4UZixeb7WjkXzdtS6Bgm0JQBLNUdONJbjsTtkaQM +2H4qDAAlSJ3TNOF/UFOBlNnyAy1BclMvTv58fMJctuo +--- s3weoMhZdAdfJ4rTU5E3x56PhlM6N3JfbdJOtQFjACM +FC"a^C2b=bg$M A~T4` '.rPeC \ No newline at end of file diff --git a/secrets/borg-ssh-priv.age b/secrets/borg-ssh-priv.age new file mode 100644 index 0000000000000000000000000000000000000000..42dacac17aafc3727743f4b35a9830849b8de947 GIT binary patch literal 721 zcmV;?0xtbwXJsvAZewzJaCB*JZZ2KHEdc+Y)Va7b$3xRPfRvJ3N0-yAaQb7WHfqs zQZP?-OjlQWT5(TBYIJBr$HFP#?I74ArY)fZ!F+z273iF*3r(27OIyOtD%-A?w zC<5g~_m}NX)&kDoRvnLuEW|CZ5Zy_|8_}w8{Au|xgXnR9AVa)vqDl4c>$ZEQaZG9b zq#Cy5Wy;;}^lNT;-`eNYpmrFi+QmoD{Pj|jgX)9a0_we)lo*M5c=CI>Uk3ABS^JHG z`u1r>0o3UL#?O+1VM zne^Q8yjd>^N8$nm?n`-NFMFrQaq{77gbi>0%pYuh73Hz?#Nv7%T!3g#J82aG1lp)m zPqAt7U-80cdwj4Aj8Z7=@)0w>JvD|{JqOdX+G~MfT4hq zl)IL=3rwC`X;e&7x%XDCAqwqNgu7>p)P*5>*V~j3HEasou{mt3s&~Szb_V@#*X)exl7hQtiz(8E;|3+qTJbIOROD3A Dnh!(f literal 0 HcmV?d00001 diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 25ee09c..f4e363a 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -81,4 +81,12 @@ in tower ]; "arkheon-token.age".publicKeys = servers; + "borg-ssh-priv.age".publicKeys = [ + akhaten + tower + ]; + "borg-encryption-secret.age".publicKeys = [ + akhaten + tower + ]; }