{ config, lib, pkgs, ... }: let homeDir = "/var/lib/aria2"; downloadDir = "/data/torrent"; sessionFile = "${homeDir}/session"; settings = { # locations dir = downloadDir; # logging show-console-readout = false; summary-interval = 0; # rpc enable-rpc = true; # permanent queue bt-load-saved-metadata = true; bt-save-metadata = true; force-save = true; input-file = sessionFile; save-session = sessionFile; save-session-interval = 900; # automatic saving # network async-dns-server = "193.138.218.74"; # aria2 does not respect netns resolv.conf dht-listen-port = 54971; listen-port = 54931; interface = "wg-aria"; # limits max-concurrent-downloads = 65536; max-overall-download-limit = "6M"; max-overall-upload-limit = "4M"; seed-ratio = 0; # do not stop seeding after reaching ratio }; toString' = value: if lib.isBool value then (if value then "true" else "false") else (toString value); configFile = pkgs.writeText "aria2.conf" (lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: "${k}=${toString' v}") settings)); # Without this patch, the download and upload lengths are wrong after pausing # and unpausing. I don’t patch this globally (in the overlay), since aria2 is # in the user environment and would have to be rebuilt on slow machines. aria2 = pkgs.aria2.overrideAttrs (o: o // { patches = [ (pkgs.fetchpatch { url = "https://github.com/aria2/aria2/commit/6ebdddb9f159e87923736f25900897b3602305f9.diff"; sha256 = "1j5mnafiv92xnzghh03hfdvpn9647vfdcipamnvi8hnzk3ak4mj7"; }) ]; }); mkProxyService = socket: port: { wantedBy = [ "multi-user.target" ]; after = [ "wireguard-wg-aria.service" ]; partOf = [ "wireguard-wg-aria.service" ]; serviceConfig = { PrivateNetwork = true; NetworkNamespacePath = "/run/netns/aria2"; Restart = "always"; ExecStart = "${pkgs.socat}/bin/socat UNIX-LISTEN:${socket},fork,reuseaddr,mode=660 TCP:127.0.0.1:${toString port}"; User = "aria2"; Group = "nginx"; # systemd-analyze --no-pager security aria2-rpc-proxy.service CapabilityBoundingSet = null; PrivateDevices = true; PrivateTmp = true; PrivateUsers = true; ProtectHome = true; RestrictNamespaces = true; SystemCallFilter = "@system-service"; }; }; in { users.users.aria2 = { group = "aria2"; uid = config.ids.uids.aria2; home = homeDir; }; users.groups.aria2.gid = config.ids.gids.aria2; systemd.tmpfiles.rules = [ "d '${downloadDir}' 0775 aria2 users - -" "d '${homeDir}' 0771 aria2 aria2 - -" ]; krops.secrets.wg-aria-private-key = { }; networking.wireguard.interfaces.wg-aria = { interfaceNamespace = "aria2"; preSetup = "ip netns add aria2 && ip -n aria2 link set lo up"; postShutdown = "ip netns del aria2"; privateKeyFile = config.krops.secrets.wg-aria-private-key.path; } // (import ../secrets/aria2-wireguard.nix); # potentially sensitive data systemd.services.aria2 = { description = "aria2 Service"; after = [ "wireguard-wg-aria.service" ]; requires = [ "wireguard-wg-aria.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' if [[ ! -e "${sessionFile}" ]]; then touch "${sessionFile}" fi ''; serviceConfig = { PrivateNetwork = true; NetworkNamespacePath = "/run/netns/aria2"; Restart = "always"; ExecStart = "${aria2}/bin/aria2c --conf-path=${configFile}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; User = "aria2"; Group = "aria2"; # systemd-analyze --no-pager security aria2.service CapabilityBoundingSet = null; PrivateDevices = true; PrivateTmp = true; PrivateUsers = true; ProtectHome = true; RestrictNamespaces = true; SystemCallFilter = "@system-service"; }; }; systemd.services.aria2-rpc-proxy = mkProxyService "${homeDir}/rpc.sock" 6800; services.aria2_exporter = { enable = true; listenAddress = "localhost:9578"; }; systemd.services.aria2_exporter = { after = [ "wireguard-wg-aria.service" ]; partOf = [ "wireguard-wg-aria.service" ]; serviceConfig = { PrivateNetwork = true; NetworkNamespacePath = "/run/netns/aria2"; }; }; systemd.services.aria2_exporter-proxy = mkProxyService "${homeDir}/metrics.sock" 9578; services.nginx.virtualHosts."torrent.sbruder.de" = { enableACME = true; forceSSL = true; # treated as state basicAuthFile = "${homeDir}/htpasswd"; locations = { "/" = { root = toString (import (pkgs.fetchzip { url = "https://git.sbruder.de/simon/AriaNg/archive/51c53f298091a6a6b6077febc2bc9370acb47271.tar.gz"; sha256 = "0f8j86l3fw71q1m00radlv2bg33l9jad0razyalvspzam3p3bsk1"; }) { inherit pkgs; }); }; "/jsonrpc" = { proxyPass = "http://unix:${homeDir}/rpc.sock"; proxyWebsockets = true; }; "/download/" = { alias = "${downloadDir}/"; extraConfig = '' autoindex on; ''; }; "=/metrics" = { proxyPass = "http://unix:${homeDir}/metrics.sock"; }; }; }; services.nginx.virtualHosts."aria2-metrics" = { listen = lib.singleton { addr = "127.0.0.1"; port = 9578; }; locations."=/metrics" = { proxyPass = "http://unix:${homeDir}/metrics.sock"; }; }; environment.systemPackages = with pkgs; [ aria2 mktorrent ]; }