diff --git a/hosts/nixos/thinkpad/system.nix b/hosts/nixos/thinkpad/system.nix index bda991d..49a94d8 100644 --- a/hosts/nixos/thinkpad/system.nix +++ b/hosts/nixos/thinkpad/system.nix @@ -2,6 +2,7 @@ imports = [ ./hardware-configuration.nix ../../../modules/wireguard.nix + ../../../modules/borg-server.nix ]; # Bootloader - standard UEFI setup @@ -353,6 +354,19 @@ }; }; + # Borg backup server configuration + services.borgbackup-server = { + enable = true; + dataDir = "/srv/borg"; + users = { + borg = { + publicKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG26nw5vhvMl0FoFJbsLBb0mqziiSKS7tuK4IVcvWhKk yanlin@mba" + ]; + }; + }; + }; + # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions # on your system were taken. diff --git a/hosts/nixos/vps/system.nix b/hosts/nixos/vps/system.nix index 863d90a..f19e975 100644 --- a/hosts/nixos/vps/system.nix +++ b/hosts/nixos/vps/system.nix @@ -96,8 +96,8 @@ # Borg backup configuration services.borgbackup-custom = { enable = true; - # Use SSH alias from SSH config for remote backup - repositoryUrl = "ssh://storage-box/./vps"; + # Use SSH alias from SSH config for remote backup to thinkpad borg server + repositoryUrl = "ssh://borg-backup/./vps"; backupPaths = [ "/home" "/var/lib/containers" diff --git a/modules/borg-server.nix b/modules/borg-server.nix new file mode 100644 index 0000000..0c2b670 --- /dev/null +++ b/modules/borg-server.nix @@ -0,0 +1,172 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.borgbackup-server; +in + +{ + options.services.borgbackup-server = { + enable = mkEnableOption "Borg backup server"; + + dataDir = mkOption { + type = types.str; + default = "/srv/borg"; + example = "/mnt/backup/borg"; + description = "Base directory for all borg repositories"; + }; + + users = mkOption { + type = types.attrsOf (types.submodule { + options = { + publicKeys = mkOption { + type = types.listOf types.str; + example = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxyz..." ]; + description = "List of SSH public keys for this user"; + }; + }; + }); + default = {}; + example = { + alice = { + publicKeys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxyz..." ]; + }; + }; + description = "Borg backup users configuration"; + }; + + sshPort = mkOption { + type = types.port; + default = 22; + example = 2222; + description = "SSH port for borg connections"; + }; + }; + + config = mkIf cfg.enable { + # Install required packages + environment.systemPackages = [ pkgs.borgbackup pkgs.openssh ]; + + # Create borg-server group + users.groups.borg-server = {}; + + # Create a system user for each borg user + users.users = mapAttrs (username: userCfg: { + isSystemUser = true; + group = "borg-server"; + home = "${cfg.dataDir}/${username}"; + createHome = true; + shell = pkgs.bash; + description = "Borg backup user ${username}"; + openssh.authorizedKeys.keys = map (key: + "command=\"borg serve --restrict-to-path ${cfg.dataDir}/${username}\",restrict ${key}" + ) userCfg.publicKeys; + }) cfg.users; + + # Ensure proper permissions on data directory + systemd.tmpfiles.rules = [ + "d ${cfg.dataDir} 0755 root borg-server -" + ] ++ (mapAttrsToList (username: _: + "d ${cfg.dataDir}/${username} 0700 ${username} borg-server -" + ) cfg.users); + + # Configure SSH for borg access + services.openssh = { + enable = true; + ports = [ cfg.sshPort ]; + settings = { + # SSH hardening for borg users + Match = "Group borg-server"; + PasswordAuthentication = false; + PubkeyAuthentication = true; + PermitRootLogin = "no"; + X11Forwarding = false; + AllowAgentForwarding = false; + AllowTcpForwarding = false; + PermitTunnel = "no"; + + # Keep connection alive settings + ClientAliveInterval = 10; + ClientAliveCountMax = 30; + }; + }; + + # Open firewall port + networking.firewall.allowedTCPPorts = mkIf (cfg.sshPort != 22) [ cfg.sshPort ]; + + # Create convenience scripts and aliases + environment.shellAliases = { + borg-server-status = "systemctl status sshd"; + borg-server-users = "ls -la ${cfg.dataDir}"; + borg-server-logs = "journalctl -u sshd -f"; + }; + + # Create a helper script for adding new users + environment.etc."borg-server/add-user.sh" = { + text = '' + set -e + + if [ $# -lt 2 ]; then + echo "Usage: $0 " + echo "Example: $0 alice 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxyz...'" + exit 1 + fi + + USERNAME="$1" + SSH_KEY="$2" + USER_DIR="${cfg.dataDir}/$USERNAME" + + echo "To add user '$USERNAME', add the following to your NixOS configuration:" + echo "" + echo "services.borgbackup-server.users.$USERNAME = {" + echo " publicKeys = [ \"$SSH_KEY\" ];" + echo "};" + echo "" + echo "Then rebuild your system with: nixos-rebuild switch" + ''; + mode = "0755"; + }; + + # Create a helper script for checking repository info + environment.etc."borg-server/check-repo.sh" = { + text = '' + set -e + + if [ $# -lt 1 ]; then + echo "Usage: $0 [repository]" + echo "Example: $0 alice" + echo "Example: $0 alice main-repo" + exit 1 + fi + + USERNAME="$1" + REPO_NAME="''${2:-}" + USER_DIR="${cfg.dataDir}/$USERNAME" + + if [ ! -d "$USER_DIR" ]; then + echo "User directory $USER_DIR does not exist" + exit 1 + fi + + if [ -n "$REPO_NAME" ]; then + REPO_PATH="$USER_DIR/$REPO_NAME" + else + echo "Repositories for user $USERNAME:" + ls -la "$USER_DIR" + exit 0 + fi + + if [ -d "$REPO_PATH" ]; then + echo "Repository info for $USERNAME/$REPO_NAME:" + sudo -u "$USERNAME" borg info "$REPO_PATH" + else + echo "Repository $REPO_PATH does not exist" + exit 1 + fi + ''; + mode = "0755"; + }; + + }; +} \ No newline at end of file diff --git a/modules/ssh.nix b/modules/ssh.nix index 1b5650a..3828126 100644 --- a/modules/ssh.nix +++ b/modules/ssh.nix @@ -57,6 +57,16 @@ port = 23; }; + "borg-backup" = { + hostname = "10.2.2.30"; + user = "borg"; + identityFile = "~/.ssh/keys/borg-backup"; + proxyJump = "vps"; + setEnv = { + TERM = "xterm-256color"; + }; + }; + }; }; }