From 5cf4b3e22b612a1693b0df6d32317a5c1089593f Mon Sep 17 00:00:00 2001 From: Simon Elsbrock Date: Sun, 16 Mar 2025 20:54:18 +0100 Subject: [PATCH 1/4] feat(jellyseerr): implement expose contributes to https://github.com/rasmus-kirk/nixarr/issues/22 --- nixarr/jellyseerr/default.nix | 136 ++++++++++++++++++++++++++++------ 1 file changed, 114 insertions(+), 22 deletions(-) diff --git a/nixarr/jellyseerr/default.nix b/nixarr/jellyseerr/default.nix index 494352a..07378f9 100644 --- a/nixarr/jellyseerr/default.nix +++ b/nixarr/jellyseerr/default.nix @@ -68,9 +68,51 @@ in { description = '' **Required options:** [`nixarr.vpn.enable`](#nixarr.vpn.enable) + **Conflicting options:** [`nixarr.jellyseerr.expose.https.enable`](#nixarr.jellyseerr.expose.https.enable) + Route Jellyseerr traffic through the VPN. ''; }; + + expose = { + https = { + enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + **Required options:** + + - [`nixarr.jellyseerr.expose.https.acmeMail`](#nixarr.jellyseerr.expose.https.acmemail) + - [`nixarr.jellyseerr.expose.https.domainName`](#nixarr.jellyseerr.expose.https.domainname) + + **Conflicting options:** [`nixarr.jellyseerr.vpn.enable`](#nixarr.jellyseerr.vpn.enable) + + Expose the Jellyseerr web service to the internet with https support, + allowing anyone to access it. + + > **Warning:** Do _not_ enable this without setting up Jellyseerr + > 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 = "jellyseerr.example.com"; + description = "The domain name to host Jellyseerr 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 { @@ -89,6 +131,28 @@ in { nixarr.vpn.enable option to be set, but it was not. ''; } + { + assertion = !(cfg.vpn.enable && cfg.expose.https.enable); + message = '' + The nixarr.jellyseerr.vpn.enable option conflicts with the + nixarr.jellyseerr.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.jellyseerr.expose.https.enable option requires the + following options to be set, but one of them were not: + + - nixarr.jellyseerr.expose.https.domainName + - nixarr.jellyseerr.expose.https.acmeMail + ''; + } ]; util-nixarr.services.jellyseerr = { @@ -99,6 +163,56 @@ in { configDir = cfg.stateDir; }; + networking.firewall = mkIf cfg.expose.https.enable { + allowedTCPPorts = [80 443]; + }; + + util-nixarr.upnp = mkIf cfg.expose.https.upnp.enable { + enable = true; + openTcpPorts = [80 443]; + }; + + 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}" = { + listen = [ + { + addr = "0.0.0.0"; + port = defaultPort; + } + ]; + 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.jellyseerr.vpnConfinement = mkIf cfg.vpn.enable { enable = true; @@ -114,27 +228,5 @@ in { } ]; }; - - services.nginx = mkIf cfg.vpn.enable { - enable = true; - - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - - virtualHosts."127.0.0.1:${builtins.toString defaultPort}" = { - listen = [ - { - addr = "0.0.0.0"; - port = defaultPort; - } - ]; - locations."/" = { - recommendedProxySettings = true; - proxyWebsockets = true; - proxyPass = "http://192.168.15.1:${builtins.toString defaultPort}"; - }; - }; - }; }; } From 32b4c3797b942325e6fbaf78ac197ef3e76b2435 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Tue, 18 Mar 2025 20:06:17 +0100 Subject: [PATCH 2/4] Sandboxed jellyseerr --- CHANGELOG.md | 1 + .../jellyseerr/jellyseerr-module/default.nix | 33 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1723d3c..2625aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Added: - Plex service +- Expose and sandboxing to Jellyseerr module Updated: - If `nixarr.enable` is not enabled other services will automatically now diff --git a/nixarr/jellyseerr/jellyseerr-module/default.nix b/nixarr/jellyseerr/jellyseerr-module/default.nix index 8b4d4a4..103131a 100644 --- a/nixarr/jellyseerr/jellyseerr-module/default.nix +++ b/nixarr/jellyseerr/jellyseerr-module/default.nix @@ -1,4 +1,3 @@ -# TODO: Add expose options for this module { config, pkgs, @@ -68,22 +67,22 @@ in { Group = cfg.group; ExecStart = lib.getExe cfg.package; Restart = "on-failure"; - # TODO: These are from nixpkgs, add them back if we add expose options - # ProtectHome = true; - # ProtectSystem = "strict"; - # PrivateTmp = true; - # PrivateDevices = true; - # ProtectHostname = true; - # ProtectClock = true; - # ProtectKernelTunables = true; - # ProtectKernelModules = true; - # ProtectKernelLogs = true; - # ProtectControlGroups = true; - # NoNewPrivileges = true; - # RestrictRealtime = true; - # RestrictSUIDSGID = true; - # RemoveIPC = true; - # PrivateMounts = true; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + NoNewPrivileges = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + ProtectSystem = "strict"; + ReadWritePaths = [ cfg.configDir ]; }; }; From eae2381419fe8440fb704e6e001ab3e917e314e3 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Tue, 18 Mar 2025 21:42:18 +0100 Subject: [PATCH 3/4] Fix --- nixarr/ddns/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixarr/ddns/default.nix b/nixarr/ddns/default.nix index ca7fb80..bd21ee7 100644 --- a/nixarr/ddns/default.nix +++ b/nixarr/ddns/default.nix @@ -109,7 +109,7 @@ in { }; }; - config = mkIf (nixarr.enable && cfg.enable) { + config = mkIf nixarr.enable { assertions = [ { assertion = cfg.njalla.enable -> cfg.njalla.keysFile != null; From b6a6206cea816481681e2f682ae8dd737b6299b8 Mon Sep 17 00:00:00 2001 From: rasmus-kirk Date: Tue, 18 Mar 2025 22:25:56 +0100 Subject: [PATCH 4/4] Plex/JF fix --- CHANGELOG.md | 2 +- nixarr/jellyfin/default.nix | 19 ++++++++++++++----- nixarr/plex/default.nix | 11 ++++++++++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2625aa3..77a2c6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Added: - Plex service -- Expose and sandboxing to Jellyseerr module +- Sandboxed Jellyseerr module and added expose option Updated: - If `nixarr.enable` is not enabled other services will automatically now diff --git a/nixarr/jellyfin/default.nix b/nixarr/jellyfin/default.nix index e5fe466..a39b8a6 100644 --- a/nixarr/jellyfin/default.nix +++ b/nixarr/jellyfin/default.nix @@ -16,6 +16,8 @@ in { example = true; description = '' Whether or not to enable the Jellyfin service. + + **Conflicting options:** [`nixarr.plex.enable`](#nixarr.plex.enable) ''; }; @@ -104,6 +106,13 @@ in { config = mkIf (nixarr.enable && cfg.enable) { assertions = [ + { + assertion = cfg.enable -> !nixarr.plex.enable; + message = '' + The nixarr.jellyfin.vpn.enable option requires the nixarr.plex.enable + option to NOT be set, but it was. + ''; + } { assertion = cfg.vpn.enable -> nixarr.vpn.enable; message = '' @@ -147,11 +156,11 @@ in { "d '${cfg.stateDir}' 0700 streamer root - -" # Media Dirs - "d '${nixarr.mediaDir}/library' 0775 streamer media - -" - "d '${nixarr.mediaDir}/library/shows' 0775 streamer media - -" - "d '${nixarr.mediaDir}/library/movies' 0775 streamer media - -" - "d '${nixarr.mediaDir}/library/music' 0775 streamer media - -" - "d '${nixarr.mediaDir}/library/books' 0775 streamer media - -" + "d '${nixarr.mediaDir}/library' 0775 streamer media - -" + "d '${nixarr.mediaDir}/library/shows' 0775 streamer media - -" + "d '${nixarr.mediaDir}/library/movies' 0775 streamer media - -" + "d '${nixarr.mediaDir}/library/music' 0775 streamer media - -" + "d '${nixarr.mediaDir}/library/books' 0775 streamer media - -" ]; # Always prioritise Jellyfin IO diff --git a/nixarr/plex/default.nix b/nixarr/plex/default.nix index 2d7372f..5d5867f 100644 --- a/nixarr/plex/default.nix +++ b/nixarr/plex/default.nix @@ -16,10 +16,12 @@ in { example = true; description = '' Whether or not to enable the Plex service. + + **Conflicting options:** [`nixarr.jellyfin.enable`](#nixarr.jellyfin.enable) ''; }; - package = mkPackageOption pkgs "plexmediaserver" {}; + package = mkPackageOption pkgs "plex" {}; stateDir = mkOption { type = types.path; @@ -104,6 +106,13 @@ in { config = mkIf (nixarr.enable && cfg.enable) { assertions = [ + { + assertion = cfg.enable -> !nixarr.jellyfin.enable; + message = '' + The nixarr.plex.vpn.enable option requires the nixarr.jellyfin.enable + option to NOT be set, but it was. + ''; + } { assertion = cfg.vpn.enable -> nixarr.vpn.enable; message = ''