From 712dc3728e6d30be8d661b97a30fe8f103263d58 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Tue, 12 Mar 2024 23:42:48 +0100 Subject: [PATCH] Formatted --- CODE_OF_CONDUCT.md | 128 ------ flake.nix | 9 +- nixarr/bazarr/bazarr-module/default.nix | 23 +- nixarr/bazarr/default.nix | 11 +- nixarr/ddns/default.nix | 13 +- nixarr/default.nix | 4 +- nixarr/jellyfin/default.nix | 481 ++++++++++---------- nixarr/lidarr/default.nix | 9 +- nixarr/nixarr.nix | 61 +-- nixarr/openssh/default.nix | 57 ++- nixarr/prowlarr/default.nix | 9 +- nixarr/prowlarr/prowlarr-module/default.nix | 2 +- nixarr/radarr/default.nix | 9 +- nixarr/readarr/default.nix | 9 +- nixarr/sonarr/default.nix | 9 +- nixarr/transmission/cross-seed/default.nix | 36 +- nixarr/transmission/default.nix | 165 ++++--- pkgs/cross-seed/default.nix | 8 +- 18 files changed, 503 insertions(+), 540 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 5e4fa58..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -mail@rasmuskirk.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/flake.nix b/flake.nix index 40b8979..d934035 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,11 @@ }; }; - outputs = inputs @ {flake-parts, vpnconfinement, nixpkgs, ...}: + outputs = inputs @ { + flake-parts, + vpnconfinement, + ... + }: flake-parts.lib.mkFlake { inherit inputs; } { @@ -44,7 +48,7 @@ flake = { nixosModules = rec { - nixarr = (import ./nixarr vpnconfinement); + nixarr = import ./nixarr vpnconfinement; default = nixarr; }; }; @@ -52,7 +56,6 @@ perSystem = { config, pkgs, - lib, ... }: { treefmt.config = { diff --git a/nixarr/bazarr/bazarr-module/default.nix b/nixarr/bazarr/bazarr-module/default.nix index 2ab8d40..adb72aa 100644 --- a/nixarr/bazarr/bazarr-module/default.nix +++ b/nixarr/bazarr/bazarr-module/default.nix @@ -1,14 +1,15 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.util-nixarr.services.bazarr; -in { + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.util-nixarr.services.bazarr; +in { options = { util-nixarr.services.bazarr = { - enable = mkEnableOption ("bazarr, a subtitle manager for Sonarr and Radarr"); + enable = mkEnableOption "bazarr, a subtitle manager for Sonarr and Radarr"; openFirewall = mkOption { type = types.bool; @@ -49,8 +50,8 @@ in systemd.services.bazarr = { description = "bazarr"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; serviceConfig = { Type = "simple"; @@ -68,7 +69,7 @@ in }; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPorts = [ cfg.listenPort ]; + allowedTCPPorts = [cfg.listenPort]; }; users.users = mkIf (cfg.user == "bazarr") { diff --git a/nixarr/bazarr/default.nix b/nixarr/bazarr/default.nix index 116709e..8ecf8bf 100644 --- a/nixarr/bazarr/default.nix +++ b/nixarr/bazarr/default.nix @@ -10,7 +10,7 @@ in { imports = [ ./bazarr-module ]; - + options.nixarr.bazarr = { enable = mkEnableOption "the bazarr service."; @@ -24,7 +24,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/bazarr ``` @@ -81,7 +81,12 @@ in { # Port mappings # TODO: openports vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = config.bazarr.listenPort; to = config.bazarr.listenPort; }]; + portMappings = [ + { + from = config.bazarr.listenPort; + to = config.bazarr.listenPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/ddns/default.nix b/nixarr/ddns/default.nix index 0a6235e..979ca04 100644 --- a/nixarr/ddns/default.nix +++ b/nixarr/ddns/default.nix @@ -9,7 +9,7 @@ with lib; let ddns-njalla = pkgs.writeShellApplication { name = "ddns-njalla"; - runtimeInputs = with pkgs; [ curl jq ]; + runtimeInputs = with pkgs; [curl jq]; # Thanks chatgpt... text = '' @@ -118,10 +118,13 @@ in { ''; } { - assertion = cfg.njalla.vpn.enable -> ( - cfg.njalla.vpn.keysFile != null && - config.nixarr.vpn.enable - ); + assertion = + cfg.njalla.vpn.enable + -> ( + cfg.njalla.vpn.keysFile + != null + && config.nixarr.vpn.enable + ); message = '' The nixarr.ddns.njalla.enable option requires the nixarr.vpn.enable option to be set, but it was not. diff --git a/nixarr/default.nix b/nixarr/default.nix index d1eb465..82bddf5 100644 --- a/nixarr/default.nix +++ b/nixarr/default.nix @@ -1,6 +1,4 @@ -vpnconfinement: -{ ... }: -{ +vpnconfinement: {...}: { imports = [ vpnconfinement.nixosModules.default ./nixarr.nix diff --git a/nixarr/jellyfin/default.nix b/nixarr/jellyfin/default.nix index 0a75600..27d5e48 100644 --- a/nixarr/jellyfin/default.nix +++ b/nixarr/jellyfin/default.nix @@ -2,273 +2,282 @@ config, lib, ... -}: -let +}: let cfg = config.nixarr.jellyfin; defaultPort = 8096; nixarr = config.nixarr; -in with lib; { - options.nixarr.jellyfin = { - enable = mkEnableOption "the Jellyfin service."; +in + with lib; { + options.nixarr.jellyfin = { + enable = mkEnableOption "the Jellyfin service."; - stateDir = mkOption { - type = types.path; - default = "${nixarr.stateDir}/jellyfin"; - defaultText = literalExpression ''"''${nixarr.stateDir}/jellyfin"''; - example = "/nixarr/.state/jellyfin"; - description = '' - The location of the state directory for the Jellyfin service. + stateDir = mkOption { + type = types.path; + default = "${nixarr.stateDir}/jellyfin"; + defaultText = literalExpression ''"''${nixarr.stateDir}/jellyfin"''; + example = "/nixarr/.state/jellyfin"; + description = '' + The location of the state directory for the Jellyfin service. - **Warning:** Setting this to any path, where the subpath is not - owned by root, will fail! For example: - - ```nix - stateDir = /home/user/nixarr/.state/jellyfin - ``` + **Warning:** Setting this to any path, where the subpath is not + owned by root, will fail! For example: - Is not supported, because `/home/user` is owned by `user`. - ''; - }; + ```nix + stateDir = /home/user/nixarr/.state/jellyfin + ``` - openFirewall = mkOption { - type = types.bool; - defaultText = literalExpression ''!cfg.vpn.enable''; - default = !cfg.vpn.enable; - example = true; - description = "Open firewall for Jellyfin"; - }; - - vpn.enable = mkOption { - type = types.bool; - default = false; - example = true; - description = '' - **Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable) - **Conflicting options:** [`nixarr.jellyfin.expose.https.enable`](#nixarr.jellyfin.expose.https.enable) - - Route Jellyfin traffic through the VPN. - ''; - }; - - expose = { - vpn = { - enable = mkOption { - type = types.bool; - default = false; - example = true; - description = '' - **Required options:** - - - [`nixarr.jellyfin.vpn.enable`](#nixarr.jellyfin.vpn.enable) - - [`nixarr.jellyfin.expose.vpn.port`](#nixarr.jellyfin.expose.vpn.port) - - [`nixarr.jellyfin.expose.vpn.accessibleFrom`](#nixarr.jellyfin.expose.vpn.accessiblefrom) - - Expose the Jellyfin web service to the internet, allowing anyone to - access it. - - **Warning:** Do _not_ enable this without setting up Jellyfin - authentication through localhost first! - ''; - }; - - port = mkOption { - type = with types; nullOr port; - default = null; - example = 12345; - description = '' - The port to access jellyfin on. Get this port from your VPN - provider. - ''; - }; - - accessibleFrom = mkOption { - type = with types; nullOr str; - default = null; - example = "jellyfin.airvpn.org"; - description = '' - The IP or domain that Jellyfin should be able to be accessed from. - ''; - }; + Is not supported, because `/home/user` is owned by `user`. + ''; }; - https = { - enable = mkOption { - type = types.bool; - default = false; - example = true; - description = '' - **Required options:** - - - [`nixarr.jellyfin.expose.https.acmeMail`](#nixarr.jellyfin.expose.https.acmemail) - - [`nixarr.jellyfin.expose.https.domainName`](#nixarr.jellyfin.expose.https.domainname) + openFirewall = mkOption { + type = types.bool; + defaultText = literalExpression ''!cfg.vpn.enable''; + default = !cfg.vpn.enable; + example = true; + description = "Open firewall for Jellyfin"; + }; - **Conflicting options:** [`nixarr.jellyfin.vpn.enable`](#nixarr.jellyfin.vpn.enable) + vpn.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + **Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable) + **Conflicting options:** [`nixarr.jellyfin.expose.https.enable`](#nixarr.jellyfin.expose.https.enable) - Expose the Jellyfin web service to the internet with https support, - allowing anyone to access it. + Route Jellyfin traffic through the VPN. + ''; + }; - **Warning:** Do _not_ enable this without setting up Jellyfin - authentication through localhost first! - ''; + expose = { + vpn = { + enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + **Required options:** + + - [`nixarr.jellyfin.vpn.enable`](#nixarr.jellyfin.vpn.enable) + - [`nixarr.jellyfin.expose.vpn.port`](#nixarr.jellyfin.expose.vpn.port) + - [`nixarr.jellyfin.expose.vpn.accessibleFrom`](#nixarr.jellyfin.expose.vpn.accessiblefrom) + + Expose the Jellyfin web service to the internet, allowing anyone to + access it. + + **Warning:** Do _not_ enable this without setting up Jellyfin + authentication through localhost first! + ''; + }; + + port = mkOption { + type = with types; nullOr port; + default = null; + example = 12345; + description = '' + The port to access jellyfin on. Get this port from your VPN + provider. + ''; + }; + + accessibleFrom = mkOption { + type = with types; nullOr str; + default = null; + example = "jellyfin.airvpn.org"; + description = '' + The IP or domain that Jellyfin should be able to be accessed from. + ''; + }; }; - upnp.enable = mkEnableOption "UPNP to try to open ports 80 and 443 on your router."; + https = { + enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + **Required options:** - domainName = mkOption { - type = types.nullOr types.str; - default = null; - example = "jellyfin.example.com"; - description = "The domain name to host Jellyfin on."; - }; + - [`nixarr.jellyfin.expose.https.acmeMail`](#nixarr.jellyfin.expose.https.acmemail) + - [`nixarr.jellyfin.expose.https.domainName`](#nixarr.jellyfin.expose.https.domainname) - acmeMail = mkOption { - type = types.nullOr types.str; - default = null; - example = "mail@example.com"; - description = "The ACME mail required for the letsencrypt bot."; + **Conflicting options:** [`nixarr.jellyfin.vpn.enable`](#nixarr.jellyfin.vpn.enable) + + Expose the Jellyfin web service to the internet with https support, + allowing anyone to access it. + + **Warning:** Do _not_ enable this without setting up Jellyfin + authentication through localhost first! + ''; + }; + + upnp.enable = mkEnableOption "UPNP to try to open ports 80 and 443 on your router."; + + domainName = mkOption { + type = types.nullOr types.str; + default = null; + example = "jellyfin.example.com"; + description = "The domain name to host Jellyfin on."; + }; + + acmeMail = mkOption { + type = types.nullOr types.str; + default = null; + example = "mail@example.com"; + description = "The ACME mail required for the letsencrypt bot."; + }; }; }; }; - }; - config = - mkIf cfg.enable - { - assertions = [ - { - assertion = cfg.vpn.enable -> nixarr.vpn.enable; - message = '' - The nixarr.jellyfin.vpn.enable option requires the - nixarr.vpn.enable option to be set, but it was not. - ''; - } - { - assertion = !(cfg.vpn.enable && cfg.expose.https.enable); - message = '' - The nixarr.jellyfin.vpn.enable option conflicts with the - nixarr.jellyfin.expose.https.enable option. You cannot set both. - ''; - } - { - assertion = cfg.expose.https.enable -> ( - (cfg.expose.https.domainName != null) && - (cfg.expose.https.acmeMail != null) - ); - message = '' - The nixarr.jellyfin.expose.https.enable option requires the - following options to be set, but one of them were not: + config = + mkIf cfg.enable + { + assertions = [ + { + assertion = cfg.vpn.enable -> nixarr.vpn.enable; + message = '' + The nixarr.jellyfin.vpn.enable option requires the + nixarr.vpn.enable option to be set, but it was not. + ''; + } + { + assertion = !(cfg.vpn.enable && cfg.expose.https.enable); + message = '' + The nixarr.jellyfin.vpn.enable option conflicts with the + nixarr.jellyfin.expose.https.enable option. You cannot set both. + ''; + } + { + assertion = + cfg.expose.https.enable + -> ( + (cfg.expose.https.domainName != null) + && (cfg.expose.https.acmeMail != null) + ); + message = '' + The nixarr.jellyfin.expose.https.enable option requires the + following options to be set, but one of them were not: - - nixarr.jellyfin.expose.domainName - - nixarr.jellyfin.expose.acmeMail - ''; - } - { - assertion = cfg.expose.vpn.enable -> ( - cfg.vpn.enable && - (cfg.expose.vpn.port != null) && - (cfg.expose.vpn.accessibleFrom != null) - ); - message = '' - The nixarr.jellyfin.expose.vpn.enable option requires the - following options to be set, but one of them were not: + - nixarr.jellyfin.expose.domainName + - nixarr.jellyfin.expose.acmeMail + ''; + } + { + assertion = + cfg.expose.vpn.enable + -> ( + cfg.vpn.enable + && (cfg.expose.vpn.port != null) + && (cfg.expose.vpn.accessibleFrom != null) + ); + message = '' + The nixarr.jellyfin.expose.vpn.enable option requires the + following options to be set, but one of them were not: - - nixarr.jellyfin.vpn.enable - - nixarr.jellyfin.expose.vpn.port - - nixarr.jellyfin.expose.vpn.accessibleFrom - ''; - } - ]; - - systemd.tmpfiles.rules = [ - "d '${cfg.stateDir}' 0700 streamer root - -" - ]; + - nixarr.jellyfin.vpn.enable + - nixarr.jellyfin.expose.vpn.port + - nixarr.jellyfin.expose.vpn.accessibleFrom + ''; + } + ]; - services.jellyfin = { - enable = cfg.enable; - user = "streamer"; - group = "streamer"; - openFirewall = cfg.openFirewall; - logDir = "${cfg.stateDir}/log"; - cacheDir = "${cfg.stateDir}/cache"; - dataDir = "${cfg.stateDir}/data"; - configDir = "${cfg.stateDir}/config"; - }; + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' 0700 streamer root - -" + ]; - networking.firewall = mkIf cfg.expose.https.enable { - allowedTCPPorts = [80 443]; - }; + services.jellyfin = { + enable = cfg.enable; + user = "streamer"; + group = "streamer"; + openFirewall = cfg.openFirewall; + logDir = "${cfg.stateDir}/log"; + cacheDir = "${cfg.stateDir}/cache"; + dataDir = "${cfg.stateDir}/data"; + configDir = "${cfg.stateDir}/config"; + }; - util-nixarr.upnp = mkIf cfg.expose.https.upnp.enable { - enable = true; - openTcpPorts = [80 443]; - }; + networking.firewall = mkIf cfg.expose.https.enable { + allowedTCPPorts = [80 443]; + }; - services.nginx = mkMerge [ - (mkIf (cfg.expose.https.enable || cfg.vpn.enable) { + util-nixarr.upnp = mkIf cfg.expose.https.upnp.enable { enable = true; + openTcpPorts = [80 443]; + }; - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - }) - (mkIf cfg.expose.https.enable { - virtualHosts."${builtins.replaceStrings ["\n"] [""] cfg.expose.https.domainName}" = { - enableACME = true; - forceSSL = true; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://127.0.0.1:${builtins.toString defaultPort}"; + services.nginx = mkMerge [ + (mkIf (cfg.expose.https.enable || cfg.vpn.enable) { + enable = true; + + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + }) + (mkIf cfg.expose.https.enable { + virtualHosts."${builtins.replaceStrings ["\n"] [""] cfg.expose.https.domainName}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://127.0.0.1:${builtins.toString defaultPort}"; + }; }; - }; - }) - (mkIf cfg.vpn.enable { - virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = mkIf cfg.vpn.enable { - listen = [ - { - addr = "0.0.0.0"; - port = defaultPort; - } - ]; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }) + (mkIf cfg.vpn.enable { + virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = mkIf cfg.vpn.enable { + listen = [ + { + addr = "0.0.0.0"; + port = defaultPort; + } + ]; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }; }; - }; - }) - (mkIf cfg.expose.vpn.enable { - virtualHosts."${builtins.toString cfg.expose.vpn.accessibleFrom}:${builtins.toString cfg.expose.vpn.port}" = { - enableACME = true; - forceSSL = true; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }) + (mkIf cfg.expose.vpn.enable { + virtualHosts."${builtins.toString cfg.expose.vpn.accessibleFrom}:${builtins.toString cfg.expose.vpn.port}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + recommendedProxySettings = true; + proxyWebsockets = true; + proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; + }; }; + }) + ]; + + security.acme = mkIf cfg.expose.https.enable { + acceptTerms = true; + defaults.email = cfg.expose.https.acmeMail; + }; + + # Enable and specify VPN namespace to confine service in. + systemd.services.jellyfin.vpnconfinement = mkIf cfg.vpn.enable { + enable = true; + vpnnamespace = "wg"; + }; + + # Port mappings + # TODO: openports if expose.vpn + vpnnamespaces.wg = mkIf cfg.vpn.enable { + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; + openVPNPorts = optional cfg.expose.vpn.enable { + port = cfg.expose.vpn.port; + protocol = "tcp"; }; - }) - ]; - - security.acme = mkIf cfg.expose.https.enable { - acceptTerms = true; - defaults.email = cfg.expose.https.acmeMail; - }; - - # Enable and specify VPN namespace to confine service in. - systemd.services.jellyfin.vpnconfinement = mkIf cfg.vpn.enable { - enable = true; - vpnnamespace = "wg"; - }; - - # Port mappings - # TODO: openports if expose.vpn - vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; - openVPNPorts = optional cfg.expose.vpn.enable { - port = cfg.expose.vpn.port; - protocol = "tcp"; }; }; - }; -} + } diff --git a/nixarr/lidarr/default.nix b/nixarr/lidarr/default.nix index ae5fd34..547e359 100644 --- a/nixarr/lidarr/default.nix +++ b/nixarr/lidarr/default.nix @@ -21,7 +21,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/lidarr ``` @@ -78,7 +78,12 @@ in { # Port mappings # TODO: openports vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/nixarr.nix b/nixarr/nixarr.nix index 265c42a..d91df34 100644 --- a/nixarr/nixarr.nix +++ b/nixarr/nixarr.nix @@ -13,7 +13,6 @@ with lib; let find "$1" -type f -links 1 -exec du -h {} + | sort -h ''; }; - in { imports = [ ./jellyfin @@ -46,7 +45,7 @@ in { that they manage is located by default in `/data/.state/nixarr/*` - **Optional Automatic Port Forwarding:** This module has a UPNP support that lets services request ports from your router automatically, if you enable it. - + It is possible, _but not recommended_, to run the "*Arrs" behind a VPN, because it can cause rate limiting issues. Generally, you should use VPN on transmission and maybe jellyfin, depending on your setup. @@ -69,7 +68,7 @@ in { mediaUsers = mkOption { type = with types; listOf str; default = []; - example = [ "user" ]; + example = ["user"]; description = '' Extra users to add to the media group. ''; @@ -84,7 +83,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix mediaDir = /home/user/nixarr ``` @@ -102,7 +101,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state ``` @@ -225,9 +224,9 @@ in { # TODO: wtf to do about openports vpnnamespaces.wg = mkIf cfg.vpn.enable { enable = true; - openVPNPorts = optional cfg.vpn.vpnTestService.enable { - port = cfg.vpn.vpnTestService.port; - protocol = "tcp"; + openVPNPorts = optional cfg.vpn.vpnTestService.enable { + port = cfg.vpn.vpnTestService.port; + protocol = "tcp"; }; accessibleFrom = [ "192.168.1.0/24" @@ -251,30 +250,36 @@ in { runtimeInputs = with pkgs; [util-linux unixtools.ping coreutils curl bash libressl netcat-gnu openresolv dig]; - text = '' - cd "$(mktemp -d)" + text = + '' + cd "$(mktemp -d)" - # Print resolv.conf - echo "/etc/resolv.conf contains:" - cat /etc/resolv.conf + # Print resolv.conf + echo "/etc/resolv.conf contains:" + cat /etc/resolv.conf - # Query resolvconf - echo "resolvconf output:" - resolvconf -l - echo "" + # Query resolvconf + echo "resolvconf output:" + resolvconf -l + echo "" - # Get ip - echo "Getting IP:" - curl -s ipinfo.io + # Get ip + echo "Getting IP:" + curl -s ipinfo.io - echo -ne "DNS leak test:" - curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh - chmod +x dnsleaktest.sh - ./dnsleaktest.sh - '' + (if cfg.vpn.vpnTestService.port != null then '' - echo "starting netcat on port ${builtins.toString cfg.vpn.vpnTestService.port}:" - nc -vnlp ${builtins.toString cfg.vpn.vpnTestService.port} - '' else ""); + echo -ne "DNS leak test:" + curl -s https://raw.githubusercontent.com/macvk/dnsleaktest/b03ab54d574adbe322ca48cbcb0523be720ad38d/dnsleaktest.sh -o dnsleaktest.sh + chmod +x dnsleaktest.sh + ./dnsleaktest.sh + '' + + ( + if cfg.vpn.vpnTestService.port != null + then '' + echo "starting netcat on port ${builtins.toString cfg.vpn.vpnTestService.port}:" + nc -vnlp ${builtins.toString cfg.vpn.vpnTestService.port} + '' + else "" + ); }; in "${vpn-test}/bin/vpn-test"; diff --git a/nixarr/openssh/default.nix b/nixarr/openssh/default.nix index bdcac94..66ce8d0 100644 --- a/nixarr/openssh/default.nix +++ b/nixarr/openssh/default.nix @@ -15,7 +15,7 @@ in { **Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable) Run the openssh service through a vpn, exposing it to the internet. - + **Warning:** This lets anyone on the internet connect through SSH, make sure the SSH configuration is secure! Disallowing password authentication and only allowing SSH-keys is considered secure. @@ -53,28 +53,31 @@ in { } ]; - warnings = if config.services.openssh.enable then [ - '' - nixarr.openssh.expose.vpn.enable is set, but openssh is not enabled - on your system, so the openssh server is not running. This is probably - not what you wanted. You can add the following lines to enable it: + warnings = + if config.services.openssh.enable + then [ + '' + nixarr.openssh.expose.vpn.enable is set, but openssh is not enabled + on your system, so the openssh server is not running. This is probably + not what you wanted. You can add the following lines to enable it: - services.openssh = { - enable = true; - settings.PasswordAuthentication = false; - # Get this port from your VPN provider - ports [ 12345 ]; - }; + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + # Get this port from your VPN provider + ports [ 12345 ]; + }; - users.extraUsers.username.openssh.authorizedKeys.keyFiles = [ - ./path/to/public/key/machine.pub - ]; + users.extraUsers.username.openssh.authorizedKeys.keyFiles = [ + ./path/to/public/key/machine.pub + ]; - Then replace username with your username and the keyFiles path - to a ssh public key file from the machine that you want to have - access. Don't use password authentication as it is insecure! - '' - ] else []; + Then replace username with your username and the keyFiles path + to a ssh public key file from the machine that you want to have + access. Don't use password authentication as it is insecure! + '' + ] + else []; # Enable and specify VPN namespace to confine service in. systemd.services.openssh.vpnconfinement = { @@ -84,8 +87,18 @@ in { # Port mappings vpnnamespaces.wg = { - portMappings = [{ From = defaultPort; To = defaultPort; }]; - openVPNPorts = map (x: { port = x; protocol = "both"; }) services.openssh.ports; + portMappings = [ + { + From = defaultPort; + To = defaultPort; + } + ]; + openVPNPorts = + map (x: { + port = x; + protocol = "both"; + }) + services.openssh.ports; }; }; } diff --git a/nixarr/prowlarr/default.nix b/nixarr/prowlarr/default.nix index 54d1f10..884c644 100644 --- a/nixarr/prowlarr/default.nix +++ b/nixarr/prowlarr/default.nix @@ -26,7 +26,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/prowlarr ``` @@ -80,7 +80,12 @@ in { # Port mappings vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/prowlarr/prowlarr-module/default.nix b/nixarr/prowlarr/prowlarr-module/default.nix index 48c919f..d327a94 100644 --- a/nixarr/prowlarr/prowlarr-module/default.nix +++ b/nixarr/prowlarr/prowlarr-module/default.nix @@ -71,7 +71,7 @@ in { }; users.groups = mkIf (cfg.group == "prowlarr") { - prowlarr = { }; + prowlarr = {}; }; }; } diff --git a/nixarr/radarr/default.nix b/nixarr/radarr/default.nix index a55027e..944c067 100644 --- a/nixarr/radarr/default.nix +++ b/nixarr/radarr/default.nix @@ -22,7 +22,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/radarr ``` @@ -78,7 +78,12 @@ in { # Port mappings vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/readarr/default.nix b/nixarr/readarr/default.nix index 714b357..c8eb236 100644 --- a/nixarr/readarr/default.nix +++ b/nixarr/readarr/default.nix @@ -20,7 +20,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/readarr ``` @@ -76,7 +76,12 @@ in { # Port mappings vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/sonarr/default.nix b/nixarr/sonarr/default.nix index 8890bad..9dd35c9 100644 --- a/nixarr/sonarr/default.nix +++ b/nixarr/sonarr/default.nix @@ -26,7 +26,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/sonarr ``` @@ -82,7 +82,12 @@ in { # Port mappings vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = defaultPort; to = defaultPort; }]; + portMappings = [ + { + from = defaultPort; + to = defaultPort; + } + ]; }; services.nginx = mkIf cfg.vpn.enable { diff --git a/nixarr/transmission/cross-seed/default.nix b/nixarr/transmission/cross-seed/default.nix index 5e32c4d..172b92b 100644 --- a/nixarr/transmission/cross-seed/default.nix +++ b/nixarr/transmission/cross-seed/default.nix @@ -82,7 +82,7 @@ in { }; config = mkIf cfg.enable { - assertions = [ + assertions = [ { assertion = cfg.enable -> cfg.settings.outputDir != null; message = '' @@ -91,14 +91,16 @@ in { } ]; - systemd.tmpfiles.rules = [ - "L+ '${cfg.dataDir}'/config.js - - - - ${configJs}" - "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" - ] ++ ( - if cfg.settings.outputDir != null then - [ "d '${cfg.settings.outputDir}' 0755 ${cfg.user} ${cfg.group} - -" ] - else [] - ); + systemd.tmpfiles.rules = + [ + "L+ '${cfg.dataDir}'/config.js - - - - ${configJs}" + "d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -" + ] + ++ ( + if cfg.settings.outputDir != null + then ["d '${cfg.settings.outputDir}' 0755 ${cfg.user} ${cfg.group} - -"] + else [] + ); systemd.services.cross-seed = { description = "cross-seed"; @@ -109,11 +111,15 @@ in { serviceConfig = { # Run as root in case that the cfg.credentialsFile is not readable by cross-seed - ExecStartPre = [("+" + pkgs.writeShellScript "transmission-prestart" '' - ${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' | - install -D -m 600 -o '${cfg.user}' /dev/stdin '${cfg.dataDir}/config.json' - '' - )]; + ExecStartPre = [ + ( + "+" + + pkgs.writeShellScript "transmission-prestart" '' + ${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' | + install -D -m 600 -o '${cfg.user}' /dev/stdin '${cfg.dataDir}/config.json' + '' + ) + ]; Type = "simple"; User = cfg.user; Group = cfg.group; @@ -130,7 +136,7 @@ in { }; users.groups = mkIf (cfg.group == "cross-seed") { - cross-seed = { }; + cross-seed = {}; }; }; } diff --git a/nixarr/transmission/default.nix b/nixarr/transmission/default.nix index bfea026..48c9768 100644 --- a/nixarr/transmission/default.nix +++ b/nixarr/transmission/default.nix @@ -9,57 +9,60 @@ with lib; let nixarr = config.nixarr; cfg-cross-seed = config.nixarr.transmission.privateTrackers.cross-seed; downloadDir = "${nixarr.mediaDir}/torrents"; - transmissionCrossSeedScript = with builtins; pkgs.writeShellApplication { - name = "transmission-cross-seed-script"; + transmissionCrossSeedScript = with builtins; + pkgs.writeShellApplication { + name = "transmission-cross-seed-script"; - runtimeInputs = with pkgs; [ curl ]; + runtimeInputs = with pkgs; [curl]; - text = '' - PROWLARR_API_KEY=$(cat prowlarr-api-key) - curl -XPOST http://localhost:2468/api/webhook?apikey="$PROWLARR_API_KEY" --data-urlencode "infoHash=$TR_TORRENT_HASH" - ''; - }; - importProwlarrApi = with builtins; pkgs.writeShellApplication { - name = "import-prowlarr-api"; - - runtimeInputs = with pkgs; [ yq ]; - - text = '' - touch ${cfg.stateDir}/prowlarr-api-key - chmod 400 ${cfg.stateDir}/prowlarr-api-key - chown torrenter ${cfg.stateDir}/prowlarr-api-key - xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml" > "${cfg.stateDir}/prowlarr-api-key" - ''; - }; - mkCrossSeedCredentials = with builtins; pkgs.writeShellApplication { - name = "mk-cross-seed-credentials"; - - runtimeInputs = with pkgs; [ jq yq ]; - - text = - "INDEX_LINKS=(" - + (strings.concatMapStringsSep " " toString cfg.privateTrackers.cross-seed.indexIds) - + ")" - + "\n" - + '' - TMP_JSON=$(mktemp) - CRED_FILE="/run/secrets/cross-seed/credentialsFile.json" - PROWLARR_API_KEY=$(xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml") - # shellcheck disable=SC2034 - CRED_DIR=$(dirname "$CRED_FILE") - - mkdir -p "$CRED_DIR" - echo '{}' > "$CRED_FILE" - chmod 400 "$CRED_FILE" - chown "${config.util-nixarr.services.cross-seed.user}" "$CRED_FILE" - - for i in "''${INDEX_LINKS[@]}" - do - LINK="http://localhost:9696/$i/api?apikey=$PROWLARR_API_KEY" - jq ".torznab += [\"$LINK\"]" "$CRED_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$CRED_FILE" - done + text = '' + PROWLARR_API_KEY=$(cat prowlarr-api-key) + curl -XPOST http://localhost:2468/api/webhook?apikey="$PROWLARR_API_KEY" --data-urlencode "infoHash=$TR_TORRENT_HASH" ''; - }; + }; + importProwlarrApi = with builtins; + pkgs.writeShellApplication { + name = "import-prowlarr-api"; + + runtimeInputs = with pkgs; [yq]; + + text = '' + touch ${cfg.stateDir}/prowlarr-api-key + chmod 400 ${cfg.stateDir}/prowlarr-api-key + chown torrenter ${cfg.stateDir}/prowlarr-api-key + xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml" > "${cfg.stateDir}/prowlarr-api-key" + ''; + }; + mkCrossSeedCredentials = with builtins; + pkgs.writeShellApplication { + name = "mk-cross-seed-credentials"; + + runtimeInputs = with pkgs; [jq yq]; + + text = + "INDEX_LINKS=(" + + (strings.concatMapStringsSep " " toString cfg.privateTrackers.cross-seed.indexIds) + + ")" + + "\n" + + '' + TMP_JSON=$(mktemp) + CRED_FILE="/run/secrets/cross-seed/credentialsFile.json" + PROWLARR_API_KEY=$(xq -r '.Config.ApiKey' "${nixarr.prowlarr.stateDir}/config.xml") + # shellcheck disable=SC2034 + CRED_DIR=$(dirname "$CRED_FILE") + + mkdir -p "$CRED_DIR" + echo '{}' > "$CRED_FILE" + chmod 400 "$CRED_FILE" + chown "${config.util-nixarr.services.cross-seed.user}" "$CRED_FILE" + + for i in "''${INDEX_LINKS[@]}" + do + LINK="http://localhost:9696/$i/api?apikey=$PROWLARR_API_KEY" + jq ".torznab += [\"$LINK\"]" "$CRED_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$CRED_FILE" + done + ''; + }; in { options.nixarr.transmission = { enable = mkEnableOption "the Transmission service."; @@ -74,7 +77,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/transmission ``` @@ -142,7 +145,7 @@ in { **Warning:** Setting this to any path, where the subpath is not owned by root, will fail! For example: - + ```nix stateDir = /home/user/nixarr/.state/cross-seed ``` @@ -154,7 +157,7 @@ in { indexIds = mkOption { type = with types; listOf int; default = []; - example = [ 1 3 7 ]; + example = [1 3 7]; description = '' List of indexer-ids, from prowlarr. These are from the RSS links for the indexers, located by the "radio" or "RSS" logo on the @@ -259,31 +262,37 @@ in { enable = true; dataDir = cfg-cross-seed.stateDir; group = "torrenter"; - settings = { - torrentDir = "${nixarr.mediaDir}/torrents"; - outputDir = "${nixarr.mediaDir}/torrents/.cross-seed"; - transmissionRpcUrl = "http://localhost:${builtins.toString cfg.uiPort}/transmission/rpc"; - rssCadence = "20 minutes"; + settings = + { + torrentDir = "${nixarr.mediaDir}/torrents"; + outputDir = "${nixarr.mediaDir}/torrents/.cross-seed"; + transmissionRpcUrl = "http://localhost:${builtins.toString cfg.uiPort}/transmission/rpc"; + rssCadence = "20 minutes"; - action = "inject"; + action = "inject"; - # Enable infrequent periodic searches - searchCadence = "1 week"; - excludeRecentSearch = "1 year"; - excludeOlder = "1 year"; - } // cfg-cross-seed.extraSettings; + # Enable infrequent periodic searches + searchCadence = "1 week"; + excludeRecentSearch = "1 year"; + excludeOlder = "1 year"; + } + // cfg-cross-seed.extraSettings; }; # Run as root in case that the cfg.credentialsFile is not readable by cross-seed systemd.services.cross-seed.serviceConfig = mkIf cfg-cross-seed.enable { - ExecStartPre = mkBefore [( + ExecStartPre = mkBefore [ + ( "+" + "${mkCrossSeedCredentials}/bin/mk-cross-seed-credentials" - )]; + ) + ]; }; systemd.services.transmission.serviceConfig = mkIf cfg-cross-seed.enable { - ExecStartPre = mkBefore [( + ExecStartPre = mkBefore [ + ( "+" + "${importProwlarrApi}/bin/import-prowlarr-api" - )]; + ) + ]; }; services.transmission = { enable = true; @@ -305,7 +314,10 @@ in { watch-dir-enabled = true; watch-dir = "${downloadDir}/.watch"; - rpc-bind-address = if cfg.vpn.enable then "192.168.15.1" else "127.0.0.1"; + rpc-bind-address = + if cfg.vpn.enable + then "192.168.15.1" + else "127.0.0.1"; rpc-port = cfg.uiPort; # TODO: fix this for ssh tunneling... rpc-whitelist-enabled = true; @@ -326,9 +338,10 @@ in { anti-brute-force-threshold = 10; script-torrent-done-enabled = cfg-cross-seed.enable; - script-torrent-done-filename = if cfg-cross-seed.enable then - "${transmissionCrossSeedScript}/bin/transmission-cross-seed-script" - else null; + script-torrent-done-filename = + if cfg-cross-seed.enable + then "${transmissionCrossSeedScript}/bin/transmission-cross-seed-script" + else null; message-level = if cfg.messageLevel == "none" @@ -358,9 +371,17 @@ in { # Port mappings vpnnamespaces.wg = mkIf cfg.vpn.enable { - portMappings = [{ from = cfg.uiPort; to = cfg.uiPort; }]; + portMappings = [ + { + from = cfg.uiPort; + to = cfg.uiPort; + } + ]; openVPNPorts = [ - { port = cfg.peerPort; protocol = "both"; } + { + port = cfg.peerPort; + protocol = "both"; + } ]; }; diff --git a/pkgs/cross-seed/default.nix b/pkgs/cross-seed/default.nix index 2a69754..8e4def2 100644 --- a/pkgs/cross-seed/default.nix +++ b/pkgs/cross-seed/default.nix @@ -1,5 +1,8 @@ -{ lib, buildNpmPackage, fetchFromGitHub }: - +{ + lib, + buildNpmPackage, + fetchFromGitHub, +}: buildNpmPackage rec { pname = "cross-seed"; version = "5.9.2"; @@ -17,6 +20,5 @@ buildNpmPackage rec { description = "cross-seed is an app designed to help you download torrents that you can cross seed based on your existing torrents"; homepage = "https://www.cross-seed.org"; license = licenses.asl20; - maintainers = with maintainers; [ rasmus-kirk ]; }; }