infra/nix/modules/wanderllama/nebula-backup.nix
2026-01-05 23:09:19 +01:00

212 lines
5.7 KiB
Nix

# {config, pkgs, lib, attic, ...}: {
{
config,
lib,
pkgs,
...
}:
let
cfg = config.wanderllama.nebulaBackup;
excludesFile = pkgs.writeText "nebula-backup-exclude" ''
.zfs
/dev/*
/proc/*
/sys/*
/tmp/*
/run/*
/mnt/*
/media/*
/var/cache/*
/var/tmp/*
/var/lib/pacman/*
/var/lib/docker/*
/var/lib/containerd/*
/var/lib/containers/storage/*
/var/lib/docker/containers/*
*/containers/storage/*
/var/lib/nginx/cache/*
/var/lib/nginx/proxy/*
/var/lib/nginx/client-body/*
/var/lib/gitea/*gitea-dump*.zip
/var/lib/gitea/repos/*
/var/cache/*
/lost+found
/root/.cache/*
/root/.cargo/registry/*
/root/git/*
/home/*/.gvfs
/home/*/dotya.ml/static/iso/
/home/*/dotya.ml/static/vm/
/home/*/.vagrant/*
/home/*/.cache
/var/lib/*/.cache
/home/*/Downloads
/home/*/*.iso
/home/*/VirtualBox*
/home/renovate-bot/workdir/cache/*
__pycache__
*.o
*.dwo
*/go/pkg/
*/go/src/
*.npm/_cacache/
*.npm/logs/
'';
nebulaBackupHandler = pkgs.writeShellScript "nebula-backup-handler.sh" ''
#!/bin/bash
if ! ${pkgs.procps}/bin/pidof -x "${nebulaBackup}" >/dev/null; then
if [ $(date +%M) -eq 00 ] || [ $(date +%M) -eq 30 ];then
sleep 60;
fi
${nebulaBackup}
else
echo "[*] nebula backup script already is running, exiting"
fi
'';
nebulaBackup = pkgs.writeShellScript "nebula-backup.sh" ''
#!/bin/bash
set -e -u -x -o pipefail
sshOpts="${pkgs.openssh}/bin/ssh -c chacha20-poly1305@openssh.com -i $PRIVKEY -oServerAliveInterval=30 -oServerAliveCountMax=3 -oExitOnForwardFailure=yes -oStrictHostKeyChecking=accept-new"
# dotya.ml lazy backup script
${pkgs.rsync}/bin/rsync \
-auvEAXogt \
--compress-choice=zstd \
--compress-level=2 \
--inplace \
--delete --bwlimit="$BWLIMIT" \
-e "$sshOpts" \
--exclude-from='${excludesFile}' \
'root@144.91.70.62:/' \
$DSTPATH
${pkgs.rsync}/bin/rsync \
-auvPEAXogt \
--compress-choice=zstd \
--compress-level=2 \
--inplace \
--delete --bwlimit="$BWLIMIT" \
-e "$sshOpts" \
'root@144.91.70.62:/var/lib/gitea/' \
$DSTPATHGITEA
'';
in
{
options.wanderllama.nebulaBackup = {
enable = lib.mkEnableOption "";
bwLimit = lib.mkOption {
type = lib.types.int;
description = "rsync limit in MB";
default = 13000;
};
dstPath = lib.mkOption {
type = lib.types.str;
description = "destination path for generic backups";
};
dstPathGitea = lib.mkOption {
type = lib.types.str;
description = "destination path for Gitea backups";
};
privKey = lib.mkOption {
type = lib.types.path;
description = "path to private key with access to nebula";
};
};
config = lib.mkIf cfg.enable {
systemd = {
slices = {
nebula-backup = {
unitConfig = {
Description = "Slice that limits resources of nebula-backup";
Before = ["slices.target"];
};
sliceConfig = {
MemoryAccounting = "yes";
MemoryMax = "2G";
CPUAccounting = "yes";
CPUQuota = "10%";
};
};
};
timers = {
nebula-backup = {
unitConfig.Description = "nebula-backup timer";
timerConfig = {
# OnCalendar = "*:30/15";
# OnCalendar = "hourly";
OnCalendar = "0/4:00"; # every 4 hours
RandomizedDelaySec = "5s";
Persistent = "true";
};
wantedBy = [ "timers.target" ];
};
};
services = {
nebula-backup = {
environment = {
BWLIMIT = builtins.toString cfg.bwLimit;
DSTPATH = cfg.dstPath;
DSTPATHGITEA = cfg.dstPathGitea;
PRIVKEY = cfg.privKey;
};
# serviceConfig.execStart = "${nebulaBackup}/bin/nebula-backup.sh";
unitConfig = {
RequiresMountsFor = [cfg.dstPath cfg.dstPathGitea];
ConditionPathIsMountPoint = [cfg.dstPath cfg.dstPathGitea];
ConditionPathExists = [cfg.privKey];
};
wantedBy = ["multi-user.target"];
after = ["network-online.target"];
wants = ["network-online.target"];
serviceConfig = {
Type = "oneshot";
RestartSec = 60;
Restart = "on-failure";
ExecStart = "${nebulaBackupHandler}";
Slice = "nebula-backup.slice";
DeviceAllow = [ "/dev/null rw" ];
BindPaths = [
# cfg.dstPath
# cfg.dstPathGitea
"/root/.ssh/known_hosts"
];
BindReadOnlyPaths = [
# excludesFile
cfg.privKey
];
DevicePolicy = "strict";
LockPersonality = true;
MemoryDenyWriteExecute = true;
# NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
# # PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
# # ProtectHome = true;
# # ProtectHome = "read-only";
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
# ProtectProc = "invisible";
# # ProtectSystem = "full";
# # RemoveIPC = true;
# RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
# # RestrictNamespaces = true;
RestrictRealtime = true;
# # RestrictSUIDSGID = true;
# SystemCallArchitectures = "native";
# # SystemCallFilter = [ "@system-service" "~@privileged" ];
# # SystemCallFilter = [ "@system-service"];
};
};
};
};
};
}