Add backup node

This commit is contained in:
asonix 2023-05-06 14:22:29 -05:00
parent fa003c1dc1
commit 88bc60396e
3 changed files with 222 additions and 10 deletions

182
flake.nix
View file

@ -39,7 +39,6 @@
};
makeGenericK3sConfig = { hostname, enableK3s ? true, serverIp ? null, extraModules ? [ ] }:
with sd-images.packages.x86_64-linux.modules;
makeConfig {
inherit hostname;
@ -454,6 +453,98 @@
];
};
makeBackupConfig = system: { hostname, selfIp, unlockMounts ? true, mountVolumes ? true, backupHosts ? [] }:
let
deviceLabel = "RAID";
device = "/dev/disk/by-label/${deviceLabel}";
mountDir = "/btrfs/raid";
in
makeConfig {
inherit hostname;
extraModules = sd-images.packages.${system}.RockPro64v2.modules ++ [
(networkModule { inherit selfIp; })
(btrbkModule {
instances = builtins.map (item: item // { localMountDir = mountDir; }) backupHosts;
})
({ config, lib, pkgs, ... }:
let
keyFile = "backupKeyFile";
keyFilePath = config.sops.secrets."${keyFile}".path;
dryRun = ''
for conf in $(ls /etc/btrbk); do
btrbk -c /etc/btrbk/$conf dryrun
done
'';
prepareDrives = ''
for drive in "$1" "$2" "$3" "$4"; do
if [ "$drive" == "" ]; then
echo "Must provide 4 drives"
exit 1
fi
done
drive_num=1
for drive in "$1" "$2" "$3" "$4"; do
echo "YES" | cryptsetup luksFormat $drive -d ${keyFilePath}
cryptsetup luksOpen $drive "cryptdrive$drive_num" -d ${keyFilePath}
drive_num=$((drive_num+1))
done
mkfs.btrfs -L ${deviceLabel} -d raid10 /dev/mapper/cryptdrive1 /dev/mapper/cryptdrive2 /dev/mapper/cryptdrive3 /dev/mapper/cryptdrive4
mkdir -p ${mountDir}
mount ${device} ${mountDir}
btrfs subvolume create ${mountDir}/@snapshots
umount ${mountDir}
for drive_num in {1..4}; do
cryptsetup luksClose "cryptdrive$drive_num"
done
'';
in
{
sops.secrets."${keyFile}" = {
format = "binary";
sopsFile = ./secrets/${keyFile}.bin;
};
environment.systemPackages = with pkgs; [
(writeShellScriptBin "prepare-drives" prepareDrives)
(writeShellScriptBin "btrbk-dryrun" dryRun)
];
environment.etc.crypttab = {
enable = unlockMounts;
text = ''
cryptdrive1 /dev/sda ${keyFilePath} luks
cryptdrive2 /dev/sdb ${keyFilePath} luks
cryptdrive3 /dev/sdc ${keyFilePath} luks
cryptdrive4 /dev/sdd ${keyFilePath} luks
'';
};
fileSystems =
let
filesystemConfig = {
"${mountDir}" = {
inherit device;
fsType = "btrfs";
options = [ "defaults" "compress=zstd" "rw" "loop" ];
};
};
in
if unlockMounts && mountVolumes then
filesystemConfig
else
{ };
})
];
};
makeGarageConfig = system: { hostname, selfIp, unlockMounts ? true, mountVolumes ? true }:
makeConfig {
inherit hostname;
@ -800,6 +891,80 @@
selfIp = "192.168.20.180";
serverIp = "192.168.20.120";
};
backup-rockpro1 =
let
k3sMain = { ip, name }: {
inherit name;
primaryIp = ip;
mountDir = "/btrfs/nvme";
subvolumes = ["@k3s-config"];
};
k3sOld = { ip, name }: {
inherit name;
primaryIp = ip;
mountDir = "/btrfs/nvme2";
subvolumes = ["@exports" "@garage"];
};
k3s = { ip, name }: {
inherit name;
primaryIp = ip;
mountDir = "/btrfs/nvme";
subvolumes = ["@exports" "@garage"];
};
garage = { ip, name }: [
{
name = "${name}-meta";
primaryIp = ip;
mountDir = "/btrfs/meta";
subvolumes = ["@garage-cluster-config" "@garage-cluster-meta"];
}
{
name = "${name}-data";
primaryIp = ip;
mountDir = "/btrfs/data";
subvolumes = ["@garage-cluster-data"];
}
];
db = { ip, name }: {
inherit name;
primaryIp = ip;
mountDir = "/btrfs/ssd";
subvolumes = ["@postgres" "@postgres-cfg"];
};
in
makeBackupConfig system {
hostname = "backup-rockpro1";
selfIp = "192.168.20.190";
backupHosts =
[(k3sMain { ip = "192.168.20.120"; name = "k3s1-config"; })] ++
(builtins.map k3sOld [
{ ip = "192.168.20.120"; name = "k3s1"; }
{ ip = "192.168.20.121"; name = "k3s2"; }
{ ip = "192.168.20.122"; name = "k3s3"; }
{ ip = "192.168.20.123"; name = "k3s4"; }
{ ip = "192.168.20.124"; name = "k3s5"; }
{ ip = "192.168.20.125"; name = "k3s6"; }
]) ++
(builtins.map k3s [
{ ip = "192.168.20.126"; name = "k3s7"; }
{ ip = "192.168.20.127"; name = "k3s8"; }
{ ip = "192.168.20.128"; name = "k3s9"; }
{ ip = "192.168.20.129"; name = "k3s10"; }
]) ++
(builtins.foldl' (acc: item: (acc ++ (garage item))) [] [
{ ip = "192.168.20.40"; name = "garage1"; }
{ ip = "192.168.20.41"; name = "garage2"; }
]) ++
(builtins.map db [
{ ip = "192.168.20.11"; name = "whitestorm1"; }
{ ip = "192.168.20.24"; name = "redtail2"; }
]);
};
};
deploy.nodes =
@ -810,6 +975,13 @@
{ name = "garage1"; ip = "192.168.20.40"; }
{ name = "garage2"; ip = "192.168.20.41"; }
{ name = "build2"; ip = "192.168.20.101"; }
{ name = "k3s-rock1"; ip = "192.168.20.110"; }
{ name = "k3s-rock2"; ip = "192.168.20.111"; }
{ name = "k3s-rock3"; ip = "192.168.20.112"; }
{ name = "k3s-rock4"; ip = "192.168.20.113"; }
{ name = "k3s-rock5"; ip = "192.168.20.114"; }
{ name = "k3s-rock6"; ip = "192.168.20.115"; }
{ name = "k3s-rock7"; ip = "192.168.20.116"; }
{ name = "k3s1"; ip = "192.168.20.120"; }
{ name = "k3s2"; ip = "192.168.20.121"; }
{ name = "k3s3"; ip = "192.168.20.122"; }
@ -820,15 +992,9 @@
{ name = "k3s8"; ip = "192.168.20.127"; }
{ name = "k3s9"; ip = "192.168.20.128"; }
{ name = "k3s10"; ip = "192.168.20.129"; }
{ name = "k3s-rock1"; ip = "192.168.20.110"; }
{ name = "k3s-rock2"; ip = "192.168.20.111"; }
{ name = "k3s-rock3"; ip = "192.168.20.112"; }
{ name = "k3s-rock4"; ip = "192.168.20.113"; }
{ name = "k3s-rock5"; ip = "192.168.20.114"; }
{ name = "k3s-rock6"; ip = "192.168.20.115"; }
{ name = "k3s-rock7"; ip = "192.168.20.116"; }
{ name = "k3s-quartza1"; ip = "192.168.20.160"; }
{ name = "k3s-rockpro1"; ip = "192.168.20.180"; }
{ name = "backup-rockpro1"; ip = "192.168.20.190"; }
];
in
builtins.foldl'

