From 611bde18b25f1a045e70540e9207566db7fb19cf Mon Sep 17 00:00:00 2001 From: Joseph Hanson Date: Thu, 23 Jan 2025 19:26:48 -0600 Subject: [PATCH 1/4] add qbittorrent and service --- .vscode/settings.json | 11 +- nixos/hosts/shadowfax/default.nix | 19 +- nixos/modules/nixos/services/default.nix | 1 + .../nixos/services/qbittorrent/default.nix | 173 ++++++++++++++++++ 4 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 nixos/modules/nixos/services/qbittorrent/default.nix diff --git a/.vscode/settings.json b/.vscode/settings.json index a8bbf8c..ac3d269 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,16 +11,17 @@ "files.trimTrailingWhitespace": true, "sops.defaults.ageKeyFile": "age.key", "nix.enableLanguageServer": true, - "nix.serverPath": "/run/current-system/sw/bin/nil", + "nix.serverPath": "/run/current-system/sw/bin/nixd", "nix.formatterPath": "/run/current-system/sw/bin/nixfmt", "nix.serverSettings": { - "nil": { + "nixd": { "formatting": { "command": ["nixfmt"] }, - "diagnostics": { - "ignored": [], - "excludedFiles": [] + "options": { + "nixos": { + "expr": "(builtins.getFlake \"/home/jahanson/projects/mochi\").nixosConfigurations.shadowfax.options" + } } }, "nix": { diff --git a/nixos/hosts/shadowfax/default.nix b/nixos/hosts/shadowfax/default.nix index 966ac10..c2374c0 100644 --- a/nixos/hosts/shadowfax/default.nix +++ b/nixos/hosts/shadowfax/default.nix @@ -73,6 +73,11 @@ in }; }; + # Programs + environment.systemPackages = with pkgs; [ + # Headless qBittorrent - qbittorrent-nox + ]; + programs = { # 1Password cli _1password.enable = true; @@ -103,7 +108,6 @@ in # Minio 9000 # console web interface 9001 # api interface - ]; }; @@ -229,6 +233,19 @@ in publicCertPath = config.sops.secrets."syncthing/publicCert".path; privateKeyPath = config.sops.secrets."syncthing/privateKey".path; }; + # qBittorrent + qbittorrent = { + enable = true; + package = pkgs.unstable.qbittorrent.override { guiSupport = false; }; + user = "qbittorrent"; + group = "qbittorrent"; + dataDir = "/nahar/qbittorrent"; + downloadsDir = "/eru/media/qb/downloads/complete"; + webuiPort = 8456; + openFirewall = true; + hardening = true; + qbittorrentPort = 50413; + }; # ZFS nightly snapshot of container volumes zfs-nightly-snap = { enable = true; diff --git a/nixos/modules/nixos/services/default.nix b/nixos/modules/nixos/services/default.nix index 80ab71c..e8216b9 100644 --- a/nixos/modules/nixos/services/default.nix +++ b/nixos/modules/nixos/services/default.nix @@ -10,6 +10,7 @@ ./nix-index-daily ./onepassword-connect ./podman + ./qbittorrent ./reboot-required-check.nix ./sanoid ./syncthing diff --git a/nixos/modules/nixos/services/qbittorrent/default.nix b/nixos/modules/nixos/services/qbittorrent/default.nix new file mode 100644 index 0000000..698708d --- /dev/null +++ b/nixos/modules/nixos/services/qbittorrent/default.nix @@ -0,0 +1,173 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; +let + cfg = config.mySystem.services.qbittorrent; +in +{ + options.mySystem.services.qbittorrent = { + enable = mkEnableOption "qBittorrent"; + + package = mkOption { + type = types.package; + default = pkgs.qbittorrent; + description = "qBittorrent package to use"; + }; + + user = mkOption { + type = types.str; + default = "qbittorrent"; + description = "User account under which qBittorrent runs"; + }; + + group = mkOption { + type = types.str; + default = "qbittorrent"; + description = "Group under which qBittorrent runs"; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/qbittorrent"; + description = "Storage directory for qBittorrent data"; + }; + + downloadsDir = mkOption { + type = types.path; + default = "/var/lib/qbittorrent/downloads"; + description = "Location to store the downloads"; + }; + + webuiPort = mkOption { + type = types.port; + default = 8080; + description = "Port for qBittorrent web interface"; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open firewall port for web interface"; + }; + + hardening = mkOption { + type = types.bool; + default = true; + description = "Enable security hardening features"; + }; + + qbittorrentPort = mkOption { + type = types.port; + default = 6881; + description = "Port used for peer connections"; + }; + }; + + config = mkIf cfg.enable { + users.groups.${cfg.group} = { }; + users.users = mkIf (cfg.user == "qbittorrent") { + qbittorrent = { + inherit (cfg) group; + isSystemUser = true; + home = cfg.dataDir; + }; + }; + + environment.systemPackages = [ + ]; + + systemd.services.qbittorrent = { + environment = { + QBT_CONFIRM_LEGAL_NOTICE = "1"; + QBT_WEBUI_PORT = toString cfg.webuiPort; + QBT_TORRENTING_PORT = toString cfg.qbittorrentPort; + QBT_DOWNLOADS_PATH = "${cfg.dataDir}/downloads"; + XDG_CONFIG_HOME = cfg.dataDir; + XDG_DATA_HOME = cfg.dataDir; + CONFIG_DIR = "${cfg.dataDir}"; + CONFIG_FILE = "${cfg.dataDir}/qBittorrent.conf"; + LOG_DIR = "${cfg.dataDir}/logs"; + LOG_FILE = "${cfg.dataDir}/logs/qbittorrent.log"; + }; + + preStart = '' + # Ensure config directory exists + mkdir -p "$CONFIG_DIR" + + # Set up log directory and file + mkdir -p "$LOG_DIR" + + # Copy default config if it doesn't exist + if [[ ! -f "$CONFIG_FILE" ]]; then + cat > "$CONFIG_FILE" << EOF + [BitTorrent] + Session\DefaultSavePath=${cfg.downloadsDir} + Session\Port=${toString cfg.qbittorrentPort} + Session\TempPath=${cfg.downloadsDir}/temp + EOF + fi + + # Ensure correct permissions + chown -R ${cfg.user}:${cfg.group} "$CONFIG_DIR" + ''; + + serviceConfig = + { + ExecStart = "${cfg.package}/bin/qbittorrent-nox --profile=${cfg.dataDir}"; + ReadWritePaths = [ + "/nahar/qbittorrent" + "/eru/media" + ]; + Restart = "on-failure"; + RestartSec = 5; + } + // lib.mkIf cfg.hardening { + CapabilityBoundingSet = [ ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = "read-only"; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + "AF_UNIX" + ]; + RestrictNamespaces = [ + "uts" + "ipc" + "pid" + "user" + "cgroup" + "net" + ]; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ + cfg.webuiPort + cfg.qbittorrentPort + ]; + allowedUDPPorts = [ cfg.qbittorrentPort ]; + }; + }; +} -- 2.47.0 From 619a00618dae1b5ff5562de23f2fcd8b5541d97c Mon Sep 17 00:00:00 2001 From: Joseph Hanson Date: Fri, 24 Jan 2025 10:19:55 -0600 Subject: [PATCH 2/4] moreclean up and more hardening --- nixos/hosts/shadowfax/default.nix | 49 ++++++++----------- .../nixos/services/qbittorrent/default.nix | 40 +++++---------- 2 files changed, 32 insertions(+), 57 deletions(-) diff --git a/nixos/hosts/shadowfax/default.nix b/nixos/hosts/shadowfax/default.nix index c2374c0..68625e7 100644 --- a/nixos/hosts/shadowfax/default.nix +++ b/nixos/hosts/shadowfax/default.nix @@ -4,28 +4,26 @@ inputs, pkgs, ... -}: -let - sanoidConfig = import ./config/sanoid.nix { }; +}: let + sanoidConfig = import ./config/sanoid.nix {}; disks = import ./config/disks.nix; - smartdDevices = map (device: { inherit device; }) disks; -in -{ + smartdDevices = map (device: {inherit device;}) disks; +in { imports = [ inputs.disko.nixosModules.disko (import ../../profiles/disko-nixos.nix { - disks = [ "/dev/sda|/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_500GB_S58SNM0W406409E" ]; + disks = ["/dev/sda|/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_500GB_S58SNM0W406409E"]; }) inputs.nix-minecraft.nixosModules.minecraft-servers ]; boot = { initrd = { - kernelModules = [ "nfs" ]; - supportedFilesystems = [ "nfs" ]; + kernelModules = ["nfs"]; + supportedFilesystems = ["nfs"]; }; - binfmt.emulatedSystems = [ "aarch64-linux" ]; # Enabled for arm compilation + binfmt.emulatedSystems = ["aarch64-linux"]; # Enabled for arm compilation kernelModules = [ "vfio" @@ -33,11 +31,11 @@ in "vfio_pci" "vfio_virqfd" ]; - extraModulePackages = [ ]; - kernelParams = [ "zfs.zfs_arc_max=107374182400" ]; # 100GB + extraModulePackages = []; + kernelParams = ["zfs.zfs_arc_max=107374182400"]; # 100GB }; - swapDevices = [ ]; + swapDevices = []; hardware = { cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; @@ -47,7 +45,7 @@ in nvidia-container-toolkit.enable = true; }; - users.users.root.openssh.authorizedKeys.keys = [ ]; + users.users.root.openssh.authorizedKeys.keys = []; # Network settings networking = { @@ -73,11 +71,6 @@ in }; }; - # Programs - environment.systemPackages = with pkgs; [ - # Headless qBittorrent - qbittorrent-nox - ]; - programs = { # 1Password cli _1password.enable = true; @@ -124,7 +117,7 @@ in # Minio minio = { enable = true; - dataDir = [ "/eru/minio" ]; + dataDir = ["/eru/minio"]; rootCredentialsFile = config.sops.secrets."minio".path; }; @@ -151,7 +144,7 @@ in # Soft Serve - SSH git server soft-serve = { enable = true; - settings = import ./config/soft-serve.nix { }; + settings = import ./config/soft-serve.nix {}; }; # Tailscale @@ -163,7 +156,7 @@ in # VSCode Compatibility Settings vscode-server.enable = true; - xserver.videoDrivers = [ "nvidia" ]; + xserver.videoDrivers = ["nvidia"]; }; # sops @@ -173,19 +166,19 @@ in owner = "minio"; group = "minio"; mode = "400"; - restartUnits = [ "minio.service" ]; + restartUnits = ["minio.service"]; }; "syncthing/publicCert" = { sopsFile = ./secrets.sops.yaml; owner = "jahanson"; mode = "400"; - restartUnits = [ "syncthing.service" ]; + restartUnits = ["syncthing.service"]; }; "syncthing/privateKey" = { sopsFile = ./secrets.sops.yaml; owner = "jahanson"; mode = "400"; - restartUnits = [ "syncthing.service" ]; + restartUnits = ["syncthing.service"]; }; # "caddy/env" = { # sopsFile = ./secrets.sops.yaml; @@ -236,7 +229,7 @@ in # qBittorrent qbittorrent = { enable = true; - package = pkgs.unstable.qbittorrent.override { guiSupport = false; }; + package = pkgs.unstable.qbittorrent.override {guiSupport = false;}; user = "qbittorrent"; group = "qbittorrent"; dataDir = "/nahar/qbittorrent"; @@ -259,9 +252,9 @@ in system = { incus = { enable = true; - preseed = import ./config/incus-preseed.nix { }; + preseed = import ./config/incus-preseed.nix {}; }; - motd.networkInterfaces = [ "bond0" ]; + motd.networkInterfaces = ["bond0"]; nfs.enable = true; zfs.enable = true; zfs.mountPoolsAtBoot = [ diff --git a/nixos/modules/nixos/services/qbittorrent/default.nix b/nixos/modules/nixos/services/qbittorrent/default.nix index 698708d..2c9c9e4 100644 --- a/nixos/modules/nixos/services/qbittorrent/default.nix +++ b/nixos/modules/nixos/services/qbittorrent/default.nix @@ -78,6 +78,7 @@ in }; environment.systemPackages = [ + cfg.package ]; systemd.services.qbittorrent = { @@ -88,34 +89,13 @@ in QBT_DOWNLOADS_PATH = "${cfg.dataDir}/downloads"; XDG_CONFIG_HOME = cfg.dataDir; XDG_DATA_HOME = cfg.dataDir; - CONFIG_DIR = "${cfg.dataDir}"; - CONFIG_FILE = "${cfg.dataDir}/qBittorrent.conf"; + CONFIG_DIR = "${cfg.dataDir}/qBittorrent"; + CONFIG_FILE = "${cfg.dataDir}/qBittorrent/config/qBittorrent.conf"; LOG_DIR = "${cfg.dataDir}/logs"; LOG_FILE = "${cfg.dataDir}/logs/qbittorrent.log"; }; - preStart = '' - # Ensure config directory exists - mkdir -p "$CONFIG_DIR" - - # Set up log directory and file - mkdir -p "$LOG_DIR" - - # Copy default config if it doesn't exist - if [[ ! -f "$CONFIG_FILE" ]]; then - cat > "$CONFIG_FILE" << EOF - [BitTorrent] - Session\DefaultSavePath=${cfg.downloadsDir} - Session\Port=${toString cfg.qbittorrentPort} - Session\TempPath=${cfg.downloadsDir}/temp - EOF - fi - - # Ensure correct permissions - chown -R ${cfg.user}:${cfg.group} "$CONFIG_DIR" - ''; - - serviceConfig = + serviceConfig = lib.mkMerge [ { ExecStart = "${cfg.package}/bin/qbittorrent-nox --profile=${cfg.dataDir}"; ReadWritePaths = [ @@ -124,9 +104,12 @@ in ]; Restart = "on-failure"; RestartSec = 5; + User = cfg.user; + Group = cfg.group; } - // lib.mkIf cfg.hardening { - CapabilityBoundingSet = [ ]; + (lib.mkIf cfg.hardening { + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; DevicePolicy = "closed"; LockPersonality = true; MemoryDenyWriteExecute = true; @@ -141,8 +124,6 @@ in RestrictAddressFamilies = [ "AF_INET" "AF_INET6" - "AF_NETLINK" - "AF_UNIX" ]; RestrictNamespaces = [ "uts" @@ -159,7 +140,8 @@ in "~@privileged" "~@resources" ]; - }; + }) + ]; }; networking.firewall = mkIf cfg.openFirewall { -- 2.47.0 From 01e9ae49cabb3df5f478c3945d18f2566a81e410 Mon Sep 17 00:00:00 2001 From: Joseph Hanson Date: Fri, 24 Jan 2025 10:21:53 -0600 Subject: [PATCH 3/4] static locations to config locations --- nixos/modules/nixos/services/qbittorrent/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/modules/nixos/services/qbittorrent/default.nix b/nixos/modules/nixos/services/qbittorrent/default.nix index 2c9c9e4..5f36b37 100644 --- a/nixos/modules/nixos/services/qbittorrent/default.nix +++ b/nixos/modules/nixos/services/qbittorrent/default.nix @@ -99,8 +99,8 @@ in { ExecStart = "${cfg.package}/bin/qbittorrent-nox --profile=${cfg.dataDir}"; ReadWritePaths = [ - "/nahar/qbittorrent" - "/eru/media" + cfg.dataDir + cfg.downloadsDir ]; Restart = "on-failure"; RestartSec = 5; -- 2.47.0 From 5a421597b27d8ca6780e39228b9562c426b47f1b Mon Sep 17 00:00:00 2001 From: Joseph Hanson Date: Fri, 24 Jan 2025 10:26:06 -0600 Subject: [PATCH 4/4] update downloads dir setting --- nixos/modules/nixos/services/qbittorrent/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/modules/nixos/services/qbittorrent/default.nix b/nixos/modules/nixos/services/qbittorrent/default.nix index 5f36b37..eb4cd4d 100644 --- a/nixos/modules/nixos/services/qbittorrent/default.nix +++ b/nixos/modules/nixos/services/qbittorrent/default.nix @@ -51,7 +51,7 @@ in openFirewall = mkOption { type = types.bool; default = false; - description = "Open firewall port for web interface"; + description = "Open firewall ports for qBittorrent"; }; hardening = mkOption { @@ -86,7 +86,7 @@ in QBT_CONFIRM_LEGAL_NOTICE = "1"; QBT_WEBUI_PORT = toString cfg.webuiPort; QBT_TORRENTING_PORT = toString cfg.qbittorrentPort; - QBT_DOWNLOADS_PATH = "${cfg.dataDir}/downloads"; + QBT_DOWNLOADS_PATH = "${cfg.downloadsDir}"; XDG_CONFIG_HOME = cfg.dataDir; XDG_DATA_HOME = cfg.dataDir; CONFIG_DIR = "${cfg.dataDir}/qBittorrent"; -- 2.47.0