From 77b3d974c545e08f3fecd45680ee698877ccf2a3 Mon Sep 17 00:00:00 2001 From: Rouven Seifert Date: Sat, 4 May 2024 13:53:26 +0200 Subject: [PATCH] nuc: configure torrenting in a dedicated network namespace --- hosts/nuc/default.nix | 1 + hosts/nuc/modules/torrent/default.nix | 112 ++++++++++++++++++++++++++ secrets.nix | 1 + secrets/nuc/mullvad.age | 7 ++ 4 files changed, 121 insertions(+) create mode 100644 hosts/nuc/modules/torrent/default.nix create mode 100644 secrets/nuc/mullvad.age diff --git a/hosts/nuc/default.nix b/hosts/nuc/default.nix index 2f93c8e..768080d 100644 --- a/hosts/nuc/default.nix +++ b/hosts/nuc/default.nix @@ -13,6 +13,7 @@ ./modules/matrix ./modules/mautrix-telegram ./modules/seafile + ./modules/torrent ./modules/vaultwarden ./modules/nginx ]; diff --git a/hosts/nuc/modules/torrent/default.nix b/hosts/nuc/modules/torrent/default.nix new file mode 100644 index 0000000..9c8d83b --- /dev/null +++ b/hosts/nuc/modules/torrent/default.nix @@ -0,0 +1,112 @@ +{ 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.iproute}/bin/ip netns add %I"; + ExecStartPost = "${pkgs.iproute2}/bin/ip netns exec %I ${pkgs.iproute2}/bin/ip link set dev lo up"; + ExecStop = "${pkgs.iproute}/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/mullvad/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} - -" + ]; +} + diff --git a/secrets.nix b/secrets.nix index f73f67b..3c5a63c 100644 --- a/secrets.nix +++ b/secrets.nix @@ -20,6 +20,7 @@ in "secrets/nuc/matrix/sync.age".publicKeys = [ rouven nuc ]; "secrets/nuc/mautrix-telegram/env.age".publicKeys = [ rouven nuc ]; "secrets/nuc/vaultwarden.age".publicKeys = [ rouven nuc ]; + "secrets/nuc/mullvad.age".publicKeys = [ rouven nuc ]; "secrets/nuc/keycloak/db.age".publicKeys = [ rouven nuc ]; "secrets/nuc/cache.age".publicKeys = [ rouven nuc ]; "secrets/nuc/borg/passphrase.age".publicKeys = [ rouven nuc ]; diff --git a/secrets/nuc/mullvad.age b/secrets/nuc/mullvad.age new file mode 100644 index 0000000..06d3b5a --- /dev/null +++ b/secrets/nuc/mullvad.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 uWbAHQ mWnXWfwlxVxg+ik61NHqSv9onxa2Lp6GBALkEbVk/TM +uKDbdYg0qp/3YmKTVAkAaywKo0hWhAK/wDKqoGmaS/U +-> ssh-ed25519 2TRdXg TXVYT8yp/mHgmm1um73RRwurbfNLlDmfHw6YTsW08UI +D7x46t9W0JB2ua+mZshaUQm+fWbl8jvuDTYAibiIhIY +--- Q8kMARj3UQyshncV40iuORct23Ajg25pUUIcE8x261w +WX.‡ççZèb D¥´Ä!¡¤ZH˜ÿ±¨ÿè¬â·ŠX¶b½ƒM¤ÅP¿vGÏÛR½PRÑB÷½v¸,ÐQžSå_‡W^w \ No newline at end of file