vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / torrent / rtorrent.nix
blob609b06b5e7066275fc58643585b177bbef60be17
1 { config, options, pkgs, lib, ... }:
3 with lib;
5 let
7   cfg = config.services.rtorrent;
8   opt = options.services.rtorrent;
10 in {
11   meta.maintainers = with lib.maintainers; [ thiagokokada ];
13   options.services.rtorrent = {
14     enable = mkEnableOption "rtorrent";
16     dataDir = mkOption {
17       type = types.str;
18       default = "/var/lib/rtorrent";
19       description = ''
20         The directory where rtorrent stores its data files.
21       '';
22     };
24     dataPermissions = mkOption {
25       type = types.str;
26       default = "0750";
27       example = "0755";
28       description = ''
29         Unix Permissions in octal on the rtorrent directory.
30       '';
31     };
33     downloadDir = mkOption {
34       type = types.str;
35       default = "${cfg.dataDir}/download";
36       defaultText = literalExpression ''"''${config.${opt.dataDir}}/download"'';
37       description = ''
38         Where to put downloaded files.
39       '';
40     };
42     user = mkOption {
43       type = types.str;
44       default = "rtorrent";
45       description = ''
46         User account under which rtorrent runs.
47       '';
48     };
50     group = mkOption {
51       type = types.str;
52       default = "rtorrent";
53       description = ''
54         Group under which rtorrent runs.
55       '';
56     };
58     package = mkPackageOption pkgs "rtorrent" { };
60     port = mkOption {
61       type = types.port;
62       default = 50000;
63       description = ''
64         The rtorrent port.
65       '';
66     };
68     openFirewall = mkOption {
69       type = types.bool;
70       default = false;
71       description = ''
72         Whether to open the firewall for the port in {option}`services.rtorrent.port`.
73       '';
74     };
76     rpcSocket = mkOption {
77       type = types.str;
78       readOnly = true;
79       default = "/run/rtorrent/rpc.sock";
80       description = ''
81         RPC socket path.
82       '';
83     };
85     configText = mkOption {
86       type = types.lines;
87       default = "";
88       description = ''
89         The content of {file}`rtorrent.rc`. The [modernized configuration template](https://rtorrent-docs.readthedocs.io/en/latest/cookbook.html#modernized-configuration-template) with the values specified in this module will be prepended using mkBefore. You can use mkForce to overwrite the config completely.
90       '';
91     };
92   };
94   config = mkIf cfg.enable {
96     users.groups = mkIf (cfg.group == "rtorrent") {
97       rtorrent = {};
98     };
100     users.users = mkIf (cfg.user == "rtorrent") {
101       rtorrent = {
102         group = cfg.group;
103         shell = pkgs.bashInteractive;
104         home = cfg.dataDir;
105         description = "rtorrent Daemon user";
106         isSystemUser = true;
107       };
108     };
110     networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [ cfg.port ];
112     services.rtorrent.configText = mkBefore ''
113       # Instance layout (base paths)
114       method.insert = cfg.basedir, private|const|string, (cat,"${cfg.dataDir}/")
115       method.insert = cfg.watch,   private|const|string, (cat,(cfg.basedir),"watch/")
116       method.insert = cfg.logs,    private|const|string, (cat,(cfg.basedir),"log/")
117       method.insert = cfg.logfile, private|const|string, (cat,(cfg.logs),(system.time),".log")
118       method.insert = cfg.rpcsock, private|const|string, (cat,"${cfg.rpcSocket}")
120       # Create instance directories
121       execute.throw = sh, -c, (cat, "mkdir -p ", (cfg.basedir), "/session ", (cfg.watch), " ", (cfg.logs))
123       # Listening port for incoming peer traffic (fixed; you can also randomize it)
124       network.port_range.set = ${toString cfg.port}-${toString cfg.port}
125       network.port_random.set = no
127       # Tracker-less torrent and UDP tracker support
128       # (conservative settings for 'private' trackers, change for 'public')
129       dht.mode.set = disable
130       protocol.pex.set = no
131       trackers.use_udp.set = no
133       # Peer settings
134       throttle.max_uploads.set = 100
135       throttle.max_uploads.global.set = 250
137       throttle.min_peers.normal.set = 20
138       throttle.max_peers.normal.set = 60
139       throttle.min_peers.seed.set = 30
140       throttle.max_peers.seed.set = 80
141       trackers.numwant.set = 80
143       protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
145       # Limits for file handle resources, this is optimized for
146       # an `ulimit` of 1024 (a common default). You MUST leave
147       # a ceiling of handles reserved for rTorrent's internal needs!
148       network.http.max_open.set = 50
149       network.max_open_files.set = 600
150       network.max_open_sockets.set = 3000
152       # Memory resource usage (increase if you have a large number of items loaded,
153       # and/or the available resources to spend)
154       pieces.memory.max.set = 1800M
155       network.xmlrpc.size_limit.set = 4M
157       # Basic operational settings (no need to change these)
158       session.path.set = (cat, (cfg.basedir), "session/")
159       directory.default.set = "${cfg.downloadDir}"
160       log.execute = (cat, (cfg.logs), "execute.log")
161       ##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
162       execute.nothrow = sh, -c, (cat, "echo >", (session.path), "rtorrent.pid", " ", (system.pid))
164       # Other operational settings (check & adapt)
165       encoding.add = utf8
166       system.umask.set = 0027
167       system.cwd.set = (cfg.basedir)
168       network.http.dns_cache_timeout.set = 25
169       schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
171       # Watch directories (add more as you like, but use unique schedule names)
172       #schedule2 = watch_start, 10, 10, ((load.start, (cat, (cfg.watch), "start/*.torrent")))
173       #schedule2 = watch_load, 11, 10, ((load.normal, (cat, (cfg.watch), "load/*.torrent")))
175       # Logging:
176       #   Levels = critical error warn notice info debug
177       #   Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
178       print = (cat, "Logging to ", (cfg.logfile))
179       log.open_file = "log", (cfg.logfile)
180       log.add_output = "info", "log"
181       ##log.add_output = "tracker_debug", "log"
183       # XMLRPC
184       scgi_local = (cfg.rpcsock)
185       schedule = scgi_group,0,0,"execute.nothrow=chown,\":${cfg.group}\",(cfg.rpcsock)"
186       schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",(cfg.rpcsock)"
187     '';
189     systemd = {
190       services = {
191         rtorrent = let
192           rtorrentConfigFile = pkgs.writeText "rtorrent.rc" cfg.configText;
193         in {
194           description = "rTorrent system service";
195           after = [ "network.target" ];
196           path = [ cfg.package pkgs.bash ];
197           wantedBy = [ "multi-user.target" ];
198           serviceConfig = {
199             User = cfg.user;
200             Group = cfg.group;
201             Type = "simple";
202             Restart = "on-failure";
203             WorkingDirectory = cfg.dataDir;
204             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"'';
205             ExecStart="${cfg.package}/bin/rtorrent -n -o system.daemon.set=true -o import=${rtorrentConfigFile}";
206             RuntimeDirectory = "rtorrent";
207             RuntimeDirectoryMode = 750;
209             CapabilityBoundingSet = [ "" ];
210             LockPersonality = true;
211             NoNewPrivileges = true;
212             PrivateDevices = true;
213             PrivateTmp = true;
214             ProtectClock = true;
215             ProtectControlGroups = true;
216             # If the default user is changed, there is a good chance that they
217             # want to store data in e.g.: $HOME directory
218             # Relax hardening in this case
219             ProtectHome = lib.mkIf (cfg.user == "rtorrent") true;
220             ProtectHostname = true;
221             ProtectKernelLogs = true;
222             ProtectKernelModules = true;
223             ProtectKernelTunables = true;
224             ProtectProc = "invisible";
225             ProtectSystem = "full";
226             RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
227             RestrictNamespaces = true;
228             RestrictRealtime = true;
229             RestrictSUIDSGID = true;
230             SystemCallArchitectures = "native";
231             SystemCallFilter = [ "@system-service" "~@privileged" ];
232           };
233         };
234       };
236       tmpfiles.rules = [ "d '${cfg.dataDir}' ${cfg.dataPermissions} ${cfg.user} ${cfg.group} -" ];
237     };
238   };