From d35531c1bdecbc523f48810eeabf086585114917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 3 Aug 2025 16:18:32 +0200 Subject: [PATCH] media --- fili/configuration.nix | 2 + fili/networking.nix | 2 +- fili/services/default.nix | 2 + fili/services/homepage.nix | 3 + fili/services/media/default.nix | 11 ++- fili/services/media/jackett.nix | 19 +++++ fili/services/media/overseerr.nix | 22 ++++++ fili/services/media/plex.nix | 82 ++++++++++++++++++++ fili/services/media/radarr.nix | 20 +++++ fili/services/media/sonarr.nix | 19 +++++ fili/services/media/torrent.nix | 125 ++++++++++++++++++++++++++++++ fili/services/reviewqueue.nix | 0 flake.lock | 18 ++++- flake.nix | 5 +- users/users.nix | 39 ++++++++++ 15 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 fili/services/homepage.nix create mode 100644 fili/services/media/jackett.nix create mode 100644 fili/services/media/overseerr.nix create mode 100644 fili/services/media/plex.nix create mode 100644 fili/services/media/radarr.nix create mode 100644 fili/services/media/sonarr.nix create mode 100644 fili/services/media/torrent.nix create mode 100644 fili/services/reviewqueue.nix diff --git a/fili/configuration.nix b/fili/configuration.nix index be8f4aa..a0559d4 100644 --- a/fili/configuration.nix +++ b/fili/configuration.nix @@ -13,8 +13,10 @@ _: { networking = { hostName = "fili"; + networkmanager.enable = true; }; + nix.settings = { # users that can interact with nix trusted-users = [ diff --git a/fili/networking.nix b/fili/networking.nix index e2239dd..610d648 100644 --- a/fili/networking.nix +++ b/fili/networking.nix @@ -7,6 +7,6 @@ _: { } ]; networking.defaultGateway = "192.168.178.1"; - networking.nameservers = [ "8.8.8.8" ]; networking.networkmanager.enable = true; + networking.firewall.enable = false; } diff --git a/fili/services/default.nix b/fili/services/default.nix index 2fd9656..926b54d 100644 --- a/fili/services/default.nix +++ b/fili/services/default.nix @@ -3,6 +3,8 @@ _: { ./nginx.nix ./databases.nix ./matrix-synapse.nix + ./reviewqueue.nix + ./homepage.nix ./media ]; } diff --git a/fili/services/homepage.nix b/fili/services/homepage.nix new file mode 100644 index 0000000..df4e214 --- /dev/null +++ b/fili/services/homepage.nix @@ -0,0 +1,3 @@ +{inputs,...}: { + +} diff --git a/fili/services/media/default.nix b/fili/services/media/default.nix index 958608a..e644e91 100644 --- a/fili/services/media/default.nix +++ b/fili/services/media/default.nix @@ -1 +1,10 @@ -_: { } +_: { + imports = [ + ./overseerr.nix + ./radarr.nix + ./sonarr.nix + ./torrent.nix + ./jackett.nix + ./plex.nix + ]; +} diff --git a/fili/services/media/jackett.nix b/fili/services/media/jackett.nix new file mode 100644 index 0000000..2318ee4 --- /dev/null +++ b/fili/services/media/jackett.nix @@ -0,0 +1,19 @@ +_: { + services.nginx = { + virtualHosts."jackett.donsz.nl" = { + forceSSL = true; + http2 = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://[::1]:9117"; + }; + }; + }; + + services.jackett = { + enable = true; + group = "jellyfin"; + user = "jellyfin"; + }; +} diff --git a/fili/services/media/overseerr.nix b/fili/services/media/overseerr.nix new file mode 100644 index 0000000..6a41605 --- /dev/null +++ b/fili/services/media/overseerr.nix @@ -0,0 +1,22 @@ +{ config, ... }: +let + port = 11002; +in +{ + config.networking.firewall.allowedTCPPorts = [ port ]; + + config.virtualisation.oci-containers.containers = { + overseerr = { + image = "mirror.gcr.io/fallenbagel/jellyseerr:develop"; + environment = { + PORT = "5555"; + TZ = "Europe/Amsterdam"; + LOG_LEVEL = "debug"; + }; + extraOptions = [ "--network=host" ]; + volumes = [ + "/var/lib/microvms/rr/storage/data/overseerr:/app/config" + ]; + }; + }; +} diff --git a/fili/services/media/plex.nix b/fili/services/media/plex.nix new file mode 100644 index 0000000..605882d --- /dev/null +++ b/fili/services/media/plex.nix @@ -0,0 +1,82 @@ +_: { + services.plex = { + enable = true; + openFirewall = true; + user = "jellyfin"; + }; + + users.groups.jellyfin = { }; + users.users.jellyfin = { + isSystemUser = true; + group = "jellyfin"; + + extraGroups = [ "storage" ]; + }; + + services.nginx = { + virtualHosts."media.donsz.nl" = { + forceSSL = true; + http2 = true; + enableACME = true; + extraConfig = '' + #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause + send_timeout 100m; + + # Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/ + ssl_stapling on; + ssl_stapling_verify on; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + #Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384. + ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; + + # Forward real ip and host to Plex + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $server_addr; + proxy_set_header Referer $server_addr; + proxy_set_header Origin $server_addr; + + # Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off. + gzip on; + gzip_vary on; + gzip_min_length 1000; + gzip_proxied any; + gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml; + gzip_disable "MSIE [1-6]\."; + + # Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones. + # Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more + client_max_body_size 100M; + + # Plex headers + proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier; + proxy_set_header X-Plex-Device $http_x_plex_device; + proxy_set_header X-Plex-Device-Name $http_x_plex_device_name; + proxy_set_header X-Plex-Platform $http_x_plex_platform; + proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version; + proxy_set_header X-Plex-Product $http_x_plex_product; + proxy_set_header X-Plex-Token $http_x_plex_token; + proxy_set_header X-Plex-Version $http_x_plex_version; + proxy_set_header X-Plex-Nocache $http_x_plex_nocache; + proxy_set_header X-Plex-Provides $http_x_plex_provides; + proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor; + proxy_set_header X-Plex-Model $http_x_plex_model; + + # Websockets + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Buffering off send to the client as soon as the data is received from Plex. + proxy_redirect off; + proxy_buffering off; + ''; + locations."/" = { + proxyPass = "http://[::1]:32400"; + }; + }; + }; +} diff --git a/fili/services/media/radarr.nix b/fili/services/media/radarr.nix new file mode 100644 index 0000000..e9f1352 --- /dev/null +++ b/fili/services/media/radarr.nix @@ -0,0 +1,20 @@ +{ ... }: +{ + services.nginx = { + virtualHosts."radarr.donsz.nl" = { + forceSSL = true; + http2 = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://[::1]:7878"; + }; + }; + }; + + services.radarr = { + enable = true; + group = "jellyfin"; + user = "jellyfin"; + }; +} diff --git a/fili/services/media/sonarr.nix b/fili/services/media/sonarr.nix new file mode 100644 index 0000000..c7d28d0 --- /dev/null +++ b/fili/services/media/sonarr.nix @@ -0,0 +1,19 @@ +_: { + services.nginx = { + virtualHosts."sonarr.donsz.nl" = { + forceSSL = true; + http2 = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://[::1]:8989"; + }; + }; + }; + + services.sonarr = { + enable = true; + group = "jellyfin"; + user = "jellyfin"; + }; +} diff --git a/fili/services/media/torrent.nix b/fili/services/media/torrent.nix new file mode 100644 index 0000000..7189f44 --- /dev/null +++ b/fili/services/media/torrent.nix @@ -0,0 +1,125 @@ +{ + config, + pkgs, + ... +}: +{ + sops.secrets.mullvad = { + sopsFile = ../../../secrets/mullvad.yaml; + owner = "root"; + format = "yaml"; + }; + + vpnNamespaces.mullvad = { + enable = true; + wireguardConfigFile = config.sops.secrets.mullvad.path; + accessibleFrom = [ + "192.168.0.0/16" + ]; + portMappings = [ + { + from = 9091; + to = 9091; + } # UI Port. + { + from = 5432; + to = 5432; + } # DB Port. + ]; + openVPNPorts = [ + { + port = 50901; + protocol = "both"; + } + { + port = 50902; + protocol = "both"; + } + { + port = 50903; + protocol = "both"; + } + { + port = 50904; + protocol = "both"; + } + { + port = 50905; + protocol = "both"; + } + { + port = 50906; + protocol = "both"; + } + { + port = 50907; + protocol = "both"; + } + { + port = 50908; + protocol = "both"; + } + { + port = 50909; + protocol = "both"; + } + ]; + }; + + # proxy."dl.donsz.nl" = { + # port = 9091; + # to = "192.168.15.1"; + # authenticated = true; + # }; + + services.nginx = { + virtualHosts."dl.donsz.nl" = { + forceSSL = true; + http2 = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://192.168.15.1:9091"; + }; + }; + }; + + systemd.services.transmission.vpnConfinement = { + enable = true; + vpnNamespace = "mullvad"; + }; + + services.transmission = { + enable = true; + package = pkgs.transmission_4; + webHome = pkgs.stdenv.mkDerivation { + name = "flood-modified"; + version = "1.0"; + src = pkgs.flood-for-transmission; + installPhase = '' + mkdir -p $out + cp -r ./* $out + cp ./config.json.defaults $out/config.json + ''; + }; + home = "/var/lib/transmission"; + user = "jellyfin"; + group = "jellyfin"; + + settings = { + download-dir = "/storage/storage/torrents"; + incomplete-dir-enabled = false; + # incomplete-dir = "/storage/storage/torrents"; + + rpc-bind-address = "192.168.15.1"; + rpc-host-whitelist-enabled = false; + rpc-whitelist-enabled = false; + + rpc-port = 9091; + + peer-port = 50909; + cache-size-mb = 2048; + preallocation = 1; + }; + }; +} diff --git a/fili/services/reviewqueue.nix b/fili/services/reviewqueue.nix new file mode 100644 index 0000000..e69de29 diff --git a/flake.lock b/flake.lock index 9f93eba..02fd13c 100644 --- a/flake.lock +++ b/flake.lock @@ -161,7 +161,8 @@ "colmena": "colmena", "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs_2", - "sops-nix": "sops-nix" + "sops-nix": "sops-nix", + "vpn-confinement": "vpn-confinement" } }, "sops-nix": { @@ -213,6 +214,21 @@ "repo": "default", "type": "github" } + }, + "vpn-confinement": { + "locked": { + "lastModified": 1749672087, + "narHash": "sha256-j8LG0s0QcvNkZZLcItl78lvTZemvsScir0dG3Ii4B1c=", + "owner": "Maroka-chan", + "repo": "VPN-Confinement", + "rev": "880b3bd2c864dce4f6afc79f6580ca699294c011", + "type": "github" + }, + "original": { + "owner": "Maroka-chan", + "repo": "VPN-Confinement", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 5c18ee3..6b513aa 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,7 @@ colmena.url = "github:zhaofengli/colmena"; flake-utils.url = "github:numtide/flake-utils"; sops-nix.url = "github:jdonszelmann/sops-nix"; + vpn-confinement.url = "github:Maroka-chan/VPN-Confinement"; }; outputs = { @@ -13,6 +14,7 @@ colmena, flake-utils, sops-nix, + vpn-confinement, ... }: let @@ -20,7 +22,7 @@ system: import nixpkgs { inherit system; - # config.allowUnfree = true; + config.allowUnfree = true; overlays = [ ]; }; in @@ -47,6 +49,7 @@ ./users/users.nix ./default-machine-config.nix sops-nix.nixosModules.sops + vpn-confinement.nixosModules.default ]; }; }; diff --git a/users/users.nix b/users/users.nix index c32bb88..d03e618 100644 --- a/users/users.nix +++ b/users/users.nix @@ -16,6 +16,45 @@ ]; }; + users.extraUsers.jonathan = { + isNormalUser = true; + shell = pkgs.fish; + openssh.authorizedKeys.keys = [ + # ori (lenovo laptop/desktop) + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIET69oniNUA2nJV5+GxQ6XuK+vQbO8Uhtgrp1TrmiXVi jana@ori" + + # bastion (arch server) + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHJT6QJcxhUKjvHBv3Bd1rugyfAFUpxIe9cu1Frw3ylL jana@bastion" + + # fili (server) + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC0pmCsQeMMJ0r3o/XN7Zw8YFa9OEqrL3ikoGTK0OUY6 jana@fili" + + # kili (tudelft laptop) + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOAXOTU6E06zjK/zkzlSPhTG35PoNRYgTCStEPUYyjeE jana@kili" + + # nori hp tudelft laptop + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOSCuEu1kFg8mAgpOuYZ/IH2Ur7LQP7sQrDjcPmerkSx jana@nori" + + # oneplus 5 phone + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBTqoHEVYxD+mwmZhPj+1+i1P0XmgTxXgSnPdPwFT1vr u0_a484@localhost" + + # git deploy key + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICgadaDrViJp0Z6fbLBAo9grkmCeNQliIPXe12l3X3i/ jana@deploy" + ]; + # Make me admin + extraGroups = [ + "systemd-journal" + "wheel" + "networkmanager" + "libvirtd" + "dialout" + "storage" + "syncthing" + "jellyfin" + "media" + ]; + }; + users.extraUsers.jana = { isNormalUser = true; shell = pkgs.fish;