From 21c94df25a19d34ff52aee22ef2d490d80d5729d Mon Sep 17 00:00:00 2001 From: matt1432 Date: Sat, 2 Mar 2024 22:04:23 -0500 Subject: [PATCH] feat(servers): setup borgbackup repos --- common/default.nix | 2 +- devices/cluster/modules/unbound.nix | 3 - devices/nos/default.nix | 1 + devices/nos/hardware-configuration.nix | 7 - devices/nos/modules/arion/default.nix | 8 +- modules/borgbackup.nix | 87 -------- modules/borgbackup/default.nix | 20 ++ modules/borgbackup/module.nix | 268 +++++++++++++++++++++++++ 8 files changed, 294 insertions(+), 102 deletions(-) delete mode 100644 modules/borgbackup.nix create mode 100644 modules/borgbackup/default.nix create mode 100644 modules/borgbackup/module.nix diff --git a/common/default.nix b/common/default.nix index 58aef8d..3ac864f 100644 --- a/common/default.nix +++ b/common/default.nix @@ -19,7 +19,7 @@ home-manager.nixosModules.home-manager ../modules/arion - ../modules/borgbackup.nix + ../modules/borgbackup ]; nixpkgs = { diff --git a/devices/cluster/modules/unbound.nix b/devices/cluster/modules/unbound.nix index 91597b3..e2bc970 100644 --- a/devices/cluster/modules/unbound.nix +++ b/devices/cluster/modules/unbound.nix @@ -18,7 +18,6 @@ in { # Custom DNS local-zone = [ - "pve.nelim.org redirect" "headscale.nelim.org redirect" "git.nelim.org redirect" "mc.nelim.org transparent" @@ -34,8 +33,6 @@ in { then "100.64.0.8" else "100.64.0.9"; in [ - "\"pve.nelim.org IN A 100.64.0.4\"" - "\"headscale.nelim.org. IN A ${wanIP}\"" "\"git.nelim.org. IN A ${wanIP}\"" diff --git a/devices/nos/default.nix b/devices/nos/default.nix index deaa699..32f5c2d 100644 --- a/devices/nos/default.nix +++ b/devices/nos/default.nix @@ -26,6 +26,7 @@ in { extraGroups = [ "wheel" "adm" + "borg" ]; }; diff --git a/devices/nos/hardware-configuration.nix b/devices/nos/hardware-configuration.nix index 5f2ff51..2c575a1 100644 --- a/devices/nos/hardware-configuration.nix +++ b/devices/nos/hardware-configuration.nix @@ -42,13 +42,6 @@ }; }; - swapDevices = [ - { - device = "/var/lib/swapfile"; - size = 16 * 1024; - } - ]; - zramSwap.enable = true; hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware; diff --git a/devices/nos/modules/arion/default.nix b/devices/nos/modules/arion/default.nix index 297ea7e..729fca9 100644 --- a/devices/nos/modules/arion/default.nix +++ b/devices/nos/modules/arion/default.nix @@ -29,8 +29,8 @@ in { rwDataDir = configPath; }; - #services.borgbackup.configs.arion = { - # paths = [configPath]; - # exclude = ["**/lineageos*"]; - #}; + services.borgbackup.configs.arion = { + paths = [configPath]; + exclude = ["**/lineageos*"]; + }; } diff --git a/modules/borgbackup.nix b/modules/borgbackup.nix deleted file mode 100644 index c3de95e..0000000 --- a/modules/borgbackup.nix +++ /dev/null @@ -1,87 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - inherit (lib) filterAttrs mapAttrs mkDefault mkIf mkOption types; - - cfg = config.services.borgbackup; - secrets = config.sops.secrets; -in { - # Make this file declare default settings - options.services.borgbackup = { - defaults = mkOption { - type = types.attrs; - }; - configs = mkOption { - type = types.attrs; - default = {}; - }; - }; - - config = { - # TODO: change this to nos - programs.ssh.knownHosts = { - pve.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/4mrp8E4Ittwg8feRmPtDHSDR2+Pq4uZHeF5MweVcW"; - }; - - services.borgbackup = mkIf (cfg.configs != {}) { - defaults = { - environment = mkDefault {BORG_RSH = "ssh -i ${secrets.borg-ssh.path}";}; - - # TODO: change this to nos - repo = mkDefault "ssh://matt@pve/data/backups/borg"; - encryption = mkDefault { - mode = "repokey"; - passCommand = let - cat = "${pkgs.coreutils}/bin/cat"; - key = secrets.borg-repo.path; - in "${cat} ${key}"; - }; - - # Run every 3 hours - startAt = mkDefault "00/3:00"; - compression = mkDefault "auto,lzma"; - }; - - jobs = let - tempJobs = mapAttrs (_: v: cfg.defaults // v) cfg.configs; - in - mapAttrs (n: v: let - attrs = filterAttrs (n: _: n != "preHook" || n != "postHook" || n != "paths") v; - pathPrefix = "/root/snaps"; - snapPath = "${pathPrefix}/${n}"; - in - attrs - // { - paths = map (x: snapPath + x) v.paths; - - preHook = - v.preHook - or "" - + - /* - bash - */ - '' - if [[ ! -d ${pathPrefix} ]]; then - mkdir -p ${pathPrefix} - fi - - ${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r / ${snapPath} - ''; - - postHook = - /* - bash - */ - '' - ${pkgs.btrfs-progs}/bin/btrfs subvolume delete ${snapPath} - '' - + v.postHook or ""; - }) - tempJobs; - }; - }; -} diff --git a/modules/borgbackup/default.nix b/modules/borgbackup/default.nix new file mode 100644 index 0000000..954238b --- /dev/null +++ b/modules/borgbackup/default.nix @@ -0,0 +1,20 @@ +{...}: { + imports = [./module.nix]; + + services.borgbackup = { + existingRepos = [ + { + name = "arion"; + authorizedKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPijoxuSwH9IrS4poewzHHwe64UoX4QY7Qix5VhEdqKR root@servivi" + ]; + } + { + name = "mc"; + authorizedKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPijoxuSwH9IrS4poewzHHwe64UoX4QY7Qix5VhEdqKR root@servivi" + ]; + } + ]; + }; +} diff --git a/modules/borgbackup/module.nix b/modules/borgbackup/module.nix new file mode 100644 index 0000000..9aa700b --- /dev/null +++ b/modules/borgbackup/module.nix @@ -0,0 +1,268 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib) all any attrValues length mapAttrs mkIf mkOption types; + inherit (builtins) listToAttrs removeAttrs; + + inherit (config.sops) secrets; + inherit (config.vars) hostName; + + cfg = config.services.borgbackup; +in { + options.services.borgbackup = { + configs = mkOption { + # Taken from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/backup/borgbackup.nix + type = types.attrsOf (types.submodule ( + let + globalConfig = config; + in + { + name, + config, + ... + }: { + options = { + paths = mkOption { + type = with types; nullOr (coercedTo str lib.singleton (listOf str)); + default = null; + }; + dumpCommand = mkOption { + type = with types; nullOr path; + default = null; + }; + repo = mkOption { + type = types.str; + default = name; + }; + removableDevice = mkOption { + type = types.bool; + default = false; + }; + archiveBaseName = mkOption { + type = types.nullOr (types.strMatching "[^/{}]+"); + default = "${globalConfig.networking.hostName}-${name}"; + }; + dateFormat = mkOption { + type = types.str; + default = "+%Y-%m-%dT%H:%M:%S"; + }; + startAt = mkOption { + type = with types; either str (listOf str); + # Run every 3 hours + default = "00/3:00"; + }; + persistentTimer = mkOption { + default = false; + type = types.bool; + }; + inhibitsSleep = mkOption { + default = false; + type = types.bool; + }; + user = mkOption { + type = types.str; + default = "root"; + }; + group = mkOption { + type = types.str; + default = "root"; + }; + encryption.mode = mkOption { + type = types.enum [ + "repokey" + "keyfile" + "repokey-blake2" + "keyfile-blake2" + "authenticated" + "authenticated-blake2" + "none" + ]; + default = "none"; + }; + encryption.passCommand = mkOption { + type = with types; nullOr str; + default = null; + }; + encryption.passphrase = mkOption { + type = with types; nullOr str; + default = null; + }; + compression = mkOption { + type = types.strMatching "none|(auto,)?(lz4|zstd|zlib|lzma)(,[[:digit:]]{1,2})?"; + default = "auto,lz4"; + }; + exclude = mkOption { + type = with types; listOf str; + default = []; + }; + patterns = mkOption { + type = with types; listOf str; + default = []; + }; + readWritePaths = mkOption { + type = with types; listOf path; + default = []; + }; + privateTmp = mkOption { + type = types.bool; + default = true; + }; + doInit = mkOption { + type = types.bool; + default = true; + }; + appendFailedSuffix = mkOption { + type = types.bool; + default = true; + }; + prune.keep = mkOption { + type = with types; attrsOf (either int (strMatching "[[:digit:]]+[Hdwmy]")); + default = {}; + }; + prune.prefix = mkOption { + type = types.nullOr (types.str); + default = config.archiveBaseName; + }; + environment = mkOption { + type = with types; attrsOf str; + default = {}; + }; + preHook = mkOption { + type = types.lines; + default = ""; + }; + postInit = mkOption { + type = types.lines; + default = ""; + }; + postCreate = mkOption { + type = types.lines; + default = ""; + }; + postPrune = mkOption { + type = types.lines; + default = ""; + }; + postHook = mkOption { + type = types.lines; + default = ""; + }; + extraArgs = mkOption { + type = types.str; + default = ""; + }; + extraInitArgs = mkOption { + type = types.str; + default = ""; + }; + extraCreateArgs = mkOption { + type = types.str; + default = ""; + }; + extraPruneArgs = mkOption { + type = types.str; + default = ""; + }; + extraCompactArgs = mkOption { + type = types.str; + default = ""; + }; + }; + } + )); + default = {}; + }; + + existingRepos = mkOption { + type = with types; + listOf (submodule { + options = { + name = mkOption { + type = str; + }; + authorizedKeys = mkOption { + type = listOf str; + default = []; + }; + }; + }); + default = []; + }; + }; + + config = mkIf (cfg.configs != {}) { + assertions = [ + { + assertion = all ( + conf: + any (repo: conf.repo == repo.name) cfg.existingRepos + ) (attrValues cfg.configs); + message = '' + The repo you want to backup to needs to exist. + ''; + } + ]; + + programs.ssh.knownHosts = { + nos.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/4mrp8E4Ittwg8feRmPtDHSDR2+Pq4uZHeF5MweVcW"; + }; + + services.borgbackup = let + backupDir = "/data/borgbackups"; + in { + repos = + mkIf (hostName == "nos" && length cfg.existingRepos > 0) + (listToAttrs (map (r: { + inherit (r) name; + value = { + authorizedKeysAppendOnly = r.authorizedKeys; + path = "${backupDir}/${r.name}"; + }; + }) + cfg.existingRepos)); + + jobs = mapAttrs (n: v: let + otherAttrs = removeAttrs v [ + "environment" + "paths" + "preHook" + "postHook" + "repo" + ]; + pathPrefix = "/root/snaps"; + snapPath = "${pathPrefix}/${n}"; + in + { + environment = v.environment // (mkIf (hostName != "nos") {BORG_RSH = "ssh -i ${secrets.borg-ssh.path}";}); + + paths = map (x: snapPath + x) v.paths; + + preHook = + v.preHook + + '' + if [[ ! -d ${pathPrefix} ]]; then + mkdir -p ${pathPrefix} + fi + + ${pkgs.btrfs-progs}/bin/btrfs subvolume snapshot -r / ${snapPath} + ''; + + postHook = + v.postHook + + '' + ${pkgs.btrfs-progs}/bin/btrfs subvolume delete ${snapPath} + ''; + + repo = + if (hostName != "nos") + then "ssh://matt@nos${backupDir}/${v.repo}" + else "${backupDir}/${v.repo}"; + } + // otherAttrs) + cfg.configs; + }; + }; +}