feat(servers): setup borgbackup repos
All checks were successful
Discord / discord commits (push) Successful in 34s
All checks were successful
Discord / discord commits (push) Successful in 34s
This commit is contained in:
parent
8b902e9c00
commit
21c94df25a
8 changed files with 294 additions and 102 deletions
|
@ -19,7 +19,7 @@
|
|||
home-manager.nixosModules.home-manager
|
||||
|
||||
../modules/arion
|
||||
../modules/borgbackup.nix
|
||||
../modules/borgbackup
|
||||
];
|
||||
|
||||
nixpkgs = {
|
||||
|
|
|
@ -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}\""
|
||||
|
|
|
@ -26,6 +26,7 @@ in {
|
|||
extraGroups = [
|
||||
"wheel"
|
||||
"adm"
|
||||
"borg"
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -42,13 +42,6 @@
|
|||
};
|
||||
};
|
||||
|
||||
swapDevices = [
|
||||
{
|
||||
device = "/var/lib/swapfile";
|
||||
size = 16 * 1024;
|
||||
}
|
||||
];
|
||||
|
||||
zramSwap.enable = true;
|
||||
|
||||
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||
|
|
|
@ -29,8 +29,8 @@ in {
|
|||
rwDataDir = configPath;
|
||||
};
|
||||
|
||||
#services.borgbackup.configs.arion = {
|
||||
# paths = [configPath];
|
||||
# exclude = ["**/lineageos*"];
|
||||
#};
|
||||
services.borgbackup.configs.arion = {
|
||||
paths = [configPath];
|
||||
exclude = ["**/lineageos*"];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
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…
Reference in a new issue