diff --git a/src/system/machines/server/system.nix b/src/system/machines/server/system.nix index 7a465a8..b0e8be6 100644 --- a/src/system/machines/server/system.nix +++ b/src/system/machines/server/system.nix @@ -8,6 +8,18 @@ nginx.enable = true; forgejo.enable = true; frigate.enable = false; + + backup = { + enable = true; + recipients = [ + # TODO: Add your age recipients + # "${config.user.keys.age.yubikey}" + # "${config.user.keys.ssh.desktop}" + ]; + destination = "gdrive:backups/server"; # TODO: configure rclone remote + schedule = "daily"; + keepLast = 7; + }; }; users.users = { diff --git a/src/system/modules/backup/default.nix b/src/system/modules/backup/default.nix new file mode 100644 index 0000000..834e024 --- /dev/null +++ b/src/system/modules/backup/default.nix @@ -0,0 +1,95 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + cfg = config.modules.system.backup; + + recipientArgs = concatMapStrings (r: "-r '${r}' ") cfg.recipients; + + # Convert absolute paths to relative for tar, preserving structure + # e.g., /var/lib/forgejo -> var/lib/forgejo + tarPaths = map (p: removePrefix "/" p) cfg.paths; + + backupScript = pkgs.writeShellScript "backup" '' + set -euo pipefail + + TIMESTAMP=$(date +%Y%m%d-%H%M%S) + BACKUP_NAME="backup-$TIMESTAMP.tar.age" + TEMP_DIR=$(mktemp -d) + trap "rm -rf $TEMP_DIR" EXIT + + echo "Starting backup: $BACKUP_NAME" + echo "Paths: ${concatStringsSep " " cfg.paths}" + + ${pkgs.gnutar}/bin/tar -C / -cf - ${concatStringsSep " " tarPaths} | \ + ${pkgs.age}/bin/age ${recipientArgs} -o "$TEMP_DIR/$BACKUP_NAME" + + ${pkgs.rclone}/bin/rclone copy "$TEMP_DIR/$BACKUP_NAME" "${cfg.destination}" + + # Prune old backups + ${pkgs.rclone}/bin/rclone lsf "${cfg.destination}" | \ + sort -r | \ + tail -n +$((${toString cfg.keepLast} + 1)) | \ + while read -r old; do + ${pkgs.rclone}/bin/rclone delete "${cfg.destination}/$old" + done + + echo "Backup complete" + ''; + +in +{ + options.modules.system.backup = { + enable = mkEnableOption "Encrypted backups"; + + paths = mkOption { + type = types.listOf types.str; + default = []; + description = "Absolute paths to include in backup (structure preserved)"; + }; + + recipients = mkOption { + type = types.listOf types.str; + default = []; + description = "Age public keys for encryption"; + }; + + destination = mkOption { + type = types.str; + default = ""; + description = "Rclone destination"; + }; + + schedule = mkOption { + type = types.str; + default = "daily"; + description = "Systemd calendar expression"; + }; + + keepLast = mkOption { + type = types.int; + default = 3; + description = "Number of backups to keep"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.rclone ]; + + systemd.services.backup = { + description = "Encrypted backup"; + serviceConfig = { + Type = "oneshot"; + ExecStart = backupScript; + }; + }; + + systemd.timers.backup = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.schedule; + Persistent = true; + }; + }; + }; +} diff --git a/src/system/modules/forgejo/default.nix b/src/system/modules/forgejo/default.nix index 9c70bd0..ed6b461 100644 --- a/src/system/modules/forgejo/default.nix +++ b/src/system/modules/forgejo/default.nix @@ -73,6 +73,10 @@ in }; }; + modules.system.backup.paths = [ + "/var/lib/forgejo" + ]; + services.nginx.virtualHosts."git.${domain}" = mkIf nginx.enable { useACMEHost = domain; forceSSL = true; diff --git a/src/user/config/keys/pgp/windows.pub.key b/src/user/config/keys/pgp/windows.pub.key deleted file mode 100644 index cf9f326..0000000 --- a/src/user/config/keys/pgp/windows.pub.key +++ /dev/null @@ -1,109 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -HRncE+dM4PA+Ge8YbBCL6pU= -=X5C7 ------END PGP PUBLIC KEY BLOCK----- diff --git a/src/user/config/keys/ssh/windows.pub.key b/src/user/config/keys/ssh/windows.pub.key deleted file mode 100644 index c44f5ba..0000000 --- a/src/user/config/keys/ssh/windows.pub.key +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCo2BuGY1zz2UjY7/4Rq6rse3dDbxkSA+GyqI2xJi86dqzpmnsQPDYCM61xBNGIJWCQRSRPvNMhQjL8zFo2+u3EyFZXP7twO9mtZCyo5guBRUYuzoCNMtxHt7ZERVei2NI+T7ZmU34L1cuo+fSluhC62hGGD8mMO/wQURO9CwXN+JGAgP5jxLnHKfS6xP7FHDvH7XbbgiC1M7B1B7mCyjzLadQqQebkmJYDnKcdz424VBljxVHlrSjDU0wsnBNpj/nP4eTsnzL+dGTWxkLwUzCDeSIvNoFjSe617iGfYK7y9imZsIb6zAUY7zwFqCN9co9PRJzBDKKhygFNkoZ9/OjIRm0MXWEn7eFsiaO7mB+tSoGCgi+/R34tRaaelmDS6D09HdWBgnOSsqW8VIa1YhAuDzSk3p0UQrSNSTuwMLh5fwDF8fpZ5c7M4U6oJzzfRp/ssWuq6XUnrVt43OqlacgCctFzRcdyZjQedF//ucUoG5dudmvYOGX9NnYEuIYdGtMVneMzsoGTycSi7fAtLC7ORyj6Q3LiOR+rEE+t+Wvw7pmnzmc2PH/C3yVBZhLGPgR+rH0NIjPQa6TnX8Y2NOCn4T4vL7LGxVfVOYoMzRcgYdB1JmrUxz6TweBX3IIpkbV+EmGpq3f0vhJAF1kE6fsrcrw/jWzIIVbymV0fim+ZGQ== bryan@ramos.codes diff --git a/src/user/modules/security/modules/gpg/default.nix b/src/user/modules/security/modules/gpg/default.nix index 170b570..79b5ec5 100644 --- a/src/user/modules/security/modules/gpg/default.nix +++ b/src/user/modules/security/modules/gpg/default.nix @@ -21,11 +21,6 @@ in text = "${config.user.keys.pgp.primary}"; trust = 5; } - ] ++ optionals (osConfig.networking.hostName == "desktop") [ - { - text = "${config.user.keys.pgp.windows}"; - trust = 5; - } ] ++ optionals (osConfig.networking.hostName == "workstation") [ { text = "${config.user.keys.pgp.work}";