{ config, pkgs, ... }: let cfg = { stateDir = "/var/lib/qbittorrent"; downloadDir = "/var/videos/"; # TODO support other Media Types port = 8081; user = "qbittorrent"; }; in { age.secrets.mullvad.file = ../../../../secrets/nuc/mullvad.age; environment.etc."netns/torrent/resolv.conf".text = '' nameserver 10.64.0.1 ''; systemd.services."netns@" = { description = "%I network namespace"; before = [ "network.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "${pkgs.iproute2}/bin/ip netns add %I"; ExecStartPost = "${pkgs.iproute2}/bin/ip netns exec %I ${pkgs.iproute2}/bin/ip link set dev lo up"; ExecStop = "${pkgs.iproute2}/bin/ip netns del %I"; }; }; systemd.services."qbittorrent-portforward@" = { description = "%I network namespace port mapping"; requires = [ "netns@%i.service" ]; after = [ "netns@%i.service" ]; # wantedBy = [ "multi-user.target" ]; serviceConfig = { Restart = "on-failure"; TimeoutStopSec = 300; ExecStart = ''${pkgs.socat}/bin/socat tcp-listen:${toString cfg.port},fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec %I ${pkgs.socat}/bin/socat STDIO "tcp-connect:127.0.0.1:${toString cfg.port}"',nofork''; # ExecStart = [ # "${pkgs.socat}/bin/socat tcp-listen:%j,fork,reuseaddr" # ''${pkgs.iproute2}/bin/ip netns exec %I ${pkgs.socat}/bin/socat STDIO "tcp-connect:127.0.0.1:%j",nofork'' # ]; }; }; # scripted wireguard since systemd-networkd doesn't support netns yet networking.wireguard.interfaces."wg0-mullvad" = { # Funny Mole privateKeyFile = config.age.secrets.mullvad.path; ips = [ "10.67.237.93/32" ]; peers = [ { publicKey = "QEVIaIycN8p5twXCuZeQTEj9utozakw/MU8H6+/whls="; allowedIPs = [ "0.0.0.0/0" ]; endpoint = "138.199.34.129:51820"; } ]; interfaceNamespace = "torrent"; }; systemd.services."wireguard-wg0-mullvad" = { requires = [ "netns@torrent.service" ]; }; users.users.${cfg.user} = { group = cfg.user; home = cfg.stateDir; isSystemUser = true; extraGroups = [ "media" ]; }; users.groups.${cfg.user} = { }; systemd.services."qbittorrent" = { description = "qBittorrent Service"; bindsTo = [ "netns@torrent.service" ]; after = [ "netns@torrent.service" ]; requires = [ "qbittorrent-portforward@torrent.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { User = cfg.user; Group = cfg.user; Restart = "always"; # PrivateNetwork = true; NetworkNamespacePath = "/var/run/netns/torrent"; ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox --profile=${cfg.stateDir} --webui-port=${toString cfg.port}"; # Increase number of open file descriptors (default: 1024) # LimitNOFILE = 65536; # Avoid using nscd (leaks dns) InaccessiblePaths = [ "/run/nscd" ]; BindReadOnlyPaths = [ "/etc/netns/torrent/resolv.conf:/etc/resolv.conf" ]; # systemd-analyze --no-pager security qbittorrent.service CapabilityBoundingSet = null; PrivateDevices = true; PrivateTmp = true; PrivateUsers = true; ProtectHome = true; RestrictNamespaces = true; SystemCallFilter = "@system-service"; }; }; networking.firewall.allowedTCPPorts = [ cfg.port ]; systemd.tmpfiles.rules = [ # ensure downloads directory is created, set permissions "d ${cfg.stateDir} - ${cfg.user} ${cfg.user} - -" "d ${cfg.stateDir}/qBittorrent - ${cfg.user} ${cfg.user} - -" ]; }