summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hosts/ahmed/torrenting/default.nix156
-rw-r--r--secrets/mullvad-wg.key.age20
-rw-r--r--secrets/mullvad-wg.key.example1
-rw-r--r--secrets/secrets.nix10
4 files changed, 176 insertions, 11 deletions
diff --git a/hosts/ahmed/torrenting/default.nix b/hosts/ahmed/torrenting/default.nix
index c49baa6..9b7a9c5 100644
--- a/hosts/ahmed/torrenting/default.nix
+++ b/hosts/ahmed/torrenting/default.nix
@@ -1,14 +1,29 @@
# This module configures the my torrenting setup. It uses qBittorrent over a VPN.
-{pkgs, options, config, ...}: let
+{
+ pkgs,
+ options,
+ config,
+ ...
+}: let
downloadPath = "/srv/media/";
- interface = "tun0";
+ qbWebUiPort = 8082;
+ qbListeningPort = 52916;
+
+ wgInterface = "wg0";
+ wgPort = 51820;
+
+ qbDomain = "qbittorrent.ulovlighacker.download";
+ jellyfinDomain = "ulovlighacker.download";
+ useACME = true;
in {
# Configure the actual qBittorrent service.
services.qbittorrent = {
enable = true;
- openFirewall = true; # TEMP: reverse proxy will cover this instead
+ # We will use a reverse proxy in front of qBittorrent.
+ openFirewall = false;
+ port = qbWebUiPort;
settings = {
BitTorrent = {
@@ -16,22 +31,149 @@ in {
"Session\\DefaultSavePath" = downloadPath;
"Session\\TempPath" = "${config.services.qbittorrent.profile}/qBittorrent/temp";
"Session\\TempPathEnabled" = true;
+
+ # Instruct qBittorrent to only use VPN interface.
+ "Session\\Interface" = wgInterface;
+ "Session\\InterfaceName" = wgInterface;
+
+ "Session\\Port" = qbListeningPort;
};
- # Instruct qBittorrent to only use VPN interface.
+ Preferences = {
+ "Downloads\\SavePath" = downloadPath;
+ "General\\Locale" = "da";
+
+ # Used in conjunction with the --webui-port flag (via services.qbittorrent.port)
+ # since we'll be using a reverse proxy.
+ "WebUI\\UseUPnP" = false;
+ };
};
};
+ systemd.services.qbittorrent.unitConfig.After = ["wireguard-${wgInterface}.target"];
+
# Create the directory to which media will be downloaded.
# This is also used by Jellyfin to serve the files.
systemd.tmpfiles.rules = let
- user = options.services.qbittorrent.user.default;
- group = options.services.qbittorrent.group.default;
+ user = config.services.qbittorrent.user;
+ group = config.services.qbittorrent.group;
in [
"d ${downloadPath} 0755 ${user} ${group}"
];
# Create a connection to Mullvad's WireGuard server.
+ networking.wireguard.interfaces = {
+ ${wgInterface} = {
+ # The port to use for communication. This should also be opened in the firewall.
+ ips = ["10.70.101.133/32" "fc00:bbbb:bbbb:bb01::7:6584/128"];
+ privateKeyFile = config.age.secrets.mullvad-wg-key.path;
+ allowedIPsAsRoutes = false;
+ listenPort = wgPort;
+
+ # Create a differente networking namespace to isolate the qBittorent
+ # process. I decided not to do this because connecting the WebUI to NGINX
+ # becomes a bit tricky then. I will keep it around just in case I take up
+ # this issue again sometime later.
+ #
+ # Remember, you would also need to set NetworkNamespacePath= on
+ # qBittorrent [0]. The network namespace would when be located under
+ # /run/netns/${wgNamespace}.
+ #
+ # [0]: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#NetworkNamespacePath=
+ #
+ # interfaceNamespace = wgNamespace;
+ # preSetup = ''
+ # echo "Setting up namespace: ${wgNamespace}"
+ # ${pkgs.iproute2}/bin/ip netns add ${wgNamespace}
+ # ${pkgs.iproute2}/bin/ip -n ${wgNamespace} link set lo up
+ # '';
+ # postShutdown = ''
+ # echo "Tearing down namespace: ${wgNamespace}"
+ # ${pkgs.iproute2}/bin/ip netns del "${wgNamespace}"
+ # '';
+
+ # Since this is a client configuration, we only need a single peer: the Mullvad server.
+ peers = [
+ {
+ # The public key of the server.
+ publicKey = "/iivwlyqWqxQ0BVWmJRhcXIFdJeo0WbHQ/hZwuXaN3g=";
+
+ # The location of the server.
+ endpoint = "193.32.127.66:${toString wgPort}";
+
+ # Which destination IPs should be directed to this ip/pubkey pair. In
+ # this case, we send all packets to our only peer.
+ #
+ # NOTE: It is important the we either use a network namespace or set
+ # `allowedIPsAsRoutes = false` as otherwise we run into the loop
+ # routing problem.
+ #
+ # See: https://wiki.archlinux.org/title/WireGuard#Loop_routing
+ # See: https://cohost.org/linuwus/post/5040530-an-unexpected-soluti
+ allowedIPs = ["0.0.0.0/0" "::/0"];
+
+ # Send keepalives messages. Important to keep NAT tables alive.
+ persistentKeepalive = 25;
+ }
+ ];
+ };
+ };
+
+ # Here we load the secret file containing this clients private key. It is
+ # defined in the configuration file from Mullvad's website.
+ age.secrets.mullvad-wg-key.file = ../../../secrets/mullvad-wg.key.age;
+
+ # TODO: Use Peer as DNS server: https://arc.net/l/quote/axlprdca
- # Use NGINX as a reverse proxy for qBittorrent's WebUI.
+ services.jellyfin = {
+ enable = true;
+ # We use a reverse proxy.
+ openFirewall = false;
+ };
+
+ # Use NGINX as a reverse proxy for the various services that present web UIs.
+ services.nginx = {
+ virtualHosts.${qbDomain} = {
+ enableACME = useACME;
+ forceSSL = useACME;
+
+ locations."/" = {
+ proxyPass = "http://localhost:${toString qbWebUiPort}";
+ recommendedProxySettings = true;
+ };
+ };
+
+ virtualHosts.${jellyfinDomain} = {
+ enableACME = useACME;
+ forceSSL = useACME;
+
+ locations."/" = {
+ # This is the "static port" of the HTTP web interface.
+ #
+ # See: https://jellyfin.org/docs/general/networking/#port-bindings
+ proxyPass = "http://localhost:8096";
+ recommendedProxySettings = true;
+ };
+ };
+ };
+
+ services.cloudflare-dyndns.domains = [
+ qbDomain
+ jellyfinDomain
+ ];
+
+ networking.firewall = {
+ # Clients and peers use the same port. I'm actually not sure we need to
+ # accept incomming connections as clients participating in the wireguard
+ # protocol.
+ allowedUDPPorts = [wgPort];
+
+ # This is a weird fix. Apparently the rpfilter set up as part of
+ # nixos-rpfilter in the 'mangle' table will block WireGuard traffic.
+ # Setting this to "loose" somehow fixes that.
+ #
+ # See: https://discourse.nixos.org/t/solved-minimal-firewall-setup-for-wireguard-client/7577/2?u=linnnus
+ # See: https://github.com/NixOS/nixpkgs/issues/51258#issuecomment-448005659
+ checkReversePath = "loose";
+ };
}
diff --git a/secrets/mullvad-wg.key.age b/secrets/mullvad-wg.key.age
new file mode 100644
index 0000000..aa1b344
--- /dev/null
+++ b/secrets/mullvad-wg.key.age
@@ -0,0 +1,20 @@
+age-encryption.org/v1
+-> ssh-rsa 5MROTA
+Ey8R/TdctoyamAZWhoUaTLAfWdja0g3tWBRYexyH/1hlnTgaz9KwkXpRW9desg7/
+IA0GaBVuRlmVx1kbA2KKbgpG8c/a34AwM7F2LnYoFkPV4VDZoN54fDEDa01TRG2o
+qk96gjmCTS8l+AY2mlpUbsBzIYZLjtLQgPGOLpfKuyL9cw1IM+8Es3NHbkb5seLi
+zGnvl/qGV2usetfd+Vdb8QycPtE29UJaQw/Qh2f4nvquVZgzxwPzVhEDtqzdj5X8
+bRJxOcsIf7kK14xHM+nT1S8dfSUN3Ztfcuyy6EbNX1/opIO0KOEEnnKs/wNZQq98
+26INpaB7rnCtdZNkblpmGvo6/QCqeRerIWNPdjAnvSWogQjtgVsL6OveAI4lKuQ/
+rq1+KtvOgZChLCOVxcI888IRnMDYxlTUJ6QlJmS7KYGWITejcnbL1kbKFvE/IJ1L
+MSSJpK7FIM2YwY78NcrVmaJlc+K/VVzTms85bq5NNawGTyjWgZaHZSvlzGYMVgu5
+ze4P5jUjYCFON8Ohe20H4jvac8DlP64efH/EokTDEAaLO9Ye0Ew66tMoh4fbWEpw
+mA1UVED8X3zejAhWRdwAmvpEWejOrIizmWEGZB5iOxWBfDz3UUBXM2x3Dgf068K0
+2hWli4GMn4Ls6v7oEVe4lYIV7PSaLqdc1MU4IjTmx2g
+-> ssh-ed25519 LNzQIA awafDaYwRJjBEWAlOShvwSLTIPQDSYZsiJux4PlFOE4
+iQpj96oh9wta8wrxMewHAyNY99wEXiXOUt1bGO3paOI
+-> jHn93"D-grease P{.i5 *<*l {Y:GM
+ns4gUbJyOQnGDuqxKIAfjHhLMhXyTjT6BeUzPQasvoLvXvO3Bju3YN6FbR6LcMBz
+Km8KE+iczuv4pfhILngj/Mv8wJBuWuAyi4DjnIRs38wF
+--- QKAOhH8C9OLDT9hYvSnzmXut29OBZpSUwVpOPmqDTDs
+W@X�\�� �1(�`y(U�`������h�:{��7��1� w/�kG)[S�a��qr�o�/���&��V���C�^ ��J�3� \ No newline at end of file
diff --git a/secrets/mullvad-wg.key.example b/secrets/mullvad-wg.key.example
new file mode 100644
index 0000000..442f26a
--- /dev/null
+++ b/secrets/mullvad-wg.key.example
@@ -0,0 +1 @@
+IMF/WbG/aJSKj0LkEkU/+88/f+bO+dQnYzKhKmazz0w=
diff --git a/secrets/secrets.nix b/secrets/secrets.nix
index e50f757..34fd87b 100644
--- a/secrets/secrets.nix
+++ b/secrets/secrets.nix
@@ -2,9 +2,11 @@
# imported into the system cofniguration.
let
metadata = builtins.fromTOML (builtins.readFile ../metadata.toml);
- publicKeys = map (builtins.getAttr "sshPubKey") (builtins.attrValues metadata.hosts);
+ ahmedKey = metadata.hosts.ahmed.sshPubKey;
+ muhammedKey = metadata.hosts.muhammed.sshPubKey;
in {
- "cloudflare-ddns-token.env.age".publicKeys = publicKeys;
- "duksebot.env.age".publicKeys = publicKeys;
- "forsvarsarper.env.age".publicKeys = publicKeys;
+ "cloudflare-ddns-token.env.age".publicKeys = [muhammedKey ahmedKey];
+ "duksebot.env.age".publicKeys = [muhammedKey ahmedKey];
+ "forsvarsarper.env.age".publicKeys = [muhammedKey ahmedKey];
+ "mullvad-wg.key.age".publicKeys = [muhammedKey ahmedKey];
}