Replace backup module with built-in solutions
May not be as universal, but is more certain to work correctly than my hacked up version that I'm not so invested in.
This commit is contained in:
parent
c788855196
commit
c883524efc
|
@ -1,210 +0,0 @@
|
||||||
# modules/backup.nix --- in-house backup/restoration procedures
|
|
||||||
#
|
|
||||||
# A simple way to register certain file paths as backup targets. They will be
|
|
||||||
# incrementally backed up to /run/backups/$NAME/$DATE/.
|
|
||||||
# /run/backups/{backup,restore}.sh scripts will be available for easy backing up
|
|
||||||
# or restoration of said data.
|
|
||||||
#
|
|
||||||
# Warning: don't use me yet, it's a WIP.
|
|
||||||
|
|
||||||
{ options, config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
with lib;
|
|
||||||
with lib.my;
|
|
||||||
let cfg = config.modules.backup;
|
|
||||||
|
|
||||||
backupType = with types; submodule ({ config, ... }: {
|
|
||||||
options = {
|
|
||||||
enable = mkBoolOpt false;
|
|
||||||
|
|
||||||
name = mkOpt' str config._module.args.name "Name of this backup";
|
|
||||||
owner = mkOpt str "backup";
|
|
||||||
group = mkOpt str users.${config.owner}.group;
|
|
||||||
mode = mkOpt str "0400";
|
|
||||||
|
|
||||||
baseDir = mkOpt' (either str path) "" "Base directory for backup targets";
|
|
||||||
targets = mkOpt' (listOf (either str path)) [] ''
|
|
||||||
List of file globs or paths to files/directories that will be backed
|
|
||||||
up (or restored) for this target. Paths are relative to this target's
|
|
||||||
baseDir to modules.backup.path.
|
|
||||||
'';
|
|
||||||
|
|
||||||
backupScript = mkOpt' lines "" ''
|
|
||||||
Script to run when backing up this target. The current directory is
|
|
||||||
this target's baseDir and $outdir contains the backup destination.
|
|
||||||
'';
|
|
||||||
restoreScript = mkOpt' lines "" ''
|
|
||||||
Script to run when restoring this target's backed up files. The
|
|
||||||
current directory is this target's latest backup directory and $outdir
|
|
||||||
contains the restoration destination.
|
|
||||||
'';
|
|
||||||
|
|
||||||
suspendServices = mkOpt (listOf str) [] ''
|
|
||||||
Systemd services to suspend while backing up or restoring this target.
|
|
||||||
'';
|
|
||||||
maxGenerations = mkOpt' int cfg.maxGenerations ''
|
|
||||||
How many generations of backups to keep in store
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
targetBackupScript = target: writeShellScriptBin "backup.sh" ''
|
|
||||||
${optionalString (target.enable != true) ''
|
|
||||||
echo "Skipping ${target.name} (reason: disabled)"
|
|
||||||
exit 0
|
|
||||||
''}
|
|
||||||
set -e
|
|
||||||
basedir="${cfg.path}/${target.name}"
|
|
||||||
indir="${target.baseDir}"
|
|
||||||
outdir="$basedir/$(date +"%Y%m%d-%H%M%S)"
|
|
||||||
|
|
||||||
# Ensure current generation's backup folder exists, and we're in it
|
|
||||||
rm -rf "$outdir"
|
|
||||||
mkdir -p "$outdir" && cd "$outdir"
|
|
||||||
|
|
||||||
# Suspend dependent services
|
|
||||||
${concatStringSep "\n" (map (s: "systemctl stop ${s}") target.suspendServices)}
|
|
||||||
|
|
||||||
# Backup individual targets
|
|
||||||
${concatStringSep "\n"
|
|
||||||
(mapAttrsToList (n: v: ''cp -RTfpv "$indir/"${v} "$outdir/"'') target.targets)}
|
|
||||||
|
|
||||||
# Launch custom script
|
|
||||||
${optionalString (target.backupScript != "") target.backupScript}
|
|
||||||
|
|
||||||
# Clean up excess generations
|
|
||||||
gens="$(ls -1dtr "$basedir" | head -n -${target.maxGenerations})"
|
|
||||||
for gen in $gens; do rm -rf $gen; done
|
|
||||||
ln -sf "$basedir/latest" "$outdir"
|
|
||||||
|
|
||||||
# Resume services
|
|
||||||
${concatStringSep "\n" (map (s: "systemctl start ${s}") target.suspendServices)}
|
|
||||||
'';
|
|
||||||
|
|
||||||
targetRestoreScript = target: writeShellScriptBin "restore.sh" ''
|
|
||||||
set -e
|
|
||||||
indir="${cfg.path}/${target.name}/latest"
|
|
||||||
outdir="${target.baseDir}"
|
|
||||||
cd "$indir"
|
|
||||||
|
|
||||||
# Suspend services
|
|
||||||
${concatStringSep "\n"
|
|
||||||
(map (s: "systemctl stop #{s}") target.suspendServices)}
|
|
||||||
|
|
||||||
# Restore individual targets
|
|
||||||
${concatStringSep "\n"
|
|
||||||
(mapAttrsToList (n: v: ''cp -RTfpv "$indir/"${v} "$outdir/"'') target.targets)}
|
|
||||||
|
|
||||||
# Launch custom script
|
|
||||||
${optionalString (target.restoreScript != "") target.restoreScript}
|
|
||||||
|
|
||||||
# Resume services
|
|
||||||
${concatStringSep "\n" (map (s: "systemctl start ${s}") target.suspendServices)}
|
|
||||||
'';
|
|
||||||
|
|
||||||
backupBin = writeShellScriptBin "hey-backup" ''
|
|
||||||
if [[ $1 ]]; then
|
|
||||||
if [[ ! -x ${cfg.path}/$1/backup.sh ]]; then
|
|
||||||
>&2 echo "Could not find backup for #{name}. Aborting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Backing up $1"
|
|
||||||
$i/backup.sh
|
|
||||||
else
|
|
||||||
echo "Backing up all targets"
|
|
||||||
for i in ${cfg.path}/*; do
|
|
||||||
echo "Backing up: $i"
|
|
||||||
$i/backup.sh
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
restoreBin = writeShellScriptBin "hey-restore" ''
|
|
||||||
if [[ ! -d ${cfg.path} || -z "$(ls -A ${cfg.path})" ]]; then
|
|
||||||
>&2 echo "No backups on this system. Aborting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ $1 ]]; then
|
|
||||||
if [[ ! -x ${cfg.path}/$1/restore.sh ]]; then
|
|
||||||
>&2 echo "Could not find backup for #{name}. Aborting..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Restoring $1"
|
|
||||||
${cfg.path}/$1/restore.sh
|
|
||||||
else
|
|
||||||
echo "Restoring all targets from latest backup"
|
|
||||||
for i in ${cfg.path}/*; do
|
|
||||||
echo "Restoring backup: $i"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
in {
|
|
||||||
options.modules.backup = with types; {
|
|
||||||
enable = mkBoolOpt false;
|
|
||||||
path = mkOpt (either path str) "/run/backups";
|
|
||||||
targets = mkOpt (attrsOf backupType) {};
|
|
||||||
schedule = mkOpt str ""; # e.g. "02:00"
|
|
||||||
maxGenerations = mkOpt int 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkIf (cfg.enable && cfg.targets != {}) {
|
|
||||||
users.users.backup = {
|
|
||||||
description = "The nixos backup service";
|
|
||||||
group = "backup";
|
|
||||||
home = cfg.path;
|
|
||||||
isSystemUser = true;
|
|
||||||
useDefaultShell = true;
|
|
||||||
};
|
|
||||||
users.groups.backup = {};
|
|
||||||
|
|
||||||
|
|
||||||
system.activationScripts.backupInit = ''
|
|
||||||
echo "Generating backup files"
|
|
||||||
BASEDIR="${cfg.path}"
|
|
||||||
mkdir -p "$BASEDIR"
|
|
||||||
chown backup:backup "$BASEDIR"
|
|
||||||
chmod 700 "$BASEDIR"
|
|
||||||
${concatStringSep "\n" (mapAttrsToList (n: v: ''
|
|
||||||
echo "Generating backup dir: $BASEDIR"
|
|
||||||
TARGETDIR="$BASEDIR/${v.name}"
|
|
||||||
mkdir -p "$TARGETDIR"
|
|
||||||
chown ${v.owner}:${v.group} "$TARGETDIR"
|
|
||||||
chmod ${v.mode} "$TARGETDIR"
|
|
||||||
cp -f ${targetBackupScript v}/bin/backup.sh "$TARGETDIR/backup.sh"
|
|
||||||
cp -f ${targetRestoreScript v}/bin/restore.sh "$TARGETDIR/restore.sh"
|
|
||||||
chmod 700 {backup,restore}.sh
|
|
||||||
'') cfg.targets)}
|
|
||||||
echo "Generated backup files!"
|
|
||||||
'';
|
|
||||||
|
|
||||||
|
|
||||||
systemd.services.hey-backups = {
|
|
||||||
description = "Backup system";
|
|
||||||
environment.TARGET_DIR = cfg.path;
|
|
||||||
serviceConfig = {
|
|
||||||
SyslogIdentifier = "hey-backups";
|
|
||||||
Type = "oneshot";
|
|
||||||
User = mkDefault user;
|
|
||||||
Group = mkDefault group;
|
|
||||||
ExecStart = "${pkgs.bash}/bin/bash ${backupBin}/bin/hey-backup";
|
|
||||||
};
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.timers.hey-backups = mkIf (cfg.schedule != "") {
|
|
||||||
description = "Backup dotfile targets on time";
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = cfg.schedule;
|
|
||||||
Persistent = "true";
|
|
||||||
Unit = "hey-backups.service";
|
|
||||||
};
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Add 'bin/hey backup' extensions to PATH
|
|
||||||
environment.systemPackages = [
|
|
||||||
backupBin
|
|
||||||
restoreBin
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -9,7 +9,10 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
services.bitwarden_rs.enable = true;
|
services.bitwarden_rs = {
|
||||||
|
enable = true;
|
||||||
|
backupDir = "/run/backups/bitwarden_rs";
|
||||||
|
};
|
||||||
|
|
||||||
user.extraGroups = [ "bitwarden_rs" ];
|
user.extraGroups = [ "bitwarden_rs" ];
|
||||||
|
|
||||||
|
@ -19,12 +22,5 @@ in {
|
||||||
port = 80,443,8002
|
port = 80,443,8002
|
||||||
maxretry = 5
|
maxretry = 5
|
||||||
'';
|
'';
|
||||||
|
|
||||||
modules.backup.targets.bitwarden = {
|
|
||||||
baseDir = "/var/lib/bitwarden_rs";
|
|
||||||
owner = "bitwarden_rs";
|
|
||||||
targets = [ "*" ];
|
|
||||||
suspendServices = [ "bitwarden_rs" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ in {
|
||||||
database.LOG_SQL = false;
|
database.LOG_SQL = false;
|
||||||
service.ENABLE_BASIC_AUTHENTICATION = false;
|
service.ENABLE_BASIC_AUTHENTICATION = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dump = {
|
||||||
|
enable = true;
|
||||||
|
interval = "daily";
|
||||||
|
backupDir = "/run/backups/gitea";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.fail2ban.jails.gitea = ''
|
services.fail2ban.jails.gitea = ''
|
||||||
|
@ -52,12 +58,5 @@ in {
|
||||||
banaction = %(banaction_allports)s
|
banaction = %(banaction_allports)s
|
||||||
maxretry = 5
|
maxretry = 5
|
||||||
'';
|
'';
|
||||||
|
|
||||||
modules.backup.targets.gitea = {
|
|
||||||
baseDir = config.users.users.git.home;
|
|
||||||
owner = "git";
|
|
||||||
targets = [ "repositories" "data" ];
|
|
||||||
suspendServices = [ "gitea" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue