From 7c55eed58963b98c86d4330039433570c7ba489e Mon Sep 17 00:00:00 2001 From: bolives-hax Date: Mon, 12 Feb 2024 14:19:27 -0800 Subject: [PATCH 1/7] updated rutorrent version --- nixos/modules/module-list.nix | 1 + nixos/modules/services/torrent/rtorrent.nix | 10 +- nixos/modules/services/web-apps/rutorrent.nix | 300 ++++++++++++++++++ .../networking/p2p/rutorrent/default.nix | 25 ++ pkgs/top-level/all-packages.nix | 2 + 5 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 nixos/modules/services/web-apps/rutorrent.nix create mode 100644 pkgs/applications/networking/p2p/rutorrent/default.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index ec022713e12e..23d769a029d6 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1348,6 +1348,7 @@ ./services/web-apps/pretalx.nix ./services/web-apps/prosody-filer.nix ./services/web-apps/rimgo.nix + ./services/web-apps/rutorrent.nix ./services/web-apps/sftpgo.nix ./services/web-apps/suwayomi-server.nix ./services/web-apps/rss-bridge.nix diff --git a/nixos/modules/services/torrent/rtorrent.nix b/nixos/modules/services/torrent/rtorrent.nix index 699f3be82a9d..90cb9d980d17 100644 --- a/nixos/modules/services/torrent/rtorrent.nix +++ b/nixos/modules/services/torrent/rtorrent.nix @@ -80,6 +80,14 @@ in { ''; }; + rpcGroup = mkOption { + type = types.str; + default = "rtorrent"; + description = '' + The group owning the RPC socket. + ''; + }; + configText = mkOption { type = types.lines; default = ""; @@ -180,7 +188,7 @@ in { # XMLRPC scgi_local = (cfg.rpcsock) - schedule = scgi_group,0,0,"execute.nothrow=chown,\":rtorrent\",(cfg.rpcsock)" + schedule = scgi_group,0,0,"execute.nothrow=chown,\":${cfg.rpcGroup}\",(cfg.rpcsock)" schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",(cfg.rpcsock)" ''; diff --git a/nixos/modules/services/web-apps/rutorrent.nix b/nixos/modules/services/web-apps/rutorrent.nix new file mode 100644 index 000000000000..eea200ca8d22 --- /dev/null +++ b/nixos/modules/services/web-apps/rutorrent.nix @@ -0,0 +1,300 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.rutorrent; + + rtorrentPluginDependencies = with pkgs; { + _task = [ procps ]; + unpack = [ unzip unrar ]; + rss = [ curl ]; + mediainfo = [ mediainfo ]; + spectrogram = [ sox ]; + screenshots = [ ffmpeg ]; + }; + + phpPluginDependencies = with pkgs; { + _cloudflare = [ python3 ]; + }; + + getPluginDependencies = dependencies: concatMap (p: attrByPath [ p ] [] dependencies); + +in { + options = { + services.rutorrent = { + enable = mkEnableOption "ruTorrent"; + + hostName = mkOption { + type = types.str; + description = "FQDN for the ruTorrent instance."; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/rutorrent"; + description = "Storage path of ruTorrent."; + }; + + user = mkOption { + type = types.str; + default = "rutorrent"; + description = '' + User which runs the ruTorrent service. + ''; + }; + + group = mkOption { + type = types.str; + default = "rutorrent"; + description = '' + Group which runs the ruTorrent service. + ''; + }; + + rpcSocket = mkOption { + type = types.str; + default = config.services.rtorrent.rpcSocket; + defaultText = "config.services.rtorrent.rpcSocket"; + description = '' + Path to rtorrent rpc socket. + ''; + }; + + plugins = mkOption { + type = with types; listOf (either str package); + default = [ "httprpc" ]; + example = literalExample ''[ "httprpc" "data" "diskspace" "edit" "erasedata" "theme" "trafic" ]''; + description = '' + List of plugins to enable. See the list of available plugins. Note: the unpack plugin needs the nonfree unrar package. + You need to either enable one of the rpc or httprpc plugin or enable the option. + ''; + }; + + poolSettings = mkOption { + type = with types; attrsOf (oneOf [ str int bool ]); + default = { + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 4; + "pm.max_requests" = 500; + }; + description = '' + Options for ruTorrent's PHP pool. See the documentation on php-fpm.conf for details on configuration directives. + ''; + }; + + nginx = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable nginx virtual host management. + Further nginx configuration can be done by adapting services.nginx.virtualHosts.<name>. + See for further information. + ''; + }; + + exposeInsecureRPC2mount = mkOption { + type = types.bool; + default = false; + description = '' + If you do not enable one of the rpc or httprpc plugins you need to expose an RPC mount through scgi using this option. + Warning: This allow to run arbitrary commands, as the rtorrent user, so make sure to use authentification. The simplest way would be to use the services.nginx.virtualHosts.<name>.basicAuth option. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { assertions = let + usedRpcPlugins = intersectLists cfg.plugins [ "httprpc" "rpc" ]; + in [ + { assertion = (length usedRpcPlugins < 2); + message = "Please specify only one of httprpc or rpc plugins"; + } + { assertion = !(length usedRpcPlugins > 0 && cfg.nginx.exposeInsecureRPC2mount); + message = "Please do not use exposeInsecureRPC2mount if you use one of httprpc or rpc plugins"; + } + ]; + + warnings = let + nginxVhostCfg = config.services.nginx.virtualHosts."${cfg.hostName}"; + in [] + ++ (optional (cfg.nginx.exposeInsecureRPC2mount && (nginxVhostCfg.basicAuth == {} || nginxVhostCfg.basicAuthFile == null )) '' + You are using exposeInsecureRPC2mount without using basic auth on the virtual host. The exposed rpc mount allow for remote command execution. + + Please make sure it is not accessible from the outside. + ''); + + systemd = { + services = { + rtorrent.path = getPluginDependencies rtorrentPluginDependencies cfg.plugins; + rutorrent-setup = let + rutorrentConfig = pkgs.writeText "rutorrent-config.php" '' + false, + 'proto' => 'http', // 'http' or 'https' + 'host' => 'PROXY_HOST_HERE', + 'port' => 3128 + ); + + @define('RPC_TIME_OUT', 5, true); // in seconds + + @define('LOG_RPC_CALLS', false, true); + @define('LOG_RPC_FAULTS', true, true); + + // for php + @define('PHP_USE_GZIP', false, true); + @define('PHP_GZIP_LEVEL', 2, true); + + $schedule_rand = 10; // rand for schedulers start, +0..X seconds + + $do_diagnostic = true; + $log_file = '${cfg.dataDir}/logs/errors.log'; // path to log file (comment or leave blank to disable logging) + + $saveUploadedTorrents = true; // Save uploaded torrents to profile/torrents directory or not + $overwriteUploadedTorrents = false; // Overwrite existing uploaded torrents in profile/torrents directory or make unique name + + $topDirectory = '/'; // Upper available directory. Absolute path with trail slash. + $forbidUserSettings = false; + + $scgi_port = 0; + $scgi_host = "unix://${cfg.rpcSocket}"; + + $XMLRPCMountPoint = "/RPC2"; // DO NOT DELETE THIS LINE!!! DO NOT COMMENT THIS LINE!!! + + $pathToExternals = array( + "php" => "${pkgs.php}/bin/php", // Something like /usr/bin/php. If empty, will be found in PATH. + "curl" => "${pkgs.curl}/bin/curl", // Something like /usr/bin/curl. If empty, will be found in PATH. + "gzip" => "${pkgs.gzip}/bin/gzip", // Something like /usr/bin/gzip. If empty, will be found in PATH. + "id" => "${pkgs.coreutils}/bin/id", // Something like /usr/bin/id. If empty, will be found in PATH. + "stat" => "${pkgs.coreutils}/bin/stat", // Something like /usr/bin/stat. If empty, will be found in PATH. + "pgrep" => "${pkgs.procps}/bin/pgrep", // TODO why can't we use phpEnv.PATH + ); + + $localhosts = array( // list of local interfaces + "127.0.0.1", + "localhost", + ); + + $profilePath = '${cfg.dataDir}/share'; // Path to user profiles + $profileMask = 0770; // Mask for files and directory creation in user profiles. + // Both Webserver and rtorrent users must have read-write access to it. + // For example, if Webserver and rtorrent users are in the same group then the value may be 0770. + + $tempDirectory = null; // Temp directory. Absolute path with trail slash. If null, then autodetect will be used. + + $canUseXSendFile = false; // If true then use X-Sendfile feature if it exist + + $locale = "UTF8"; + ''; + in { + wantedBy = [ "multi-user.target" ]; + before = [ "phpfpm-rutorrent.service" ]; + script = '' + ln -sf ${pkgs.rutorrent}/{css,images,js,lang,index.html} ${cfg.dataDir}/ + mkdir -p ${cfg.dataDir}/{conf,logs,plugins} ${cfg.dataDir}/share/{settings,torrents,users} + ln -sf ${pkgs.rutorrent}/conf/{access.ini,plugins.ini} ${cfg.dataDir}/conf/ + ln -sf ${rutorrentConfig} ${cfg.dataDir}/conf/config.php + + cp -r ${pkgs.rutorrent}/php ${cfg.dataDir}/ + + ${optionalString (cfg.plugins != []) + ''cp -r ${concatMapStringsSep " " (p: "${pkgs.rutorrent}/plugins/${p}") cfg.plugins} ${cfg.dataDir}/plugins/''} + + chown -R ${cfg.user}:${cfg.group} ${cfg.dataDir}/{conf,share,logs,plugins} + chmod -R 755 ${cfg.dataDir}/{conf,share,logs,plugins} + ''; + serviceConfig.Type = "oneshot"; + }; + }; + + tmpfiles.rules = [ "d '${cfg.dataDir}' 0775 ${cfg.user} ${cfg.group} -" ]; + }; + + users.groups."${cfg.group}" = {}; + + users.users = { + "${cfg.user}" = { + home = cfg.dataDir; + group = cfg.group; + extraGroups = [ config.services.rtorrent.group ]; + description = "ruTorrent Daemon user"; + isSystemUser = true; + }; + + "${config.services.rtorrent.user}" = { + extraGroups = [ cfg.group ]; + }; + }; + } + + (mkIf cfg.nginx.enable (mkMerge [ + { services = { + phpfpm.pools.rutorrent = let + envPath = lib.makeBinPath (getPluginDependencies phpPluginDependencies cfg.plugins); + pool = { + user = cfg.user; + group = config.services.rtorrent.group; + settings = mapAttrs (name: mkDefault) { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + } // cfg.poolSettings; + }; + in if (envPath == "") then pool else pool // { phpEnv.PATH = envPath; }; + + nginx = { + enable = true; + virtualHosts = { + ${cfg.hostName} = { + root = cfg.dataDir; + locations = { + "~ [^/]\.php(/|$)" = { + extraConfig = '' + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + if (!-f $document_root$fastcgi_script_name) { + return 404; + } + + # Mitigate https://httpoxy.org/ vulnerabilities + fastcgi_param HTTP_PROXY ""; + + fastcgi_pass unix:${config.services.phpfpm.pools.rutorrent.socket}; + fastcgi_index index.php; + + include ${pkgs.nginx}/conf/fastcgi.conf; + ''; + }; + }; + }; + }; + }; + }; + } + + (mkIf cfg.nginx.exposeInsecureRPC2mount { + services.nginx.virtualHosts."${cfg.hostName}".locations."/RPC2" = { + extraConfig = '' + include ${pkgs.nginx}/conf/scgi_params; + scgi_pass unix:${cfg.rpcSocket}; + ''; + }; + + services.rtorrent.rpcGroup = "nginx"; + }) + ])) + ]); +} diff --git a/pkgs/applications/networking/p2p/rutorrent/default.nix b/pkgs/applications/networking/p2p/rutorrent/default.nix new file mode 100644 index 000000000000..3d7fc10d8247 --- /dev/null +++ b/pkgs/applications/networking/p2p/rutorrent/default.nix @@ -0,0 +1,25 @@ +{stdenv, lib, fetchFromGitHub }: + +stdenv.mkDerivation rec { + pname = "rutorrent"; + version = "4.2.10-hotfix"; + + src = fetchFromGitHub { + owner = "Novik"; + repo = "ruTorrent"; + rev = "v${version}"; + sha256 = "GmAF1doqkEX2xyu+hsvchwNuXs8xvtYs4s14MPcxKKk="; + }; + + installPhase = '' + mkdir -p $out/ + cp -r . $out/ + ''; + + meta = with lib; { + description = "Yet another web front-end for rTorrent"; + homepage = "https://github.com/Novik/ruTorrent"; + license = licenses.gpl3Plus; + platforms = platforms.unix; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index e9439ffe2464..da4d125faa60 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -34985,6 +34985,8 @@ with pkgs; rusty-psn-gui = rusty-psn.override { withGui = true; }; + rutorrent = callPackage ../applications/networking/p2p/rutorrent {}; + rymcast = callPackage ../applications/audio/rymcast { inherit (gnome) zenity; }; From ef37a35c745c66e48e4f51f2c7668719e259eb3a Mon Sep 17 00:00:00 2001 From: bolive-hax Date: Mon, 12 Feb 2024 23:49:26 +0100 Subject: [PATCH 2/7] typo --- pkgs/applications/networking/p2p/rutorrent/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/applications/networking/p2p/rutorrent/default.nix b/pkgs/applications/networking/p2p/rutorrent/default.nix index 3d7fc10d8247..1b22c27b29a5 100644 --- a/pkgs/applications/networking/p2p/rutorrent/default.nix +++ b/pkgs/applications/networking/p2p/rutorrent/default.nix @@ -2,7 +2,7 @@ stdenv.mkDerivation rec { pname = "rutorrent"; - version = "4.2.10-hotfix"; + version = "4.2.10"; src = fetchFromGitHub { owner = "Novik"; From c784691d6e2b4d8b5dae3739867c18c3671cbdf7 Mon Sep 17 00:00:00 2001 From: bolive-hax Date: Tue, 13 Feb 2024 00:17:57 +0100 Subject: [PATCH 3/7] updated + working --- nixos/modules/services/web-apps/rutorrent.nix | 2 ++ pkgs/applications/networking/p2p/rutorrent/default.nix | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/web-apps/rutorrent.nix b/nixos/modules/services/web-apps/rutorrent.nix index eea200ca8d22..e32aec081a2e 100644 --- a/nixos/modules/services/web-apps/rutorrent.nix +++ b/nixos/modules/services/web-apps/rutorrent.nix @@ -176,6 +176,8 @@ in { $XMLRPCMountPoint = "/RPC2"; // DO NOT DELETE THIS LINE!!! DO NOT COMMENT THIS LINE!!! + $throttleMaxSpeed = 327625*1024; + $pathToExternals = array( "php" => "${pkgs.php}/bin/php", // Something like /usr/bin/php. If empty, will be found in PATH. "curl" => "${pkgs.curl}/bin/curl", // Something like /usr/bin/curl. If empty, will be found in PATH. diff --git a/pkgs/applications/networking/p2p/rutorrent/default.nix b/pkgs/applications/networking/p2p/rutorrent/default.nix index 1b22c27b29a5..eeff6207d915 100644 --- a/pkgs/applications/networking/p2p/rutorrent/default.nix +++ b/pkgs/applications/networking/p2p/rutorrent/default.nix @@ -8,7 +8,7 @@ stdenv.mkDerivation rec { owner = "Novik"; repo = "ruTorrent"; rev = "v${version}"; - sha256 = "GmAF1doqkEX2xyu+hsvchwNuXs8xvtYs4s14MPcxKKk="; + sha256 = "Hkh2fWaZpJLxUYaojR97XVQWXTRzmFkQe4xKsmY1E8M="; }; installPhase = '' From 3cd09aea9144812a8c782975e1749f3d31ad9db3 Mon Sep 17 00:00:00 2001 From: bl0v3 Date: Tue, 25 Jun 2024 01:10:29 +0200 Subject: [PATCH 4/7] switching to pkgs/by-name/ru/rutorrent/package.nix --- .../rutorrent/default.nix => by-name/ru/rutorrent/package.nix} | 0 pkgs/top-level/all-packages.nix | 2 -- 2 files changed, 2 deletions(-) rename pkgs/{applications/networking/p2p/rutorrent/default.nix => by-name/ru/rutorrent/package.nix} (100%) diff --git a/pkgs/applications/networking/p2p/rutorrent/default.nix b/pkgs/by-name/ru/rutorrent/package.nix similarity index 100% rename from pkgs/applications/networking/p2p/rutorrent/default.nix rename to pkgs/by-name/ru/rutorrent/package.nix diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 168ab542b061..0c0a7ef9da36 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -33998,8 +33998,6 @@ with pkgs; rusty-psn-gui = rusty-psn.override { withGui = true; }; - rutorrent = callPackage ../applications/networking/p2p/rutorrent {}; - rymcast = callPackage ../applications/audio/rymcast { inherit (gnome) zenity; }; From 9abeee7aab52788f4c06c3558835ed5f7bfaed47 Mon Sep 17 00:00:00 2001 From: bl0v3 Date: Tue, 25 Jun 2024 01:17:47 +0200 Subject: [PATCH 5/7] fixed doc --- nixos/modules/services/web-apps/rutorrent.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/web-apps/rutorrent.nix b/nixos/modules/services/web-apps/rutorrent.nix index e32aec081a2e..e6ee43113a03 100644 --- a/nixos/modules/services/web-apps/rutorrent.nix +++ b/nixos/modules/services/web-apps/rutorrent.nix @@ -64,7 +64,7 @@ in { plugins = mkOption { type = with types; listOf (either str package); default = [ "httprpc" ]; - example = literalExample ''[ "httprpc" "data" "diskspace" "edit" "erasedata" "theme" "trafic" ]''; + example = literalExpression ''[ "httprpc" "data" "diskspace" "edit" "erasedata" "theme" "trafic" ]''; description = '' List of plugins to enable. See the list of available plugins. Note: the unpack plugin needs the nonfree unrar package. You need to either enable one of the rpc or httprpc plugin or enable the option. From 4575922a1fece39afd9fd6430c84ed3f55cadad6 Mon Sep 17 00:00:00 2001 From: bl0v3 Date: Mon, 22 Jul 2024 01:00:28 +0200 Subject: [PATCH 6/7] removed rtorrent group as fixed in #285299 --- nixos/modules/services/torrent/rtorrent.nix | 38 +++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/nixos/modules/services/torrent/rtorrent.nix b/nixos/modules/services/torrent/rtorrent.nix index d67dc3c79532..609b06b5e706 100644 --- a/nixos/modules/services/torrent/rtorrent.nix +++ b/nixos/modules/services/torrent/rtorrent.nix @@ -8,6 +8,8 @@ let opt = options.services.rtorrent; in { + meta.maintainers = with lib.maintainers; [ thiagokokada ]; + options.services.rtorrent = { enable = mkEnableOption "rtorrent"; @@ -80,14 +82,6 @@ in { ''; }; - rpcGroup = mkOption { - type = types.str; - default = "rtorrent"; - description = '' - The group owning the RPC socket. - ''; - }; - configText = mkOption { type = types.lines; default = ""; @@ -188,7 +182,7 @@ in { # XMLRPC scgi_local = (cfg.rpcsock) - schedule = scgi_group,0,0,"execute.nothrow=chown,\":${cfg.rpcGroup}\",(cfg.rpcsock)" + schedule = scgi_group,0,0,"execute.nothrow=chown,\":${cfg.group}\",(cfg.rpcsock)" schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",(cfg.rpcsock)" ''; @@ -210,7 +204,31 @@ in { ExecStartPre=''${pkgs.bash}/bin/bash -c "if test -e ${cfg.dataDir}/session/rtorrent.lock && test -z $(${pkgs.procps}/bin/pidof rtorrent); then rm -f ${cfg.dataDir}/session/rtorrent.lock; fi"''; ExecStart="${cfg.package}/bin/rtorrent -n -o system.daemon.set=true -o import=${rtorrentConfigFile}"; RuntimeDirectory = "rtorrent"; - RuntimeDirectoryMode = 755; + RuntimeDirectoryMode = 750; + + CapabilityBoundingSet = [ "" ]; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + # If the default user is changed, there is a good chance that they + # want to store data in e.g.: $HOME directory + # Relax hardening in this case + ProtectHome = lib.mkIf (cfg.user == "rtorrent") true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; }; }; }; From 7c1315628b7a93652583e373a40035ede8a0046b Mon Sep 17 00:00:00 2001 From: bl0v3 Date: Mon, 22 Jul 2024 01:11:07 +0200 Subject: [PATCH 7/7] fix exposeInsecureRPC2mount rtorrent group --- nixos/modules/services/web-apps/rutorrent.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/web-apps/rutorrent.nix b/nixos/modules/services/web-apps/rutorrent.nix index e6ee43113a03..cce701fef497 100644 --- a/nixos/modules/services/web-apps/rutorrent.nix +++ b/nixos/modules/services/web-apps/rutorrent.nix @@ -295,7 +295,7 @@ in { ''; }; - services.rtorrent.rpcGroup = "nginx"; + services.rtorrent.group = "nginx"; }) ])) ]);