parent
8b902e9c00
commit
21c94df25a
8 changed files with 294 additions and 102 deletions
modules/borgbackup
20
modules/borgbackup/default.nix
Normal file
20
modules/borgbackup/default.nix
Normal file
|
@ -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"
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
268
modules/borgbackup/module.nix
Normal file
268
modules/borgbackup/module.nix
Normal file
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue