declare qbittorrent

This commit is contained in:
Danilo Reyes
2026-04-01 23:34:28 -06:00
parent 645b022bcf
commit 5a4f7c2734
5 changed files with 157 additions and 99 deletions

17
flake.lock generated
View File

@@ -1178,6 +1178,7 @@
"sops-nix": "sops-nix", "sops-nix": "sops-nix",
"stylix": "stylix", "stylix": "stylix",
"synctube": "synctube", "synctube": "synctube",
"trackerslist": "trackerslist",
"ucodenix": "ucodenix", "ucodenix": "ucodenix",
"wallpapers": "wallpapers" "wallpapers": "wallpapers"
} }
@@ -1426,6 +1427,22 @@
"type": "github" "type": "github"
} }
}, },
"trackerslist": {
"flake": false,
"locked": {
"lastModified": 1775081381,
"narHash": "sha256-LXlP3qKShp/8YVA0/lSUJkIbFsInnYtg8jAT5IfjfJk=",
"owner": "ngosang",
"repo": "trackerslist",
"rev": "446d148b04c96e8ffcb4f45f563713764a843ad0",
"type": "github"
},
"original": {
"owner": "ngosang",
"repo": "trackerslist",
"type": "github"
}
},
"ucodenix": { "ucodenix": {
"inputs": { "inputs": {
"cpu-microcodes": "cpu-microcodes" "cpu-microcodes": "cpu-microcodes"

View File

@@ -76,6 +76,10 @@
url = "github:StuffAnThings/qbit_manage"; url = "github:StuffAnThings/qbit_manage";
flake = false; flake = false;
}; };
trackerslist = {
url = "github:ngosang/trackerslist";
flake = false;
};
gallery-dl-src = { gallery-dl-src = {
url = "github:mikf/gallery-dl"; url = "github:mikf/gallery-dl";
flake = false; flake = false;

View File

@@ -9,7 +9,6 @@ let
lidarrMbGapId = 968; lidarrMbGapId = 968;
qbittorrentRouteTable = 200; qbittorrentRouteTable = 200;
serverInterface = config.my.interfaces.server; serverInterface = config.my.interfaces.server;
jawzUid = config.users.users.jawz.uid;
in in
{ {
imports = [ imports = [
@@ -100,17 +99,29 @@ in
interfaces.wg0.allowedTCPPorts = [ config.my.servers.nextcloud.port ]; interfaces.wg0.allowedTCPPorts = [ config.my.servers.nextcloud.port ];
}; };
}; };
systemd.services.qbittorrent-vpn-routing = lib.mkIf config.my.secureHost { systemd.services.qbittorrent-vpn-routing =
lib.mkIf (config.my.secureHost && config.services.qbittorrent.enable)
{
description = "Route qBittorrent user traffic through the VPS WireGuard tunnel"; description = "Route qBittorrent user traffic through the VPS WireGuard tunnel";
before = [ "qbittorrent.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ]; after = [
wants = [ "network-online.target" ]; "network-online.target"
"wireguard-wg0.service"
];
wants = [
"network-online.target"
"wireguard-wg0.service"
];
path = with pkgs; [ path = with pkgs; [
coreutils coreutils
gnugrep gnugrep
iproute2 iproute2
shadow
]; ];
script = '' script = ''
qbit_uid="$(id -u ${config.services.qbittorrent.user})"
for _ in $(seq 1 30); do for _ in $(seq 1 30); do
if ip -4 addr show dev wg0 >/dev/null 2>&1; then if ip -4 addr show dev wg0 >/dev/null 2>&1; then
break break
@@ -135,8 +146,8 @@ in
ip -4 route replace table ${toString qbittorrentRouteTable} ${config.my.ips.vps}/32 via ${config.my.ips.router} dev ${serverInterface} src ${config.my.ips.server} ip -4 route replace table ${toString qbittorrentRouteTable} ${config.my.ips.vps}/32 via ${config.my.ips.router} dev ${serverInterface} src ${config.my.ips.server}
ip -4 route replace table ${toString qbittorrentRouteTable} default via ${config.my.ips.wg-vps} dev wg0 src ${config.my.ips.wg-server} ip -4 route replace table ${toString qbittorrentRouteTable} default via ${config.my.ips.wg-vps} dev wg0 src ${config.my.ips.wg-server}
while ip -4 rule show | grep -q 'uidrange ${toString jawzUid}-${toString jawzUid} lookup ${toString qbittorrentRouteTable}'; do while ip -4 rule show | grep -q "uidrange ''${qbit_uid}-''${qbit_uid} lookup ${toString qbittorrentRouteTable}"; do
ip -4 rule del uidrange ${toString jawzUid}-${toString jawzUid} lookup ${toString qbittorrentRouteTable} ip -4 rule del uidrange "''${qbit_uid}-''${qbit_uid}" lookup ${toString qbittorrentRouteTable}
done done
while ip -4 rule show | grep -q 'from ${config.my.ips.wg-server} lookup ${toString qbittorrentRouteTable}'; do while ip -4 rule show | grep -q 'from ${config.my.ips.wg-server} lookup ${toString qbittorrentRouteTable}'; do
@@ -144,15 +155,17 @@ in
done done
ip -4 rule add from ${config.my.ips.wg-server}/32 lookup ${toString qbittorrentRouteTable} priority 9999 ip -4 rule add from ${config.my.ips.wg-server}/32 lookup ${toString qbittorrentRouteTable} priority 9999
ip -4 rule add uidrange ${toString jawzUid}-${toString jawzUid} lookup ${toString qbittorrentRouteTable} priority 10000 ip -4 rule add uidrange "''${qbit_uid}-''${qbit_uid}" lookup ${toString qbittorrentRouteTable} priority 10000
''; '';
preStop = '' preStop = ''
qbit_uid="$(id -u ${config.services.qbittorrent.user})"
while ip -4 rule show | grep -q 'from ${config.my.ips.wg-server} lookup ${toString qbittorrentRouteTable}'; do while ip -4 rule show | grep -q 'from ${config.my.ips.wg-server} lookup ${toString qbittorrentRouteTable}'; do
ip -4 rule del from ${config.my.ips.wg-server}/32 lookup ${toString qbittorrentRouteTable} || true ip -4 rule del from ${config.my.ips.wg-server}/32 lookup ${toString qbittorrentRouteTable} || true
done done
while ip -4 rule show | grep -q 'uidrange ${toString jawzUid}-${toString jawzUid} lookup ${toString qbittorrentRouteTable}'; do while ip -4 rule show | grep -q "uidrange ''${qbit_uid}-''${qbit_uid} lookup ${toString qbittorrentRouteTable}"; do
ip -4 rule del uidrange ${toString jawzUid}-${toString jawzUid} lookup ${toString qbittorrentRouteTable} || true ip -4 rule del uidrange "''${qbit_uid}-''${qbit_uid}" lookup ${toString qbittorrentRouteTable} || true
done done
ip -4 route del ${config.my.subnets.wg-homelab} dev wg0 || true ip -4 route del ${config.my.subnets.wg-homelab} dev wg0 || true

View File

@@ -147,7 +147,7 @@
}; };
qbittorrent = qbittorrent =
let let
url = "https://${config.my.ips.server}:${toString config.my.servers.qbittorrent.port}"; url = "http://${config.my.ips.server}:${toString config.my.servers.qbittorrent.port}";
name = "qbittorrent"; name = "qbittorrent";
in in
lib.mkIf config.my.servers.qbittorrent.enable { lib.mkIf config.my.servers.qbittorrent.enable {

View File

@@ -6,12 +6,13 @@
... ...
}: }:
let let
inherit (inputs) qbit_manage; inherit (inputs) qbit_manage trackerslist;
vuetorrent = pkgs.fetchzip { qbitProfileDir = "/var/lib/qbittorrent";
url = "https://github.com/VueTorrent/VueTorrent/releases/download/v2.31.0/vuetorrent.zip"; qbitUser = "qbittorrent";
sha256 = "sha256-kVDnDoCoJlY2Ew71lEMeE67kNOrKTJEMqNj2OfP01qw="; wgInterface = "wg0";
stripRoot = true; qbitDownloadDir = "/srv/pool/multimedia/downloads/torrent";
}; qbitIncompleteDir = "${qbitDownloadDir}/.incomplete";
additionalTrackers = lib.strings.trim (builtins.readFile "${trackerslist}/trackers_all.txt");
qbit_manageEnv = pkgs.python3.withPackages ( qbit_manageEnv = pkgs.python3.withPackages (
ps: ps:
builtins.attrValues { builtins.attrValues {
@@ -33,8 +34,7 @@ let
} }
); );
torrentCompletionScript = pkgs.writeShellScript "qbit-torrent-completion" '' torrentCompletionScript = pkgs.writeShellScript "qbit-torrent-completion" ''
chown jawz:piracy -R "$1" chmod -R u+rwX,g+rwX,o-rwx "$1"
chmod -R 775 "$1"
''; '';
in in
{ {
@@ -51,51 +51,75 @@ in
}; };
config = lib.mkIf (config.my.servers.qbittorrent.enable && config.my.secureHost) { config = lib.mkIf (config.my.servers.qbittorrent.enable && config.my.secureHost) {
my.network.firewall.additionalPorts = [ config.my.servers.qbittorrent.port ]; my.network.firewall.additionalPorts = [ config.my.servers.qbittorrent.port ];
home-manager.users.jawz.xdg.dataFile.vuetorrent.source = vuetorrent; services.qbittorrent = {
home-manager.users.jawz.imports = [ enable = true;
( user = qbitUser;
{ lib, ... }: group = "piracy";
{ profileDir = qbitProfileDir;
home.activation.qbittorrentAutorunCommand = lib.hm.dag.entryAfter [ "writeBoundary" ] '' webuiPort = config.my.servers.qbittorrent.port;
conf=/home/jawz/.config/qBittorrent/qBittorrent.conf torrentingPort = config.my.ports.qbittorrent;
if [ -f "$conf" ]; then extraArgs = [ "--confirm-legal-notice" ];
sed -i \ serverConfig = {
's|^program=.*$|program=${torrentCompletionScript} %F|' \ AutoRun = {
"$conf" enabled = true;
fi program = "${torrentCompletionScript} %F";
''; };
} BitTorrent.Session = {
) AddExtensionToIncompleteFiles = true;
AddTrackersEnabled = true;
AdditionalTrackers = additionalTrackers;
AnnounceIP = config.my.ips.vps;
DefaultSavePath = qbitDownloadDir;
GlobalMaxRatio = 1;
GlobalMaxSeedingMinutes = 120;
Interface = wgInterface;
InterfaceAddress = config.my.ips.wg-server;
InterfaceName = wgInterface;
MaxActiveDownloads = 5;
MaxActiveTorrents = 100;
MaxActiveUploads = 90;
QueueingSystemEnabled = true;
ShareLimitAction = "Stop";
TempPath = qbitIncompleteDir;
TempPathEnabled = true;
};
Core.AutoDeleteAddedTorrentFile = "Never";
Network.PortForwardingEnabled = false;
Preferences = {
Bittorrent.CustomizeTrackersListUrl = "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt";
WebUI = {
AlternativeUIEnabled = true;
HTTPS.Enabled = false;
Password_PBKDF2 = "@ByteArray(ZYy4l3ORHihzBrRYqIfmOA==:SYv4Gor5ZSI9FfAPOxAAdhlmz/h+vViEHnoW6tfJYLNnFL9DQ8udqkO9Na83RJauzhVVGvAgauPq/y4UNPyl3g==)";
ReverseProxySupportEnabled = false;
RootFolder = "${pkgs.vuetorrent}/share/vuetorrent";
Username = "9VcZWt3d0u6mmMOhryUZAcpIe8WML9J7Icj00Vu5fuOFXRB0ECAItetKAK1NnfrD";
};
};
};
};
users.users.${qbitUser} = {
home = qbitProfileDir;
createHome = true;
};
systemd.tmpfiles.rules = [
"d ${qbitDownloadDir} 0775 ${qbitUser} piracy -"
"d ${qbitIncompleteDir} 0775 ${qbitUser} piracy -"
]; ];
sops.secrets = sops.secrets =
let let
mkQbitSecret = file: mode: {
inherit mode;
inherit (config.users.users.jawz) group;
sopsFile = ../../secrets/keys.yaml;
owner = config.users.users.jawz.name;
path = "/home/jawz/.config/qBittorrent/ssl/${file}";
};
mkUnpackerrSecret = { mkUnpackerrSecret = {
sopsFile = ../../secrets/secrets.yaml; sopsFile = ../../secrets/secrets.yaml;
owner = config.users.users.jawz.name; owner = config.users.users.jawz.name;
}; };
in in
{ {
"certificates/qbit_cert" = mkQbitSecret "server.crt" "0644";
"certificates/qbit_key" = mkQbitSecret "server.key" "0600";
"unpackerr/sonarr-api" = mkUnpackerrSecret; "unpackerr/sonarr-api" = mkUnpackerrSecret;
"unpackerr/radarr-api" = mkUnpackerrSecret; "unpackerr/radarr-api" = mkUnpackerrSecret;
}; };
systemd = { systemd = {
packages = [ packages = [ torrentCompletionScript ];
pkgs.qbittorrent-nox services = {
torrentCompletionScript
];
services."qbittorrent-nox@jawz" = {
enable = true;
overrideStrategy = "asDropin";
wantedBy = [ "multi-user.target" ];
}; };
user = { user = {
services = { services = {