From 8f137c28230623259a964484adcf31fe00756594 Mon Sep 17 00:00:00 2001 From: Azat Bahawi Date: Sat, 17 Dec 2022 16:39:09 +0300 Subject: 2022-12-17 --- modules/nixos/rtorrent.nix | 297 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 modules/nixos/rtorrent.nix (limited to 'modules/nixos/rtorrent.nix') diff --git a/modules/nixos/rtorrent.nix b/modules/nixos/rtorrent.nix new file mode 100644 index 0000000..4014a3b --- /dev/null +++ b/modules/nixos/rtorrent.nix @@ -0,0 +1,297 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.rtorrent; +in { + options.nixfiles.modules.rtorrent = { + enable = mkEnableOption "rTorrent"; + + flood = { + enable = mkEnableOption "Flood" // {default = cfg.enable;}; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "flood.${config.networking.domain}"; + }; + }; + }; + + config = let + user = "rtorrent"; + group = "rtorrent"; + baseDir = "/var/lib/rtorrent"; + rpcSocket = "${baseDir}/rpc.socket"; + in + mkIf cfg.enable (mkMerge [ + (let + port = 50000; + in { + systemd = { + services.rtorrent = { + description = "rTorrent"; + after = ["network.target" "local-fs.target"]; + serviceConfig = let + leechDir = "${baseDir}/leech"; + seedDir = "${baseDir}/seed"; + sessionDir = "${baseDir}/session"; + logDir = "${baseDir}/log"; + configFile = let + moveCompleted = let + pkg = pkgs.writeShellApplication { + name = "move-completed"; + runtimeInputs = with pkgs; [ + coreutils-full + gnused + findutils + ]; + text = '' + set -x + + leech_path="$1" + seed_path="$2" + # seed_path="$(echo "$2" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b')" + + mkdir -pv "$seed_path" + mv -fv "$leech_path" "$seed_path" + ''; + }; + in "${pkg}/bin/move-completed"; + in + pkgs.writeText "rtorrent.rc" '' + method.insert = cfg.leech, private|const|string, (cat, "${leechDir}") + method.insert = cfg.seed, private|const|string, (cat, "${seedDir}") + method.insert = cfg.session, private|const|string, (cat, "${sessionDir}") + method.insert = cfg.log, private|const|string, (cat, "${logDir}") + method.insert = cfg.rpcsocket, private|const|string, (cat, "${rpcSocket}") + + directory.default.set = (cat, (cfg.leech)) + session.path.set = (cat, (cfg.session)) + + network.port_range.set = ${toString port}-${toString port} + network.port_random.set = no + + dht.mode.set = disable + protocol.pex.set = no + + trackers.use_udp.set = no + + protocol.encryption.set = allow_incoming,try_outgoing,enable_retry + + pieces.memory.max.set = ${toString (pow 2 11)}M + pieces.preload.type.set = 2 + + network.xmlrpc.size_limit.set = ${toString (pow 2 17)} + + network.max_open_files.set = ${toString (pow 2 10)} + network.max_open_sockets.set = ${toString (pow 2 10)} + + network.http.max_open.set = ${toString (pow 2 8)} + + throttle.global_down.max_rate.set_kb = 0 + throttle.global_up.max_rate.set_kb = 0 + + encoding.add = UTF-8 + system.umask.set = 0027 + system.cwd.set = (directory.default) + + network.scgi.open_local = (cat, (cfg.rpcsocket)) + + method.insert = d.move_completed, simple, "\ + d.directory.set=$argument.1=;\ + execute=${moveCompleted}, $argument.0=, $argument.1=;\ + d.save_full_session=\ + " + method.insert = d.leech_path, simple, "\ + if=(d.is_multi_file),\ + (cat, (d.directory), /),\ + (cat, (d.directory), /, (d.name))\ + " + method.insert = d.seed_path, simple, "\ + cat=$cfg.seed=, /, $d.custom1=\ + " + method.set_key = event.download.finished, move_complete, "\ + d.move_completed=$d.leech_path=, $d.seed_path=\ + " + + log.open_file = "log", (cat, (cfg.log), "/", "default.log") + log.add_output = "info", "log" + log.execute = (cat, (cfg.log), "/", "execute.log") + ''; + in { + Restart = "on-failure"; + RestartSec = 3; + + KillMode = "process"; + KillSignal = "SIGHUP"; + + User = user; + Group = group; + + ExecStartPre = concatStringsSep " " [ + "${pkgs.coreutils-full}/bin/mkdir -p" + leechDir + seedDir + sessionDir + logDir + ]; + ExecStart = concatStringsSep " " [ + "${pkgs.rtorrent}/bin/rtorrent" + "-n" + "-o system.daemon.set=true" + "-o network.bind_address.set=0.0.0.0" + "-o import=${configFile}" + ]; + ExecStop = concatStringsSep " " [ + "${pkgs.coreutils-full}/bin/rm -rf" + rpcSocket + ]; + + RuntimeDirectory = "rtorrent"; + RuntimeDirectoryMode = 0750; + UMask = 0027; + AmbientCapabilities = [""]; + CapabilityBoundingSet = [""]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProcSubset = "pid"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = ["@system-service" "~@resources" "~@privileged"]; + }; + wantedBy = ["multi-user.target"]; + }; + + tmpfiles.rules = ["d '${baseDir}' 0750 ${user} ${group} -"]; + }; + + users = { + users.${user} = { + inherit group; + shell = pkgs.bashInteractive; + home = baseDir; + description = "rTorrent"; + isSystemUser = true; + }; + groups.${group} = {}; + }; + my.extraGroups = [group]; + + networking.firewall.allowedTCPPorts = [port]; + + boot.kernel.sysctl = { + "net.core.rmem_max" = mkOverride 500 (pow 2 24); + "net.core.wmem_max" = mkOverride 500 (pow 2 24); + "net.ipv4.tcp_fin_timeout" = mkOverride 500 30; + "net.ipv4.tcp_rmem" = mkOverride 500 (mkTcpMem 12 23 24); + "net.ipv4.tcp_slow_start_after_idle" = 0; + "net.ipv4.tcp_tw_recycle" = mkOverride 500 1; + "net.ipv4.tcp_tw_reuse" = mkOverride 500 1; + "net.ipv4.tcp_wmem" = mkOverride 500 (mkTcpMem 12 23 24); + }; + }) + (let + port = 50001; + pkg = pkgs.nodePackages.flood; + in + mkIf cfg.flood.enable { + nixfiles.modules.nginx = { + enable = true; + upstreams.flood.servers."127.0.0.1:${toString port}" = {}; + virtualHosts.${cfg.flood.domain} = { + root = "${pkg}/lib/node_modules/flood/dist/assets"; + locations = { + "/".tryFiles = "$uri /index.html"; + "/api" = { + proxyPass = "http://flood"; + extraConfig = '' + proxy_buffering off; + proxy_cache off; + ''; + }; + }; + extraConfig = nginxInternalOnly; + }; + }; + + systemd.services.flood = { + description = "Flood"; + after = ["network.target" "rtorrent.service"]; + path = with pkgs; [mediainfo]; + serviceConfig = { + Restart = "on-failure"; + RestartSec = 3; + + User = user; + Group = group; + + ExecStart = concatStringsSep " " [ + "${pkg}/bin/flood" + "--allowedpath=${baseDir}" + "--baseuri=/" + "--rundir=${baseDir}/flood" + "--host=127.0.0.1" + "--port=${toString port}" + "--rtsocket=${rpcSocket}" + "--ssl=false" + "--auth=none" + ]; + + RuntimeDirectory = "rtorrent"; + RuntimeDirectoryMode = 0750; + UMask = 0027; + AmbientCapabilities = [""]; + CapabilityBoundingSet = [""]; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProcSubset = "pid"; + ProtectProc = "invisible"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "~@cpu-emulation" + "~@debug" + "~@mount" + "~@obsolete" + "~@privileged" + "~@resources" + ]; + }; + wantedBy = ["multi-user.target"]; + }; + }) + ]); +} -- cgit 1.4.1