diff --git a/hosts/by-name/servo/services/jellyfin/default.nix b/hosts/by-name/servo/services/jellyfin/default.nix index 1ac6da056..4055558e1 100644 --- a/hosts/by-name/servo/services/jellyfin/default.nix +++ b/hosts/by-name/servo/services/jellyfin/default.nix @@ -14,158 +14,160 @@ # # N.B.: default install DOES NOT SUPPORT DLNA out of the box. # one must install it as a "plugin", which can be done through the UI. -{ ... }: +{ config, lib, ... }: # lib.mkIf false #< XXX(2024-11-17): disabled because it hasn't been working for months; web UI hangs on load, TVs see no files { - # https://jellyfin.org/docs/general/networking/index.html - sane.ports.ports."1900" = { - protocol = [ "udp" ]; - visibleTo.lan = true; - description = "colin-upnp-for-jellyfin"; - }; - sane.ports.ports."7359" = { - protocol = [ "udp" ]; - visibleTo.lan = true; - description = "colin-jellyfin-specific-client-discovery"; - # ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall - }; - # not sure if 8096/8920 get used either: - sane.ports.ports."8096" = { - protocol = [ "tcp" ]; - visibleTo.lan = true; - description = "colin-jellyfin-http-lan"; - }; - sane.ports.ports."8920" = { - protocol = [ "tcp" ]; - visibleTo.lan = true; - description = "colin-jellyfin-https-lan"; - }; - - sane.persist.sys.byStore.plaintext = [ - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/data"; method = "bind"; } - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/metadata"; method = "bind"; } - # TODO: ship plugins statically, via nix. that'll be less fragile - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/plugins/DLNA_5.0.0.0"; method = "bind"; } - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/root"; method = "bind"; } - ]; - sane.persist.sys.byStore.ephemeral = [ - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/log"; method = "bind"; } - { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/transcodes"; method = "bind"; } - ]; - - services.jellyfin.enable = true; - users.users.jellyfin.extraGroups = [ "media" ]; - - sane.fs."/var/lib/jellyfin".dir.acl = { - user = "jellyfin"; - group = "jellyfin"; - mode = "0700"; - }; - - # `"Jellyfin.Plugin.Dlna": "Debug"` logging: - # TODO: switch Dlna back to 'Information' once satisfied with stability - sane.fs."/var/lib/jellyfin/config/logging.json".symlink.text = '' - { - "Serilog": { - "MinimumLevel": { - "Default": "Information", - "Override": { - "Microsoft": "Warning", - "System": "Warning", - "Jellyfin.Plugin.Dlna": "Debug" - } - }, - "WriteTo": [ - { - "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}" - } - } - ], - "Enrich": [ "FromLogContext", "WithThreadId" ] - } - } - ''; - - sane.fs."/var/lib/jellyfin/config/network.xml".file.text = '' - - - - false - false - 8096 - 8920 - 8096 - 8920 - true - false - true - false - true - - 10.78.76.0/22 - - - 127.0.0.1 - localhost - 10.78.79.1 - - false - - false - - - false - - ''; - - # guest user id is `5ad194d60dca41de84b332950ffc4308` - sane.fs."/var/lib/jellyfin/plugins/configurations/Jellyfin.Plugin.Dlna.xml".file.text = '' - - - true - 60 - true - 180 - true - 5ad194d6-0dca-41de-84b3-32950ffc4308 - - ''; - - # fix LG TV to play more files. - # there are certain files for which it only supports Direct Play (not even "Direct Stream" -- but "Direct Play"). - # this isn't a 100% fix: patching the profile allows e.g. Azumanga Daioh to play, - # but A Place Further Than the Universe still fails as before. - # - # profile is based on upstream: - sane.fs."/var/lib/jellyfin/plugins/DLNA_5.0.0.0/profiles/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml; - # XXX(2024-11-17): old method, but the file referenced seems not to be used and setting just it causes failures: - # > [DBG] Jellyfin.Plugin.Dlna.ContentDirectory.ContentDirectoryService: Not eligible for DirectPlay due to unsupported subtitles - # sane.fs."/var/lib/jellyfin/plugins/configurations/dlna/user/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml; - - systemd.services.jellyfin.unitConfig.RequiresMountsFor = [ - "/var/media" - ]; - - # Jellyfin multimedia server - # this is mostly taken from the official jellfin.org docs - services.nginx.virtualHosts."jelly.uninsane.org" = { - forceSSL = true; - enableACME = true; - # inherit kTLS; - - locations."/" = { - proxyPass = "http://127.0.0.1:8096"; - proxyWebsockets = true; - recommendedProxySettings = true; - # extraConfig = '' - # # Disable buffering when the nginx proxy gets very resource heavy upon streaming - # proxy_buffering off; - # ''; + config = lib.mkIf (config.sane.maxBuildCost >= 2) { + # https://jellyfin.org/docs/general/networking/index.html + sane.ports.ports."1900" = { + protocol = [ "udp" ]; + visibleTo.lan = true; + description = "colin-upnp-for-jellyfin"; + }; + sane.ports.ports."7359" = { + protocol = [ "udp" ]; + visibleTo.lan = true; + description = "colin-jellyfin-specific-client-discovery"; + # ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall + }; + # not sure if 8096/8920 get used either: + sane.ports.ports."8096" = { + protocol = [ "tcp" ]; + visibleTo.lan = true; + description = "colin-jellyfin-http-lan"; + }; + sane.ports.ports."8920" = { + protocol = [ "tcp" ]; + visibleTo.lan = true; + description = "colin-jellyfin-https-lan"; }; - }; - sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native"; + sane.persist.sys.byStore.plaintext = [ + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/data"; method = "bind"; } + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/metadata"; method = "bind"; } + # TODO: ship plugins statically, via nix. that'll be less fragile + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/plugins/DLNA_5.0.0.0"; method = "bind"; } + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/root"; method = "bind"; } + ]; + sane.persist.sys.byStore.ephemeral = [ + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/log"; method = "bind"; } + { user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/var/lib/jellyfin/transcodes"; method = "bind"; } + ]; + + services.jellyfin.enable = true; + users.users.jellyfin.extraGroups = [ "media" ]; + + sane.fs."/var/lib/jellyfin".dir.acl = { + user = "jellyfin"; + group = "jellyfin"; + mode = "0700"; + }; + + # `"Jellyfin.Plugin.Dlna": "Debug"` logging: + # TODO: switch Dlna back to 'Information' once satisfied with stability + sane.fs."/var/lib/jellyfin/config/logging.json".symlink.text = '' + { + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning", + "Jellyfin.Plugin.Dlna": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}" + } + } + ], + "Enrich": [ "FromLogContext", "WithThreadId" ] + } + } + ''; + + sane.fs."/var/lib/jellyfin/config/network.xml".file.text = '' + + + + false + false + 8096 + 8920 + 8096 + 8920 + true + false + true + false + true + + 10.78.76.0/22 + + + 127.0.0.1 + localhost + 10.78.79.1 + + false + + false + + + false + + ''; + + # guest user id is `5ad194d60dca41de84b332950ffc4308` + sane.fs."/var/lib/jellyfin/plugins/configurations/Jellyfin.Plugin.Dlna.xml".file.text = '' + + + true + 60 + true + 180 + true + 5ad194d6-0dca-41de-84b3-32950ffc4308 + + ''; + + # fix LG TV to play more files. + # there are certain files for which it only supports Direct Play (not even "Direct Stream" -- but "Direct Play"). + # this isn't a 100% fix: patching the profile allows e.g. Azumanga Daioh to play, + # but A Place Further Than the Universe still fails as before. + # + # profile is based on upstream: + sane.fs."/var/lib/jellyfin/plugins/DLNA_5.0.0.0/profiles/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml; + # XXX(2024-11-17): old method, but the file referenced seems not to be used and setting just it causes failures: + # > [DBG] Jellyfin.Plugin.Dlna.ContentDirectory.ContentDirectoryService: Not eligible for DirectPlay due to unsupported subtitles + # sane.fs."/var/lib/jellyfin/plugins/configurations/dlna/user/LG Smart TV.xml".symlink.target = ./dlna/user/LG_Smart_TV.xml; + + systemd.services.jellyfin.unitConfig.RequiresMountsFor = [ + "/var/media" + ]; + + # Jellyfin multimedia server + # this is mostly taken from the official jellfin.org docs + services.nginx.virtualHosts."jelly.uninsane.org" = { + forceSSL = true; + enableACME = true; + # inherit kTLS; + + locations."/" = { + proxyPass = "http://127.0.0.1:8096"; + proxyWebsockets = true; + recommendedProxySettings = true; + # extraConfig = '' + # # Disable buffering when the nginx proxy gets very resource heavy upon streaming + # proxy_buffering off; + # ''; + }; + }; + + sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native"; + }; } diff --git a/hosts/by-name/servo/services/kiwix-serve.nix b/hosts/by-name/servo/services/kiwix-serve.nix index 1b8bd1859..83b75d9d1 100644 --- a/hosts/by-name/servo/services/kiwix-serve.nix +++ b/hosts/by-name/servo/services/kiwix-serve.nix @@ -1,40 +1,42 @@ -{ pkgs, ... }: +{ config, lib, pkgs, ... }: { - sane.services.kiwix-serve = { - enable = true; - port = 8013; - zimPaths = with pkgs.zimPackages; [ - alpinelinux_en_all_maxi.zimPath - archlinux_en_all_maxi.zimPath - bitcoin_en_all_maxi.zimPath - devdocs_en_nix.zimPath - gentoo_en_all_maxi.zimPath - # khanacademy_en_all.zimPath #< TODO: enable - openstreetmap-wiki_en_all_maxi.zimPath - psychonautwiki_en_all_maxi.zimPath - rationalwiki_en_all_maxi.zimPath - # wikipedia_en_100.zimPath - wikipedia_en_all_maxi.zimPath - # wikipedia_en_all_mini.zimPath - zimgit-food-preparation_en.zimPath - zimgit-medicine_en.zimPath - zimgit-post-disaster_en.zimPath - zimgit-water_en.zimPath - ]; - }; - - services.nginx.virtualHosts."w.uninsane.org" = { - forceSSL = true; - enableACME = true; - # inherit kTLS; - locations."/" = { - proxyPass = "http://127.0.0.1:8013"; - recommendedProxySettings = true; + config = lib.mkIf (config.sane.maxBuildCost >= 3) { + sane.services.kiwix-serve = { + enable = true; + port = 8013; + zimPaths = with pkgs.zimPackages; [ + alpinelinux_en_all_maxi.zimPath + archlinux_en_all_maxi.zimPath + bitcoin_en_all_maxi.zimPath + devdocs_en_nix.zimPath + gentoo_en_all_maxi.zimPath + # khanacademy_en_all.zimPath #< TODO: enable + openstreetmap-wiki_en_all_maxi.zimPath + psychonautwiki_en_all_maxi.zimPath + rationalwiki_en_all_maxi.zimPath + # wikipedia_en_100.zimPath + wikipedia_en_all_maxi.zimPath + # wikipedia_en_all_mini.zimPath + zimgit-food-preparation_en.zimPath + zimgit-medicine_en.zimPath + zimgit-post-disaster_en.zimPath + zimgit-water_en.zimPath + ]; }; - locations."= /robots.txt".extraConfig = '' - return 200 "User-agent: *\nDisallow: /\n"; - ''; - }; - sane.dns.zones."uninsane.org".inet.CNAME."w" = "native"; + services.nginx.virtualHosts."w.uninsane.org" = { + forceSSL = true; + enableACME = true; + # inherit kTLS; + locations."/" = { + proxyPass = "http://127.0.0.1:8013"; + recommendedProxySettings = true; + }; + locations."= /robots.txt".extraConfig = '' + return 200 "User-agent: *\nDisallow: /\n"; + ''; + }; + + sane.dns.zones."uninsane.org".inet.CNAME."w" = "native"; + }; } diff --git a/hosts/by-name/servo/services/lemmy.nix b/hosts/by-name/servo/services/lemmy.nix index f21654c9d..28b372c62 100644 --- a/hosts/by-name/servo/services/lemmy.nix +++ b/hosts/by-name/servo/services/lemmy.nix @@ -3,7 +3,7 @@ # - # - -{ lib, pkgs, ... }: +{ config, lib, pkgs, ... }: let uiPort = 1234; # default ui port is 1234 backendPort = 8536; # default backend port is 8536 @@ -24,154 +24,156 @@ let media.video.max_frame_count = 30 * 60 * 60; }; in { - services.lemmy = { - enable = true; - settings.hostname = "lemmy.uninsane.org"; - # federation.debug forces outbound federation queries to be run synchronously - # N.B.: this option might not be read for 0.17.0+? - # settings.federation.debug = true; - settings.port = backendPort; - ui.port = uiPort; - database.createLocally = true; - nginx.enable = true; - }; + config = lib.mkIf (config.sane.maxBuildCost >= 2) { + services.lemmy = { + enable = true; + settings.hostname = "lemmy.uninsane.org"; + # federation.debug forces outbound federation queries to be run synchronously + # N.B.: this option might not be read for 0.17.0+? + # settings.federation.debug = true; + settings.port = backendPort; + ui.port = uiPort; + database.createLocally = true; + nginx.enable = true; + }; - systemd.services.lemmy.environment = { - RUST_BACKTRACE = "full"; - RUST_LOG = "error"; - # RUST_LOG = "warn"; - # RUST_LOG = "debug"; - # RUST_LOG = "trace"; - # upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql"; - # - Postgres complains that we didn't specify a user - # lemmy formats the url as: - # - postgres://{user}:{password}@{host}:{port}/{database} - # SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url): - # - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...] - # LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist - # LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet - # LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS - LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@/lemmy?host=/run/postgresql"; - }; - users.groups.lemmy = {}; - users.users.lemmy = { - group = "lemmy"; - isSystemUser = true; - }; + systemd.services.lemmy.environment = { + RUST_BACKTRACE = "full"; + RUST_LOG = "error"; + # RUST_LOG = "warn"; + # RUST_LOG = "debug"; + # RUST_LOG = "trace"; + # upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql"; + # - Postgres complains that we didn't specify a user + # lemmy formats the url as: + # - postgres://{user}:{password}@{host}:{port}/{database} + # SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url): + # - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...] + # LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist + # LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet + # LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS + LEMMY_DATABASE_URL = lib.mkForce "postgres://lemmy@/lemmy?host=/run/postgresql"; + }; + users.groups.lemmy = {}; + users.users.lemmy = { + group = "lemmy"; + isSystemUser = true; + }; - services.nginx.virtualHosts."lemmy.uninsane.org" = { - forceSSL = true; - enableACME = true; - }; + services.nginx.virtualHosts."lemmy.uninsane.org" = { + forceSSL = true; + enableACME = true; + }; - sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native"; + sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native"; - systemd.services.lemmy = { - # fix to use a normal user so we can configure perms correctly - # XXX(2024-07-28): this hasn't been rigorously tested: - # possible that i've set something too strict and won't notice right away - serviceConfig.DynamicUser = lib.mkForce false; - serviceConfig.User = "lemmy"; - serviceConfig.Group = "lemmy"; + systemd.services.lemmy = { + # fix to use a normal user so we can configure perms correctly + # XXX(2024-07-28): this hasn't been rigorously tested: + # possible that i've set something too strict and won't notice right away + serviceConfig.DynamicUser = lib.mkForce false; + serviceConfig.User = "lemmy"; + serviceConfig.Group = "lemmy"; - # switch postgres from Requires -> Wants, so that postgres may restart without taking lemmy down with it. - requires = lib.mkForce []; - wants = [ "postgresql.service" ]; + # switch postgres from Requires -> Wants, so that postgres may restart without taking lemmy down with it. + requires = lib.mkForce []; + wants = [ "postgresql.service" ]; - # hardening (systemd-analyze security lemmy) - # a handful of these are specified in upstream nixpkgs, but mostly not - serviceConfig.LockPersonality = true; - serviceConfig.NoNewPrivileges = true; - serviceConfig.MemoryDenyWriteExecute = true; - serviceConfig.PrivateDevices = true; - serviceConfig.PrivateMounts = true; - serviceConfig.PrivateTmp = true; - serviceConfig.PrivateUsers = true; - serviceConfig.ProcSubset = "pid"; + # hardening (systemd-analyze security lemmy) + # a handful of these are specified in upstream nixpkgs, but mostly not + serviceConfig.LockPersonality = true; + serviceConfig.NoNewPrivileges = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateMounts = true; + serviceConfig.PrivateTmp = true; + serviceConfig.PrivateUsers = true; + serviceConfig.ProcSubset = "pid"; - serviceConfig.ProtectClock = true; - serviceConfig.ProtectControlGroups = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectHostname = true; - serviceConfig.ProtectKernelLogs = true; - serviceConfig.ProtectKernelModules = true; - serviceConfig.ProtectKernelTunables = true; - serviceConfig.ProtectProc = "invisible"; - serviceConfig.ProtectSystem = "strict"; - serviceConfig.RemoveIPC = true; - serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + serviceConfig.ProtectClock = true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectProc = "invisible"; + serviceConfig.ProtectSystem = "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; - serviceConfig.RestrictNamespaces = true; - serviceConfig.RestrictSUIDSGID = true; - serviceConfig.SystemCallArchitectures = "native"; - serviceConfig.SystemCallFilter = [ "@system-service" ]; - }; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.SystemCallFilter = [ "@system-service" ]; + }; - systemd.services.lemmy-ui = { - # hardening (systemd-analyze security lemmy-ui) - # TODO: upstream into nixpkgs - serviceConfig.LockPersonality = true; - serviceConfig.NoNewPrivileges = true; - # serviceConfig.MemoryDenyWriteExecute = true; #< it uses v8, JIT - serviceConfig.PrivateDevices = true; - serviceConfig.PrivateMounts = true; - serviceConfig.PrivateTmp = true; - serviceConfig.PrivateUsers = true; - serviceConfig.ProcSubset = "pid"; + systemd.services.lemmy-ui = { + # hardening (systemd-analyze security lemmy-ui) + # TODO: upstream into nixpkgs + serviceConfig.LockPersonality = true; + serviceConfig.NoNewPrivileges = true; + # serviceConfig.MemoryDenyWriteExecute = true; #< it uses v8, JIT + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateMounts = true; + serviceConfig.PrivateTmp = true; + serviceConfig.PrivateUsers = true; + serviceConfig.ProcSubset = "pid"; - serviceConfig.ProtectClock = true; - serviceConfig.ProtectControlGroups = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectHostname = true; - serviceConfig.ProtectKernelLogs = true; - serviceConfig.ProtectKernelModules = true; - serviceConfig.ProtectKernelTunables = true; - serviceConfig.ProtectProc = "invisible"; - serviceConfig.ProtectSystem = "strict"; - serviceConfig.RemoveIPC = true; - serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + serviceConfig.ProtectClock = true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectProc = "invisible"; + serviceConfig.ProtectSystem = "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; - serviceConfig.RestrictNamespaces = true; - serviceConfig.RestrictSUIDSGID = true; - serviceConfig.SystemCallArchitectures = "native"; - serviceConfig.SystemCallFilter = [ "@system-service" "@pkey" "@sandbox" ]; - }; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.SystemCallFilter = [ "@system-service" "@pkey" "@sandbox" ]; + }; - #v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this. - services.pict-rs.package = pict-rs; + #v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this. + services.pict-rs.package = pict-rs; - systemd.services.pict-rs = { - serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [ - (lib.getExe pict-rs) - "--config-file" - tomlConfig - "run" - ]); + systemd.services.pict-rs = { + serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [ + (lib.getExe pict-rs) + "--config-file" + tomlConfig + "run" + ]); - # hardening (systemd-analyze security pict-rs) - # TODO: upstream into nixpkgs - serviceConfig.LockPersonality = true; - serviceConfig.NoNewPrivileges = true; - serviceConfig.MemoryDenyWriteExecute = true; - serviceConfig.PrivateDevices = true; - serviceConfig.PrivateMounts = true; - serviceConfig.PrivateTmp = true; - serviceConfig.PrivateUsers = true; - serviceConfig.ProcSubset = "pid"; - serviceConfig.ProtectClock = true; - serviceConfig.ProtectControlGroups = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectHostname = true; - serviceConfig.ProtectKernelLogs = true; - serviceConfig.ProtectKernelModules = true; - serviceConfig.ProtectKernelTunables = true; - serviceConfig.ProtectProc = "invisible"; - serviceConfig.ProtectSystem = "strict"; - serviceConfig.RemoveIPC = true; - serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; - serviceConfig.RestrictNamespaces = true; - serviceConfig.RestrictSUIDSGID = true; - serviceConfig.SystemCallArchitectures = "native"; - serviceConfig.SystemCallFilter = [ "@system-service" ]; + # hardening (systemd-analyze security pict-rs) + # TODO: upstream into nixpkgs + serviceConfig.LockPersonality = true; + serviceConfig.NoNewPrivileges = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateMounts = true; + serviceConfig.PrivateTmp = true; + serviceConfig.PrivateUsers = true; + serviceConfig.ProcSubset = "pid"; + serviceConfig.ProtectClock = true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectProc = "invisible"; + serviceConfig.ProtectSystem = "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.SystemCallFilter = [ "@system-service" ]; + }; }; } diff --git a/hosts/by-name/servo/services/pleroma.nix b/hosts/by-name/servo/services/pleroma.nix index fdac75ae0..28eba3d53 100644 --- a/hosts/by-name/servo/services/pleroma.nix +++ b/hosts/by-name/servo/services/pleroma.nix @@ -14,207 +14,209 @@ let # logLevel = "debug"; in { - sane.persist.sys.byStore.private = [ - # contains media i've uploaded to the server - { user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; method = "bind"; } - ]; - services.pleroma.enable = true; - services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path; - services.pleroma.configs = [ - '' - import Config + config = lib.mkIf (config.sane.maxBuildCost >= 2) { + sane.persist.sys.byStore.private = [ + # contains media i've uploaded to the server + { user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; method = "bind"; } + ]; + services.pleroma.enable = true; + services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path; + services.pleroma.configs = [ + '' + import Config - config :pleroma, Pleroma.Web.Endpoint, - url: [host: "fed.uninsane.org", scheme: "https", port: 443], - http: [ip: {127, 0, 0, 1}, port: 4040] - # secret_key_base: "{secrets.pleroma.secret_key_base}", - # signing_salt: "{secrets.pleroma.signing_salt}" + config :pleroma, Pleroma.Web.Endpoint, + url: [host: "fed.uninsane.org", scheme: "https", port: 443], + http: [ip: {127, 0, 0, 1}, port: 4040] + # secret_key_base: "{secrets.pleroma.secret_key_base}", + # signing_salt: "{secrets.pleroma.signing_salt}" - config :pleroma, :instance, - name: "Perfectly Sane", - description: "Single-user Pleroma instance", - email: "admin.pleroma@uninsane.org", - notify_email: "notify.pleroma@uninsane.org", - limit: 5000, - registrations_open: true, - account_approval_required: true, - max_pinned_statuses: 5, - external_user_synchronization: true + config :pleroma, :instance, + name: "Perfectly Sane", + description: "Single-user Pleroma instance", + email: "admin.pleroma@uninsane.org", + notify_email: "notify.pleroma@uninsane.org", + limit: 5000, + registrations_open: true, + account_approval_required: true, + max_pinned_statuses: 5, + external_user_synchronization: true - # docs: https://hexdocs.pm/swoosh/Swoosh.Adapters.Sendmail.html - # test mail config with sudo -u pleroma ./bin/pleroma_ctl email test --to someone@somewhere.net - config :pleroma, Pleroma.Emails.Mailer, - enabled: true, - adapter: Swoosh.Adapters.Sendmail, - cmd_path: "${lib.getExe' pkgs.postfix "sendmail"}" + # docs: https://hexdocs.pm/swoosh/Swoosh.Adapters.Sendmail.html + # test mail config with sudo -u pleroma ./bin/pleroma_ctl email test --to someone@somewhere.net + config :pleroma, Pleroma.Emails.Mailer, + enabled: true, + adapter: Swoosh.Adapters.Sendmail, + cmd_path: "${lib.getExe' pkgs.postfix "sendmail"}" - config :pleroma, Pleroma.User, - restricted_nicknames: [ "admin", "uninsane", "root" ] + config :pleroma, Pleroma.User, + restricted_nicknames: [ "admin", "uninsane", "root" ] - config :pleroma, :media_proxy, - enabled: false, - redirect_on_failure: true - #base_url: "https://cache.pleroma.social" + config :pleroma, :media_proxy, + enabled: false, + redirect_on_failure: true + #base_url: "https://cache.pleroma.social" - # see for reference: - # - `force_custom_plan`: - config :pleroma, Pleroma.Repo, - adapter: Ecto.Adapters.Postgres, - username: "pleroma", - database: "pleroma", - hostname: "localhost", - pool_size: 10, - prepare: :named, - parameters: [ - plan_cache_mode: "force_custom_plan" - ] - # XXX: prepare: :named is needed only for PG <= 12 - # prepare: :named, - # password: "{secrets.pleroma.db_password}", + # see for reference: + # - `force_custom_plan`: + config :pleroma, Pleroma.Repo, + adapter: Ecto.Adapters.Postgres, + username: "pleroma", + database: "pleroma", + hostname: "localhost", + pool_size: 10, + prepare: :named, + parameters: [ + plan_cache_mode: "force_custom_plan" + ] + # XXX: prepare: :named is needed only for PG <= 12 + # prepare: :named, + # password: "{secrets.pleroma.db_password}", - # Configure web push notifications - config :web_push_encryption, :vapid_details, - subject: "mailto:notify.pleroma@uninsane.org" - # public_key: "{secrets.pleroma.vapid_public_key}", - # private_key: "{secrets.pleroma.vapid_private_key}" + # Configure web push notifications + config :web_push_encryption, :vapid_details, + subject: "mailto:notify.pleroma@uninsane.org" + # public_key: "{secrets.pleroma.vapid_public_key}", + # private_key: "{secrets.pleroma.vapid_private_key}" - # config :joken, default_signer: "{secrets.pleroma.joken_default_signer}" + # config :joken, default_signer: "{secrets.pleroma.joken_default_signer}" - config :pleroma, :database, rum_enabled: false - config :pleroma, :instance, static_dir: "/var/lib/pleroma/instance/static" - config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads" - config :pleroma, configurable_from_database: false + config :pleroma, :database, rum_enabled: false + config :pleroma, :instance, static_dir: "/var/lib/pleroma/instance/static" + config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads" + config :pleroma, configurable_from_database: false - # strip metadata from uploaded images - config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool.StripLocation] + # strip metadata from uploaded images + config :pleroma, Pleroma.Upload, filters: [Pleroma.Upload.Filter.Exiftool.StripLocation] - # fix log spam: - # specifically, remove LAN addresses from `reserved` - config :pleroma, Pleroma.Web.Plugs.RemoteIp, - enabled: true, - reserved: ["127.0.0.0/8", "::1/128", "fc00::/7", "172.16.0.0/12"] + # fix log spam: + # specifically, remove LAN addresses from `reserved` + config :pleroma, Pleroma.Web.Plugs.RemoteIp, + enabled: true, + reserved: ["127.0.0.0/8", "::1/128", "fc00::/7", "172.16.0.0/12"] - # TODO: GET /api/pleroma/captcha is broken - # there was a nixpkgs PR to fix this around 2022/10 though. - config :pleroma, Pleroma.Captcha, - enabled: false, - method: Pleroma.Captcha.Native + # TODO: GET /api/pleroma/captcha is broken + # there was a nixpkgs PR to fix this around 2022/10 though. + config :pleroma, Pleroma.Captcha, + enabled: false, + method: Pleroma.Captcha.Native - # (enabled by colin) - # Enable Strict-Transport-Security once SSL is working: - config :pleroma, :http_security, - sts: true + # (enabled by colin) + # Enable Strict-Transport-Security once SSL is working: + config :pleroma, :http_security, + sts: true - # docs: https://docs.pleroma.social/backend/configuration/cheatsheet/#logger - config :logger, - backends: [{ExSyslogger, :ex_syslogger}] + # docs: https://docs.pleroma.social/backend/configuration/cheatsheet/#logger + config :logger, + backends: [{ExSyslogger, :ex_syslogger}] - config :logger, :ex_syslogger, - level: :${logLevel} + config :logger, :ex_syslogger, + level: :${logLevel} - # policies => list of message rewriting facilities to be enabled - # transparence => whether to publish these rules in node_info (and /about) - config :pleroma, :mrf, - policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy], - transparency: true + # policies => list of message rewriting facilities to be enabled + # transparence => whether to publish these rules in node_info (and /about) + config :pleroma, :mrf, + policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy], + transparency: true - # reject => { host, reason } - config :pleroma, :mrf_simple, - reject: [ {"threads.net", "megacorp"}, {"*.threads.net", "megacorp"} ] - # reject: [ [host: "threads.net", reason: "megacorp"], [host: "*.threads.net", reason: "megacorp"] ] + # reject => { host, reason } + config :pleroma, :mrf_simple, + reject: [ {"threads.net", "megacorp"}, {"*.threads.net", "megacorp"} ] + # reject: [ [host: "threads.net", reason: "megacorp"], [host: "*.threads.net", reason: "megacorp"] ] - # XXX colin: not sure if this actually _does_ anything - # better to steal emoji from other instances? - # - - config :pleroma, :emoji, - shortcode_globs: ["/emoji/**/*.png"], - groups: [ - "Cirno": "/emoji/cirno/*.png", - "Kirby": "/emoji/kirby/*.png", - "Bun": "/emoji/bun/*.png", - "Yuru Camp": "/emoji/yuru_camp/*.png", - ] - '' - ]; + # XXX colin: not sure if this actually _does_ anything + # better to steal emoji from other instances? + # - + config :pleroma, :emoji, + shortcode_globs: ["/emoji/**/*.png"], + groups: [ + "Cirno": "/emoji/cirno/*.png", + "Kirby": "/emoji/kirby/*.png", + "Bun": "/emoji/bun/*.png", + "Yuru Camp": "/emoji/yuru_camp/*.png", + ] + '' + ]; - systemd.services.pleroma.path = [ - # something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start - pkgs.bash - # used by Pleroma to strip geo tags from uploads - pkgs.exiftool - # config.sane.programs.exiftool.package #< XXX(2024-10-20): breaks image uploading - # i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical - # config.sane.programs.gawk.package - # needed for email operations like password reset - pkgs.postfix - ]; + systemd.services.pleroma.path = [ + # something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start + pkgs.bash + # used by Pleroma to strip geo tags from uploads + pkgs.exiftool + # config.sane.programs.exiftool.package #< XXX(2024-10-20): breaks image uploading + # i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical + # config.sane.programs.gawk.package + # needed for email operations like password reset + pkgs.postfix + ]; - systemd.services.pleroma = { - # postgres can be slow to service early requests, preventing pleroma from starting on the first try - serviceConfig.Restart = "on-failure"; - serviceConfig.RestartSec = "10s"; + systemd.services.pleroma = { + # postgres can be slow to service early requests, preventing pleroma from starting on the first try + serviceConfig.Restart = "on-failure"; + serviceConfig.RestartSec = "10s"; - # hardening (systemd-analyze security pleroma) - # XXX(2024-07-28): this hasn't been rigorously tested: - # possible that i've set something too strict and won't notice right away - # make sure to test: - # - image/media uploading - serviceConfig.CapabilityBoundingSet = lib.mkForce [ "" "" ]; # nixos default is `~CAP_SYS_ADMIN` - serviceConfig.LockPersonality = true; - serviceConfig.NoNewPrivileges = true; - serviceConfig.MemoryDenyWriteExecute = true; - serviceConfig.PrivateDevices = lib.mkForce true; #< dunno why nixpkgs has this set false; it seems to work as true - serviceConfig.PrivateMounts = true; - serviceConfig.PrivateTmp = true; - serviceConfig.PrivateUsers = true; + # hardening (systemd-analyze security pleroma) + # XXX(2024-07-28): this hasn't been rigorously tested: + # possible that i've set something too strict and won't notice right away + # make sure to test: + # - image/media uploading + serviceConfig.CapabilityBoundingSet = lib.mkForce [ "" "" ]; # nixos default is `~CAP_SYS_ADMIN` + serviceConfig.LockPersonality = true; + serviceConfig.NoNewPrivileges = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.PrivateDevices = lib.mkForce true; #< dunno why nixpkgs has this set false; it seems to work as true + serviceConfig.PrivateMounts = true; + serviceConfig.PrivateTmp = true; + serviceConfig.PrivateUsers = true; - serviceConfig.ProtectProc = "invisible"; - serviceConfig.ProcSubset = "all"; #< needs /proc/sys/kernel/overflowuid for bwrap + serviceConfig.ProtectProc = "invisible"; + serviceConfig.ProcSubset = "all"; #< needs /proc/sys/kernel/overflowuid for bwrap - serviceConfig.ProtectClock = true; - serviceConfig.ProtectControlGroups = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectKernelModules = true; - serviceConfig.ProtectSystem = lib.mkForce "strict"; - serviceConfig.RemoveIPC = true; - serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; + serviceConfig.ProtectClock = true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectSystem = lib.mkForce "strict"; + serviceConfig.RemoveIPC = true; + serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; - serviceConfig.RestrictSUIDSGID = true; - serviceConfig.SystemCallArchitectures = "native"; - serviceConfig.SystemCallFilter = [ "@system-service" "@mount" "@sandbox" ]; #< "sandbox" might not actually be necessary + serviceConfig.RestrictSUIDSGID = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.SystemCallFilter = [ "@system-service" "@mount" "@sandbox" ]; #< "sandbox" might not actually be necessary - serviceConfig.ProtectHostname = false; #< else brap can't mount /proc - serviceConfig.ProtectKernelLogs = false; #< else breaks exiftool ("bwrap: Can't mount proc on /newroot/proc: Operation not permitted") - serviceConfig.ProtectKernelTunables = false; #< else breaks exiftool - serviceConfig.RestrictNamespaces = false; # media uploads require bwrap - }; + serviceConfig.ProtectHostname = false; #< else brap can't mount /proc + serviceConfig.ProtectKernelLogs = false; #< else breaks exiftool ("bwrap: Can't mount proc on /newroot/proc: Operation not permitted") + serviceConfig.ProtectKernelTunables = false; #< else breaks exiftool + serviceConfig.RestrictNamespaces = false; # media uploads require bwrap + }; - # this is required to allow pleroma to send email. - # raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk. - # hack to fix that. - users.users.pleroma.extraGroups = [ "postdrop" ]; + # this is required to allow pleroma to send email. + # raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk. + # hack to fix that. + users.users.pleroma.extraGroups = [ "postdrop" ]; - # Pleroma server and web interface - # TODO: enable publog? - services.nginx.virtualHosts."fed.uninsane.org" = { - forceSSL = true; # pleroma redirects to https anyway - enableACME = true; - # inherit kTLS; - locations."/" = { - proxyPass = "http://127.0.0.1:4040"; - recommendedProxySettings = true; - # documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx - extraConfig = '' - # client_max_body_size defines the maximum upload size - client_max_body_size 16m; - ''; + # Pleroma server and web interface + # TODO: enable publog? + services.nginx.virtualHosts."fed.uninsane.org" = { + forceSSL = true; # pleroma redirects to https anyway + enableACME = true; + # inherit kTLS; + locations."/" = { + proxyPass = "http://127.0.0.1:4040"; + recommendedProxySettings = true; + # documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx + extraConfig = '' + # client_max_body_size defines the maximum upload size + client_max_body_size 16m; + ''; + }; + }; + + sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native"; + + sops.secrets."pleroma_secrets" = { + owner = config.users.users.pleroma.name; }; }; - - sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native"; - - sops.secrets."pleroma_secrets" = { - owner = config.users.users.pleroma.name; - }; }