View file

@ -54,6 +54,22 @@ let
};
};
};
backup = primaryIp: remoteMountDir: localMountDir: subvolumes: {
backend_remote = "btrfs-progs-sudo";
ssh_identity = config.sops.secrets.private_key.path;
ssh_user = "btrbk";
stream_buffer = "512m";
target_preserve = "2h 2d 10w *m";
target_preserve_min = "24h";
transaction_log = "/var/log/btrbk.log";
volume = {
"ssh://${primaryIp}${remoteMountDir}" = btrbkSecondary {
targetDir = "${localMountDir}/@snapshots";
inherit subvolumes;
};
};
};
in
{
sops.secrets.private_key = {
@ -79,9 +95,15 @@ in
];
extraPackages = with pkgs; [ gzip ];
instances = (builtins.foldl'
(acc: { primaryIp ? null, mountDir, subvolumes, name ? "btrbk" }:
(acc: { primaryIp ? null, mountDir, localMountDir ? null, subvolumes, name ? "btrbk" }:
let
selected = if primaryIp == null then (primary mountDir subvolumes) else (secondary primaryIp mountDir subvolumes);
selected =
if primaryIp == null && localMountDir == null then
(primary mountDir subvolumes)
else if localMountDir == null then
(secondary primaryIp mountDir subvolumes)
else
(backup primaryIp mountDir localMountDir subvolumes);
in
acc //
{

24
secrets/backupKeyFile.bin Normal file
View file

@ -0,0 +1,24 @@
{
"data": "ENC[AES256_GCM,data:1z/pASQGRm9I3cSlxlqtM+I8bcKQNWLxbNwKlRMTDctBTpHm/ayVwsVeNImRqMeu9zzaA/EJV1uP9s+Zxc5S5hk0hpKuNnPUwrxWMm+RvCbMpYcjE7kYorYwfZEsqniALlzA7uVmK0CcTMSy3dKSKCaEWHXgZyk9dK8wPCmu5KPlmIpZhtv6l761X6uWbsHhhW8AgRG9qPzJkdgdtuqFc0bzQeuQG7zJjeP2HXO4VgYkFJ5HtCrTxDJuYLEL5JpEl+CMuFk7I8RIw7wAr4h7DSCEQj28rw+lyiGgqMa+ZIOBfvo1Oq/el6Ut1jrcpdIZGYv8HpVduypEpfANsDjFl4yvauos8mH402Y6ag5MOLvUEKlQvvNz3xttd/mOnfSFhqcqihcD6KHqkUfhNkucGWmphne17zRYIlelzz0Ic+GuboaKaPuHx7Xlt36K/hNAUwcV2IcfdZfTMmgQY0tQwjtvQoX0PvJnBCUe6NgXO4kmq33Mgi6G6WGgNuHawyX9qfBJj5eXtbd2SdQmf56A2NdQiT8GeasEfLOYYxEmMX1kRp5qfNPTUVKI/Qfhktew20ZPBqAEo3LnyiVr/x8AFunqaJHJE0k7bBHfUnhy6ZOm2/NhL6SntP7h9QmzLhjlrRGufuWfxEPvnIk7pn/Fuos4bmxYxZ/qU7q2jMcPDFM=,iv:uohMMzhcL4krkBUDdiPMljBHF221ssNJYg1VerGwSIU=,tag:7Xg2qI4KGaU24cIZuobDIA==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"hc_vault": null,
"age": [
{
"recipient": "age17yhtwnhqjssghc5qqamt0fqdu27zpqms8d8ghrc0txeevywfp3ssklfy57",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4Tzd0RDUvZEYrc0pLbVFy\nUnV2SXFZb3kwL2laTTVYT0VJMEE3TGVGZXc4CjluVHpJRWZEM01Ta0NrWmJuaTc4\nRnpteXRJOEZuemlqcm45RFdjSSsrQVkKLS0tIGZpNnVHbkZxT0RQUlJ4Z1F1dXk1\nN1k1c1crRmNyc0hRSUYvclpoK3E2RHMKkRZM2aav3IJXC/t8b3uizgiaNA4JX61P\nc4dqbZPAP/3s9Go3/853/trFNXbys498VEUI4BYl7kjEPG1PUplpcg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age145uwrexj6ffaaxy7jg3j29gtchhwy0y0nttw06zeuxkqsy8rnpds7fh7xq",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvUGxjdGZKdTIvREtyY3lB\nZWdHcmNJQ2p1V0YyVkNZR0QrbG5BTWFBQ3dnCmI0WktWeHZsNTBwY0FNOUd1Qllk\nVXlkTTJFWHMzR0FJMVNDd3lib2FtVzQKLS0tIGMzWURjTFk2aFVObWwrSDB3N0hh\nWU5MVjEvVCtSeXJQS0w5VnFzRzFlWDgKokE04o3VtUo5l6VFDqMqj3ZfhNaNXwq3\n2Ggq0LMrow8hag/ssOcbylKOnUEb559tz6pIQCamRXcaT/epAh7iiA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2023-05-06T17:49:04Z",
"mac": "ENC[AES256_GCM,data:M0okeypT1Ot5SR4DsoOoht1kXchalmCyA62j3RxVJU9uO1rQ6kxL4LnGtVjrhgR0mk6tenZe5Ym745gq3IsMgtBmExLfC7zbY8G/zf2KUxeRPYwEbOnjxVe4IoJutsq2/fC5G1Fviqp1ih1EJTs9VHNUn5e7A6ZXoHt9cdB/J6A=,iv:R+oFRuwR2afidCn/+/whjkWd9Q+KfW0Jz7L8Hy7ZIio=,tag:M9ODjko2iSBbB60qndBQRg==,type:str]",
"pgp": null,
"unencrypted_suffix": "_unencrypted",
"version": "3.7.3"
}
}