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 ]; + }; + }; +}