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
|
home-manager.nixosModules.home-manager
|
||||||
|
|
||||||
../modules/arion
|
../modules/arion
|
||||||
../modules/borgbackup.nix
|
../modules/borgbackup
|
||||||
];
|
];
|
||||||
|
|
||||||
nixpkgs = {
|
nixpkgs = {
|
||||||
|
|
|
@ -18,7 +18,6 @@ in {
|
||||||
|
|
||||||
# Custom DNS
|
# Custom DNS
|
||||||
local-zone = [
|
local-zone = [
|
||||||
"pve.nelim.org redirect"
|
|
||||||
"headscale.nelim.org redirect"
|
"headscale.nelim.org redirect"
|
||||||
"git.nelim.org redirect"
|
"git.nelim.org redirect"
|
||||||
"mc.nelim.org transparent"
|
"mc.nelim.org transparent"
|
||||||
|
@ -34,8 +33,6 @@ in {
|
||||||
then "100.64.0.8"
|
then "100.64.0.8"
|
||||||
else "100.64.0.9";
|
else "100.64.0.9";
|
||||||
in [
|
in [
|
||||||
"\"pve.nelim.org IN A 100.64.0.4\""
|
|
||||||
|
|
||||||
"\"headscale.nelim.org. IN A ${wanIP}\""
|
"\"headscale.nelim.org. IN A ${wanIP}\""
|
||||||
|
|
||||||
"\"git.nelim.org. IN A ${wanIP}\""
|
"\"git.nelim.org. IN A ${wanIP}\""
|
||||||
|
|
|
@ -26,6 +26,7 @@ in {
|
||||||
extraGroups = [
|
extraGroups = [
|
||||||
"wheel"
|
"wheel"
|
||||||
"adm"
|
"adm"
|
||||||
|
"borg"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,13 +42,6 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
swapDevices = [
|
|
||||||
{
|
|
||||||
device = "/var/lib/swapfile";
|
|
||||||
size = 16 * 1024;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
zramSwap.enable = true;
|
zramSwap.enable = true;
|
||||||
|
|
||||||
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware;
|
||||||
|
|
|
@ -29,8 +29,8 @@ in {
|
||||||
rwDataDir = configPath;
|
rwDataDir = configPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
#services.borgbackup.configs.arion = {
|
services.borgbackup.configs.arion = {
|
||||||
# paths = [configPath];
|
paths = [configPath];
|
||||||
# exclude = ["**/lineageos*"];
|
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