diff options
Diffstat (limited to 'nixos/modules/services/misc')
28 files changed, 1260 insertions, 323 deletions
diff --git a/nixos/modules/services/misc/airsonic.nix b/nixos/modules/services/misc/airsonic.nix index 6095268eb9608..d980069608e79 100644 --- a/nixos/modules/services/misc/airsonic.nix +++ b/nixos/modules/services/misc/airsonic.nix @@ -141,7 +141,7 @@ in { -Dairsonic.home=${cfg.home} \ -Dserver.address=${cfg.listenAddress} \ -Dserver.port=${toString cfg.port} \ - -Dairsonic.contextPath=${cfg.contextPath} \ + -Dserver.context-path=${cfg.contextPath} \ -Djava.awt.headless=true \ ${optionalString (cfg.virtualHost != null) "-Dserver.use-forward-headers=true"} \ diff --git a/nixos/modules/services/misc/ananicy.nix b/nixos/modules/services/misc/ananicy.nix index f7ab41fcce61d..2129868a2919b 100644 --- a/nixos/modules/services/misc/ananicy.nix +++ b/nixos/modules/services/misc/ananicy.nix @@ -1,85 +1,119 @@ -{ config, lib, pkgs, ... }: - -with lib; +{ + config, + lib, + pkgs, + ... +}: let cfg = config.services.ananicy; - configFile = pkgs.writeText "ananicy.conf" (generators.toKeyValue { } cfg.settings); - extraRules = pkgs.writeText "extraRules" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraRules); - extraTypes = pkgs.writeText "extraTypes" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraTypes); - extraCgroups = pkgs.writeText "extraCgroups" (concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraCgroups); - servicename = if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then "ananicy-cpp" else "ananicy"; + configFile = pkgs.writeText "ananicy.conf" (lib.generators.toKeyValue { } cfg.settings); + extraRules = pkgs.writeText "extraRules" ( + lib.concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraRules + ); + extraTypes = pkgs.writeText "extraTypes" ( + lib.concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraTypes + ); + extraCgroups = pkgs.writeText "extraCgroups" ( + lib.concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.extraCgroups + ); + servicename = + if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then "ananicy-cpp" else "ananicy"; + # Ananicy-CPP with BPF is not supported on hardened kernels https://github.com/NixOS/nixpkgs/issues/327382 + finalPackage = + if (servicename == "ananicy-cpp" && config.boot.kernelPackages.isHardened) then + (cfg.package { withBpf = false; }) + else + cfg.package; in { - options = { - services.ananicy = { - enable = mkEnableOption "Ananicy, an auto nice daemon"; + options.services.ananicy = { + enable = lib.mkEnableOption "Ananicy, an auto nice daemon"; - package = mkPackageOption pkgs "ananicy" { - example = "ananicy-cpp"; - }; + package = lib.mkPackageOption pkgs "ananicy" { example = "ananicy-cpp"; }; - rulesProvider = mkPackageOption pkgs "ananicy" { - example = "ananicy-cpp"; - } // { - description = '' - Which package to copy default rules,types,cgroups from. - ''; - }; + rulesProvider = lib.mkPackageOption pkgs "ananicy" { example = "ananicy-cpp"; } // { + description = '' + Which package to copy default rules,types,cgroups from. + ''; + }; - settings = mkOption { - type = with types; attrsOf (oneOf [ int bool str ]); - default = { }; - example = { - apply_nice = false; - }; - description = '' - See <https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf> - ''; + settings = lib.mkOption { + type = + with lib.types; + attrsOf (oneOf [ + int + bool + str + ]); + default = { }; + example = { + apply_nice = false; }; + description = '' + See <https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf> + ''; + }; - extraRules = mkOption { - type = with types; listOf attrs; - default = [ ]; - description = '' - Rules to write in 'nixRules.rules'. See: - <https://github.com/Nefelim4ag/Ananicy#configuration> - <https://gitlab.com/ananicy-cpp/ananicy-cpp/#global-configuration> - ''; - example = [ - { name = "eog"; type = "Image-Viewer"; } - { name = "fdupes"; type = "BG_CPUIO"; } - ]; - }; - extraTypes = mkOption { - type = with types; listOf attrs; - default = [ ]; - description = '' - Types to write in 'nixTypes.types'. See: - <https://gitlab.com/ananicy-cpp/ananicy-cpp/#types> - ''; - example = [ - { type = "my_type"; nice = 19; other_parameter = "value"; } - { type = "compiler"; nice = 19; sched = "batch"; ioclass = "idle"; } - ]; - }; - extraCgroups = mkOption { - type = with types; listOf attrs; - default = [ ]; - description = '' - Cgroups to write in 'nixCgroups.cgroups'. See: - <https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups> - ''; - example = [ - { cgroup = "cpu80"; CPUQuota = 80; } - ]; - }; + extraRules = lib.mkOption { + type = with lib.types; listOf attrs; + default = [ ]; + description = '' + Rules to write in 'nixRules.rules'. See: + <https://github.com/Nefelim4ag/Ananicy#configuration> + <https://gitlab.com/ananicy-cpp/ananicy-cpp/#global-configuration> + ''; + example = [ + { + name = "eog"; + type = "Image-Viewer"; + } + { + name = "fdupes"; + type = "BG_CPUIO"; + } + ]; + }; + extraTypes = lib.mkOption { + type = with lib.types; listOf attrs; + default = [ ]; + description = '' + Types to write in 'nixTypes.types'. See: + <https://gitlab.com/ananicy-cpp/ananicy-cpp/#types> + ''; + example = [ + { + type = "my_type"; + nice = 19; + other_parameter = "value"; + } + { + type = "compiler"; + nice = 19; + sched = "batch"; + ioclass = "idle"; + } + ]; + }; + extraCgroups = lib.mkOption { + type = with lib.types; listOf attrs; + default = [ ]; + description = '' + Cgroups to write in 'nixCgroups.cgroups'. See: + <https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups> + ''; + example = [ + { + cgroup = "cpu80"; + CPUQuota = 80; + } + ]; }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { environment = { - systemPackages = [ cfg.package ]; + systemPackages = [ finalPackage ]; etc."ananicy.d".source = pkgs.runCommandLocal "ananicyfiles" { } '' mkdir -p $out # ananicy-cpp does not include rules or settings on purpose @@ -92,16 +126,16 @@ in # configured through .setings rm -f $out/ananicy.conf cp ${configFile} $out/ananicy.conf - ${optionalString (cfg.extraRules != [ ]) "cp ${extraRules} $out/nixRules.rules"} - ${optionalString (cfg.extraTypes != [ ]) "cp ${extraTypes} $out/nixTypes.types"} - ${optionalString (cfg.extraCgroups != [ ]) "cp ${extraCgroups} $out/nixCgroups.cgroups"} + ${lib.optionalString (cfg.extraRules != [ ]) "cp ${extraRules} $out/nixRules.rules"} + ${lib.optionalString (cfg.extraTypes != [ ]) "cp ${extraTypes} $out/nixTypes.types"} + ${lib.optionalString (cfg.extraCgroups != [ ]) "cp ${extraCgroups} $out/nixCgroups.cgroups"} ''; }; # ananicy and ananicy-cpp have different default settings services.ananicy.settings = let - mkOD = mkOptionDefault; + mkOD = lib.mkOptionDefault; in { cgroup_load = mkOD true; @@ -113,28 +147,30 @@ in apply_sched = mkOD true; apply_oom_score_adj = mkOD true; apply_cgroup = mkOD true; - } // (if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then { - # https://gitlab.com/ananicy-cpp/ananicy-cpp/-/blob/master/src/config.cpp#L12 - loglevel = mkOD "warn"; # default is info but its spammy - cgroup_realtime_workaround = mkOD config.systemd.enableUnifiedCgroupHierarchy; - log_applied_rule = mkOD false; - } else { - # https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf - check_disks_schedulers = mkOD true; - check_freq = mkOD 5; - }); + } + // ( + if servicename == "ananicy-cpp" then + { + # https://gitlab.com/ananicy-cpp/ananicy-cpp/-/blob/master/src/config.cpp#L12 + loglevel = mkOD "warn"; # default is info but its spammy + cgroup_realtime_workaround = true; + log_applied_rule = mkOD false; + } + else + { + # https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf + check_disks_schedulers = mkOD true; + check_freq = mkOD 5; + } + ); systemd = { - # https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups applies to both ananicy and -cpp - enableUnifiedCgroupHierarchy = mkDefault false; - packages = [ cfg.package ]; + packages = [ finalPackage ]; services."${servicename}" = { wantedBy = [ "default.target" ]; }; }; }; - meta = { - maintainers = with maintainers; [ artturin ]; - }; + meta.maintainers = with lib.maintainers; [ artturin ]; } diff --git a/nixos/modules/services/misc/blenderfarm.nix b/nixos/modules/services/misc/blenderfarm.nix new file mode 100644 index 0000000000000..0d8ecf7af8e20 --- /dev/null +++ b/nixos/modules/services/misc/blenderfarm.nix @@ -0,0 +1,141 @@ +{ config +, lib +, pkgs +, ... +}: +let + cfg = config.services.blendfarm; + json = pkgs.formats.json { }; + configFile = json.generate "ServerSettings" (defaultConfig // cfg.serverConfig); + defaultConfig = { + Port = 15000; + BroadcastPort = 16342; + BypassScriptUpdate = false; + BasicSecurityPassword = null; + }; +in +{ + meta.maintainers = with lib.maintainers; [ gador ]; + + options.services.blendfarm = with lib.types; { + enable = lib.mkEnableOption "Blendfarm, a render farm management software for Blender"; + package = lib.mkPackageOption pkgs "blendfarm" { }; + openFirewall = lib.mkEnableOption "allowing blendfarm network access through the firewall"; + + user = lib.mkOption { + description = "User under which blendfarm runs."; + default = "blendfarm"; + type = str; + }; + + group = lib.mkOption { + description = "Group under which blendfarm runs."; + default = "blendfarm"; + type = str; + }; + + basicSecurityPasswordFile = lib.mkOption { + description = ''Path to the password file the client needs to connect to the server. + The password must not contain a forward slash.''; + default = null; + type = nullOr str; + }; + + blenderPackage = lib.mkPackageOption pkgs "blender" { }; + + serverConfig = lib.mkOption { + description = "Server configuration"; + default = defaultConfig; + type = submodule { + freeformType = attrsOf anything; + options = { + Port = lib.mkOption { + description = "Default port blendfarm server listens on."; + default = 15000; + type = types.port; + }; + BroadcastPort = lib.mkOption { + description = "Default port blendfarm server advertises itself on."; + default = 16342; + type = types.port; + }; + + BypassScriptUpdate = lib.mkOption { + description = "Prevents blendfarm from replacing the .py self-generated scripts."; + default = false; + type = bool; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + networking.firewall = lib.optionalAttrs (cfg.openFirewall) { + allowedTCPPorts = [ cfg.serverConfig.Port ]; + allowedUDPPorts = [ cfg.serverConfig.BroadcastPort ]; + }; + + systemd.services.blendfarm-server = { + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + description = "blendfarm server"; + path = [ cfg.blenderPackage ]; + preStart = '' + rm -f ServerSettings + install -m640 ${configFile} ServerSettings + if [ ! -d "BlenderData/nix-blender-linux64" ]; then + mkdir -p BlenderData/nix-blender-linux64 + echo "nix-blender" > VersionCustom + fi + rm -f BlenderData/nix-blender-linux64/blender + ln -s ${lib.getExe cfg.blenderPackage} BlenderData/nix-blender-linux64/blender + '' + + lib.optionalString (cfg.basicSecurityPasswordFile != null) '' + BLENDFARM_PASSWORD=$(${pkgs.systemd}/bin/systemd-creds cat BLENDFARM_PASS_FILE) + sed -i "s/null/\"$BLENDFARM_PASSWORD\"/g" ServerSettings + ''; + serviceConfig = { + ExecStart = "${cfg.package}/bin/LogicReinc.BlendFarm.Server"; + DynamicUser = true; + LogsDirectory = "blendfarm"; + StateDirectory = "blendfarm"; + WorkingDirectory = "/var/lib/blendfarm"; + User = cfg.user; + Group = cfg.group; + StateDirectoryMode = "0755"; + LoadCredential = lib.optional (cfg.basicSecurityPasswordFile != null) "BLENDFARM_PASS_FILE:${cfg.basicSecurityPasswordFile}"; + ReadWritePaths = ""; + CapabilityBoundingSet = ""; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "@chown" + ]; + RestrictRealtime = true; + LockPersonality = true; + UMask = "0066"; + ProtectHostname = true; + }; + }; + + users.users.blendfarm = { + isSystemUser = true; + group = "blendfarm"; + }; + users.groups.blendfarm = { }; + }; +} diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix index 8cb51bb0b7a7f..6660d5e977ffb 100644 --- a/nixos/modules/services/misc/dictd.nix +++ b/nixos/modules/services/misc/dictd.nix @@ -62,6 +62,9 @@ in description = "DICT.org Dictionary Server"; wantedBy = [ "multi-user.target" ]; environment = { LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; }; + # Work around the fact that dictd doesn't handle SIGTERM; it terminates + # with code 143 instead of exiting with code 0. + serviceConfig.SuccessExitStatus = [ 143 ]; serviceConfig.Type = "forking"; script = "${pkgs.dict}/sbin/dictd -s -c ${dictdb}/share/dictd/dictd.conf --locale en_US.UTF-8"; }; diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix index 7b6b5249f230c..9b2ba34cc30ba 100644 --- a/nixos/modules/services/misc/etebase-server.nix +++ b/nixos/modules/services/misc/etebase-server.nix @@ -45,7 +45,7 @@ in package = mkOption { type = types.package; - default = pkgs.python3.pkgs.etebase-server; + default = pkgs.etebase-server; defaultText = literalExpression "pkgs.python3.pkgs.etebase-server"; description = "etebase-server package to use."; }; diff --git a/nixos/modules/services/misc/flaresolverr.nix b/nixos/modules/services/misc/flaresolverr.nix new file mode 100644 index 0000000000000..7967580307f99 --- /dev/null +++ b/nixos/modules/services/misc/flaresolverr.nix @@ -0,0 +1,58 @@ +{ + config, + pkgs, + lib, + ... +}: + +let + cfg = config.services.flaresolverr; +in +{ + options = { + services.flaresolverr = { + enable = lib.mkEnableOption "FlareSolverr, a proxy server to bypass Cloudflare protection"; + + package = lib.mkPackageOption pkgs "flaresolverr" { }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Open the port in the firewall for FlareSolverr."; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 8191; + description = "The port on which FlareSolverr will listen for incoming HTTP traffic."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.flaresolverr = { + description = "FlareSolverr"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + HOME = "/run/flaresolverr"; + PORT = toString cfg.port; + }; + + serviceConfig = { + SyslogIdentifier = "flaresolverr"; + Restart = "always"; + RestartSec = 5; + Type = "simple"; + DynamicUser = true; + RuntimeDirectory = "flaresolverr"; + WorkingDirectory = "/run/flaresolverr"; + ExecStart = lib.getExe cfg.package; + TimeoutStopSec = 30; + }; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; + }; +} diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix index 9a102918f35e3..9aa7b13b02e19 100644 --- a/nixos/modules/services/misc/forgejo.nix +++ b/nixos/modules/services/misc/forgejo.nix @@ -66,7 +66,7 @@ in services.forgejo = { enable = mkEnableOption "Forgejo, a software forge"; - package = mkPackageOption pkgs "forgejo" { }; + package = mkPackageOption pkgs "forgejo-lts" { }; useWizard = mkOption { default = false; diff --git a/nixos/modules/services/misc/fstrim.nix b/nixos/modules/services/misc/fstrim.nix index d2dda2636ef1b..10dced2c4e64b 100644 --- a/nixos/modules/services/misc/fstrim.nix +++ b/nixos/modules/services/misc/fstrim.nix @@ -41,5 +41,5 @@ in { }; - meta.maintainers = with maintainers; [ ]; + meta.maintainers = [ ]; } diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 7b96a182f0d94..9fd6014f2c71c 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -9,10 +9,12 @@ let toml = pkgs.formats.toml {}; yaml = pkgs.formats.yaml {}; + git = cfg.packages.gitaly.git; + postgresqlPackage = if config.services.postgresql.enable then config.services.postgresql.package else - pkgs.postgresql_13; + pkgs.postgresql_14; gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket"; gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket"; @@ -51,7 +53,7 @@ let prometheus_listen_addr = "localhost:9236" [git] - bin_path = "${pkgs.git}/bin/git" + bin_path = "${git}/bin/git" [gitlab-shell] dir = "${cfg.packages.gitlab-shell}" @@ -184,16 +186,15 @@ let MALLOC_ARENA_MAX = "2"; } // cfg.extraEnv; - runtimeDeps = with pkgs; [ + runtimeDeps = [ git ] ++ (with pkgs; [ nodejs gzip - git gnutar postgresqlPackage coreutils procps findutils # Needed for gitlab:cleanup:orphan_job_artifact_files - ]; + ]); gitlab-rake = pkgs.stdenv.mkDerivation { name = "gitlab-rake"; @@ -1119,12 +1120,12 @@ in { message = "services.gitlab.secrets.jwsFile must be set!"; } { - assertion = versionAtLeast postgresqlPackage.version "13.6.0"; - message = "PostgreSQL >=13.6 is required to run GitLab 16. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading"; + assertion = versionAtLeast postgresqlPackage.version "14.9"; + message = "PostgreSQL >= 14.9 is required to run GitLab 17. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading"; } ]; - environment.systemPackages = [ pkgs.git gitlab-rake gitlab-rails cfg.packages.gitlab-shell ]; + environment.systemPackages = [ gitlab-rake gitlab-rails cfg.packages.gitlab-shell ]; systemd.targets.gitlab = { description = "Common target for all GitLab services."; @@ -1282,6 +1283,7 @@ in { "d ${gitlabConfig.production.shared.path}/registry 0750 ${cfg.user} ${cfg.group} -" "d ${gitlabConfig.production.shared.path}/terraform_state 0750 ${cfg.user} ${cfg.group} -" "d ${gitlabConfig.production.shared.path}/ci_secure_files 0750 ${cfg.user} ${cfg.group} -" + "d ${gitlabConfig.production.shared.path}/external-diffs 0750 ${cfg.user} ${cfg.group} -" "L+ /run/gitlab/config - - - - ${cfg.statePath}/config" "L+ /run/gitlab/log - - - - ${cfg.statePath}/log" "L+ /run/gitlab/tmp - - - - ${cfg.statePath}/tmp" @@ -1294,12 +1296,11 @@ in { systemd.services.gitlab-config = { wantedBy = [ "gitlab.target" ]; partOf = [ "gitlab.target" ]; - path = with pkgs; [ + path = [ git ] ++ (with pkgs; [ jq openssl replace-secret - git - ]; + ]); serviceConfig = { Type = "oneshot"; User = cfg.user; @@ -1337,7 +1338,7 @@ in { ln -sf ${cableYml} ${cfg.statePath}/config/cable.yml ln -sf ${resqueYml} ${cfg.statePath}/config/resque.yml - ${cfg.packages.gitlab-shell}/bin/install + ${cfg.packages.gitlab-shell}/bin/gitlab-shell-install ${optionalString cfg.smtp.enable '' install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb @@ -1457,9 +1458,8 @@ in { SIDEKIQ_MEMORY_KILLER_GRACE_TIME = cfg.sidekiq.memoryKiller.graceTime; SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT = cfg.sidekiq.memoryKiller.shutdownWait; }); - path = with pkgs; [ + path = [ git ] ++ (with pkgs; [ postgresqlPackage - git ruby openssh nodejs @@ -1472,7 +1472,7 @@ in { gzip procps # Sidekiq MemoryKiller - ]; + ]); serviceConfig = { Type = "simple"; User = cfg.user; @@ -1499,12 +1499,11 @@ in { bindsTo = [ "gitlab-config.service" ]; wantedBy = [ "gitlab.target" ]; partOf = [ "gitlab.target" ]; - path = with pkgs; [ + path = [ git ] ++ (with pkgs; [ openssh - git gzip bzip2 - ]; + ]); serviceConfig = { Type = "simple"; User = cfg.user; @@ -1581,7 +1580,7 @@ in { after = [ "network.target" ]; wantedBy = [ "gitlab.target" ]; partOf = [ "gitlab.target" ]; - path = with pkgs; [ + path = [ git ] ++ (with pkgs; [ remarshal exiftool git @@ -1589,7 +1588,7 @@ in { gzip openssh cfg.packages.gitlab-workhorse - ]; + ]); serviceConfig = { Type = "simple"; User = cfg.user; @@ -1657,15 +1656,14 @@ in { requiredBy = [ "gitlab.target" ]; partOf = [ "gitlab.target" ]; environment = gitlabEnv; - path = with pkgs; [ + path = [ git ] ++ (with pkgs; [ postgresqlPackage - git openssh nodejs procps gnupg gzip - ]; + ]); serviceConfig = { Type = "notify"; User = cfg.user; diff --git a/nixos/modules/services/misc/gitweb.nix b/nixos/modules/services/misc/gitweb.nix index ec08ab51a4574..8f4869ce5d559 100644 --- a/nixos/modules/services/misc/gitweb.nix +++ b/nixos/modules/services/misc/gitweb.nix @@ -55,6 +55,6 @@ in }; - meta.maintainers = with maintainers; [ ]; + meta.maintainers = [ ]; } diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix index f320e78a91060..fb9b9e19813f1 100644 --- a/nixos/modules/services/misc/gollum.nix +++ b/nixos/modules/services/misc/gollum.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: with lib; @@ -7,6 +12,17 @@ let in { + imports = [ + (mkRemovedOptionModule + [ + "services" + "gollum" + "mathjax" + ] + "MathJax rendering might be discontinued in the future, use services.gollum.math instead to enable KaTeX rendering or file a PR if you really need Mathjax" + ) + ]; + options.services.gollum = { enable = mkEnableOption "Gollum, a git-powered wiki service"; @@ -28,20 +44,30 @@ in description = "Content of the configuration file"; }; - mathjax = mkOption { + math = mkOption { type = types.bool; default = false; - description = "Enable support for math rendering using MathJax"; + description = "Enable support for math rendering using KaTeX"; }; allowUploads = mkOption { - type = types.nullOr (types.enum [ "dir" "page" ]); + type = types.nullOr ( + types.enum [ + "dir" + "page" + ] + ); default = null; description = "Enable uploads of external files"; }; user-icons = mkOption { - type = types.nullOr (types.enum [ "gravatar" "identicon" ]); + type = types.nullOr ( + types.enum [ + "gravatar" + "identicon" + ] + ); default = null; description = "Enable specific user icons for history view"; }; @@ -109,9 +135,7 @@ in users.groups."${cfg.group}" = { }; - systemd.tmpfiles.rules = [ - "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" - ]; + systemd.tmpfiles.rules = [ "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" ]; systemd.services.gollum = { description = "Gollum wiki"; @@ -134,7 +158,7 @@ in --host ${cfg.address} \ --config ${pkgs.writeText "gollum-config.rb" cfg.extraConfig} \ --ref ${cfg.branch} \ - ${optionalString cfg.mathjax "--mathjax"} \ + ${optionalString cfg.math "--math"} \ ${optionalString cfg.emoji "--emoji"} \ ${optionalString cfg.h1-title "--h1-title"} \ ${optionalString cfg.no-edit "--no-edit"} \ @@ -147,5 +171,8 @@ in }; }; - meta.maintainers = with lib.maintainers; [ erictapen bbenno ]; + meta.maintainers = with lib.maintainers; [ + erictapen + bbenno + ]; } diff --git a/nixos/modules/services/misc/gotenberg.nix b/nixos/modules/services/misc/gotenberg.nix new file mode 100644 index 0000000000000..57932c656d632 --- /dev/null +++ b/nixos/modules/services/misc/gotenberg.nix @@ -0,0 +1,258 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.gotenberg; + + args = + [ + "--api-port=${toString cfg.port}" + "--api-timeout=${cfg.timeout}" + "--api-root-path=${cfg.rootPath}" + "--log-level=${cfg.logLevel}" + "--chromium-max-queue-size=${toString cfg.chromium.maxQueueSize}" + "--libreoffice-restart-after=${toString cfg.libreoffice.restartAfter}" + "--libreoffice-max-queue-size=${toString cfg.libreoffice.maxQueueSize}" + "--pdfengines-engines=${lib.concatStringsSep "," cfg.pdfEngines}" + ] + ++ optional cfg.enableBasicAuth "--api-enable-basic-auth" + ++ optional cfg.chromium.autoStart "--chromium-auto-start" + ++ optional cfg.chromium.disableJavascript "--chromium-disable-javascript" + ++ optional cfg.chromium.disableRoutes "--chromium-disable-routes" + ++ optional cfg.libreoffice.autoStart "--libreoffice-auto-start" + ++ optional cfg.libreoffice.disableRoutes "--libreoffice-disable-routes"; + + inherit (lib) + mkEnableOption + mkPackageOption + mkOption + types + mkIf + optional + optionalAttrs + ; +in +{ + options = { + services.gotenberg = { + enable = mkEnableOption "Gotenberg, a stateless API for PDF files"; + + # Users can override only gotenberg, libreoffice and chromium if they want to (eg. ungoogled-chromium, different LO version, etc) + # Don't allow setting the qpdf, pdftk, or unoconv paths, as those are very stable + # and there's only one version of each. + package = mkPackageOption pkgs "gotenberg" { }; + + port = mkOption { + type = types.port; + default = 3000; + description = "Port on which the API should listen."; + }; + + timeout = mkOption { + type = types.nullOr types.str; + default = "30s"; + description = "Timeout for API requests."; + }; + + rootPath = mkOption { + type = types.str; + default = "/"; + description = "Root path for the Gotenberg API."; + }; + + enableBasicAuth = mkOption { + type = types.bool; + default = false; + description = '' + HTTP Basic Authentication. + + If you set this, be sure to set `GOTENBERG_API_BASIC_AUTH_USERNAME`and `GOTENBERG_API_BASIC_AUTH_PASSWORD` + in your `services.gotenberg.environmentFile` file. + ''; + }; + + extraFontPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + description = "Extra fonts to make available."; + }; + + chromium = { + package = mkPackageOption pkgs "chromium" { }; + + maxQueueSize = mkOption { + type = types.int; + default = 0; + description = "Maximum queue size for chromium-based conversions. Setting to 0 disables the limit."; + }; + + autoStart = mkOption { + type = types.bool; + default = false; + description = "Automatically start chromium when Gotenberg starts. If false, Chromium will start on the first conversion request that uses it."; + }; + + disableJavascript = mkOption { + type = types.bool; + default = false; + description = "Disable Javascript execution."; + }; + + disableRoutes = mkOption { + type = types.bool; + default = false; + description = "Disable all routes allowing Chromium-based conversion."; + }; + }; + + libreoffice = { + package = mkPackageOption pkgs "libreoffice" { }; + + restartAfter = mkOption { + type = types.int; + default = 10; + description = "Restart LibreOffice after this many conversions. Setting to 0 disables this feature."; + }; + + maxQueueSize = mkOption { + type = types.int; + default = 0; + description = "Maximum queue size for LibreOffice-based conversions. Setting to 0 disables the limit."; + }; + + autoStart = mkOption { + type = types.bool; + default = false; + description = "Automatically start LibreOffice when Gotenberg starts. If false, Chromium will start on the first conversion request that uses it."; + }; + + disableRoutes = mkOption { + type = types.bool; + default = false; + description = "Disable all routes allowing LibreOffice-based conversion."; + }; + }; + + pdfEngines = mkOption { + type = types.listOf ( + types.enum [ + "pdftk" + "qpdf" + "libreoffice-pdfengine" + "exiftool" + "pdfcpu" + ] + ); + default = [ + "pdftk" + "qpdf" + "libreoffice-pdfengine" + "exiftool" + "pdfcpu" + ]; + description = '' + PDF engines to enable. Each one can be used to perform a specific task. + See [the documentation](https://gotenberg.dev/docs/configuration#pdf-engines) for more details. + Defaults to all possible PDF engines. + ''; + }; + + logLevel = mkOption { + type = types.enum [ + "error" + "warn" + "info" + "debug" + ]; + default = "info"; + description = "The logging level for Gotenberg."; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Environment file to load extra environment variables from."; + }; + + extraArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Any extra command-line flags to pass to the Gotenberg service."; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.enableBasicAuth -> cfg.environmentFile != null; + message = '' + When enabling HTTP Basic Authentication with `services.gotenberg.enableBasicAuth`, + you must provide an environment file via `services.gotenberg.environmentFile` with the appropriate environment variables set in it. + + See `services.gotenberg.enableBasicAuth` for the names of those variables. + ''; + } + ]; + + systemd.services.gotenberg = { + description = "Gotenberg API server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ cfg.package ]; + environment = { + LIBREOFFICE_BIN_PATH = "${cfg.libreoffice.package}/lib/libreoffice/program/soffice.bin"; + CHROMIUM_BIN_PATH = lib.getExe cfg.chromium.package; + FONTCONFIG_FILE = pkgs.makeFontsConf { + fontDirectories = [ pkgs.liberation_ttf_v2 ] ++ cfg.extraFontPackages; + }; + }; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + ExecStart = "${lib.getExe cfg.package} ${lib.escapeShellArgs args}"; + + # Hardening options + PrivateDevices = true; + PrivateIPC = true; + PrivateUsers = true; + + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + "AF_INET6" + "AF_NETLINK" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + + LockPersonality = true; + MemoryDenyWriteExecute = true; + + SystemCallFilter = [ + "@system-service" + "~@resources" + "~@privileged" + ]; + SystemCallArchitectures = "native"; + + UMask = 77; + } // optionalAttrs (cfg.environmentFile != null) { EnvironmentFile = cfg.environmentFile; }; + }; + }; + + meta.maintainers = with lib.maintainers; [ pyrox0 ]; +} diff --git a/nixos/modules/services/misc/graphical-desktop.nix b/nixos/modules/services/misc/graphical-desktop.nix index c8fe0d921c6ad..246310195edc2 100644 --- a/nixos/modules/services/misc/graphical-desktop.nix +++ b/nixos/modules/services/misc/graphical-desktop.nix @@ -42,6 +42,8 @@ in programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3; + services.speechd.enable = lib.mkDefault true; + systemd.defaultUnit = lib.mkIf (xcfg.autorun || dmcfg.enable) "graphical.target"; xdg = { diff --git a/nixos/modules/services/misc/jackett.nix b/nixos/modules/services/misc/jackett.nix index 8b5011ce0d814..a843f400b0314 100644 --- a/nixos/modules/services/misc/jackett.nix +++ b/nixos/modules/services/misc/jackett.nix @@ -11,6 +11,14 @@ in services.jackett = { enable = mkEnableOption "Jackett, API support for your favorite torrent trackers"; + port = mkOption { + default = 9117; + type = types.port; + description = '' + Port serving the web interface + ''; + }; + dataDir = mkOption { type = types.str; default = "/var/lib/jackett/.config/Jackett"; @@ -53,13 +61,13 @@ in Type = "simple"; User = cfg.user; Group = cfg.group; - ExecStart = "${cfg.package}/bin/Jackett --NoUpdates --DataFolder '${cfg.dataDir}'"; + ExecStart = "${cfg.package}/bin/Jackett --NoUpdates --Port ${toString cfg.port} --DataFolder '${cfg.dataDir}'"; Restart = "on-failure"; }; }; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPorts = [ 9117 ]; + allowedTCPPorts = [ cfg.port ]; }; users.users = mkIf (cfg.user == "jackett") { diff --git a/nixos/modules/services/misc/jellyseerr.nix b/nixos/modules/services/misc/jellyseerr.nix index 7599a1af33840..9aab517e0493b 100644 --- a/nixos/modules/services/misc/jellyseerr.nix +++ b/nixos/modules/services/misc/jellyseerr.nix @@ -9,6 +9,7 @@ in options.services.jellyseerr = { enable = mkEnableOption ''Jellyseerr, a requests manager for Jellyfin''; + package = mkPackageOption pkgs "jellyseerr" { }; openFirewall = mkOption { type = types.bool; @@ -32,10 +33,10 @@ in serviceConfig = { Type = "exec"; StateDirectory = "jellyseerr"; - WorkingDirectory = "${pkgs.jellyseerr}/libexec/jellyseerr/deps/jellyseerr"; + WorkingDirectory = "${cfg.package}/libexec/jellyseerr/deps/jellyseerr"; DynamicUser = true; - ExecStart = "${pkgs.jellyseerr}/bin/jellyseerr"; - BindPaths = [ "/var/lib/jellyseerr/:${pkgs.jellyseerr}/libexec/jellyseerr/deps/jellyseerr/config/" ]; + ExecStart = lib.getExe cfg.package; + BindPaths = [ "/var/lib/jellyseerr/:${cfg.package}/libexec/jellyseerr/deps/jellyseerr/config/" ]; Restart = "on-failure"; ProtectHome = true; ProtectSystem = "strict"; diff --git a/nixos/modules/services/misc/languagetool.nix b/nixos/modules/services/misc/languagetool.nix index ba563dace4737..2a7e68c9053a3 100644 --- a/nixos/modules/services/misc/languagetool.nix +++ b/nixos/modules/services/misc/languagetool.nix @@ -1,14 +1,17 @@ -{ config, lib, options, pkgs, ... }: +{ config, lib, pkgs, ... }: with lib; let cfg = config.services.languagetool; - settingsFormat = pkgs.formats.javaProperties {}; -in { + settingsFormat = pkgs.formats.javaProperties { }; +in +{ options.services.languagetool = { enable = mkEnableOption "the LanguageTool server, a multilingual spelling, style, and grammar checker that helps correct or paraphrase texts"; + package = mkPackageOption pkgs "languagetool" { }; + port = mkOption { type = types.port; default = 8081; @@ -31,7 +34,7 @@ in { ''; }; - settings = lib.mkOption { + settings = mkOption { type = types.submodule { freeformType = settingsFormat.type; @@ -49,11 +52,25 @@ in { for supported settings. ''; }; + + jrePackage = mkPackageOption pkgs "jre" { }; + + jvmOptions = mkOption { + description = '' + Extra command line options for the JVM running languagetool. + More information can be found here: https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html#standard-options-for-java + ''; + default = [ ]; + type = types.listOf types.str; + example = [ + "-Xmx512m" + ]; + }; }; config = mkIf cfg.enable { - systemd.services.languagetool = { + systemd.services.languagetool = { description = "LanguageTool HTTP server"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; @@ -65,13 +82,17 @@ in { RestrictNamespaces = [ "" ]; SystemCallFilter = [ "@system-service" "~ @privileged" ]; ProtectHome = "yes"; + Restart = "on-failure"; ExecStart = '' - ${pkgs.languagetool}/bin/languagetool-http-server \ - --port ${toString cfg.port} \ - ${optionalString cfg.public "--public"} \ - ${optionalString (cfg.allowOrigin != null) "--allow-origin ${cfg.allowOrigin}"} \ - "--config" ${settingsFormat.generate "languagetool.conf" cfg.settings} - ''; + ${cfg.jrePackage}/bin/java \ + -cp ${cfg.package}/share/languagetool-server.jar \ + ${toString cfg.jvmOptions} \ + org.languagetool.server.HTTPServer \ + --port ${toString cfg.port} \ + ${optionalString cfg.public "--public"} \ + ${optionalString (cfg.allowOrigin != null) "--allow-origin ${cfg.allowOrigin}"} \ + "--config" ${settingsFormat.generate "languagetool.conf" cfg.settings} + ''; }; }; }; diff --git a/nixos/modules/services/misc/mame.nix b/nixos/modules/services/misc/mame.nix index 6c7f08d48be10..38b4dd290ed56 100644 --- a/nixos/modules/services/misc/mame.nix +++ b/nixos/modules/services/misc/mame.nix @@ -65,5 +65,5 @@ in }; }; - meta.maintainers = with lib.maintainers; [ ]; + meta.maintainers = [ ]; } diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix index 1467c3f93bc85..f8dbfe9c5692d 100644 --- a/nixos/modules/services/misc/ollama.nix +++ b/nixos/modules/services/misc/ollama.nix @@ -1,78 +1,91 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let - inherit (lib) types; + inherit (lib) literalExpression types mkBefore; cfg = config.services.ollama; - ollamaPackage = cfg.package.override { - inherit (cfg) acceleration; - linuxPackages = config.boot.kernelPackages // { - nvidia_x11 = config.hardware.nvidia.package; - }; - }; + ollamaPackage = cfg.package.override { inherit (cfg) acceleration; }; + + staticUser = cfg.user != null && cfg.group != null; in { imports = [ - (lib.mkRemovedOptionModule [ "services" "ollama" "listenAddress" ] - "Use `services.ollama.host` and `services.ollama.port` instead.") + (lib.mkRemovedOptionModule [ + "services" + "ollama" + "listenAddress" + ] "Use `services.ollama.host` and `services.ollama.port` instead.") + (lib.mkRemovedOptionModule [ + "services" + "ollama" + "sandbox" + ] "Set `services.ollama.user` and `services.ollama.group` instead.") + (lib.mkRemovedOptionModule + [ + "services" + "ollama" + "writablePaths" + ] + "The `models` directory is now always writable. To make other directories writable, use `systemd.services.ollama.serviceConfig.ReadWritePaths`." + ) ]; options = { services.ollama = { enable = lib.mkEnableOption "ollama server for local large language models"; package = lib.mkPackageOption pkgs "ollama" { }; + + user = lib.mkOption { + type = with types; nullOr str; + default = null; + example = "ollama"; + description = '' + User account under which to run ollama. Defaults to [`DynamicUser`](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=) + when set to `null`. + + The user will automatically be created, if this option is set to a non-null value. + ''; + }; + + group = lib.mkOption { + type = with types; nullOr str; + default = cfg.user; + defaultText = literalExpression "config.services.ollama.user"; + example = "ollama"; + description = '' + Group under which to run ollama. Only used when `services.ollama.user` is set. + + The group will automatically be created, if this option is set to a non-null value. + ''; + }; + home = lib.mkOption { type = types.str; - default = "%S/ollama"; + default = "/var/lib/ollama"; example = "/home/foo"; description = '' The home directory that the ollama service is started in. - - See also `services.ollama.writablePaths` and `services.ollama.sandbox`. ''; }; + models = lib.mkOption { type = types.str; - default = "%S/ollama/models"; + default = "${cfg.home}/models"; + defaultText = "\${config.services.ollama.home}/models"; example = "/path/to/ollama/models"; description = '' The directory that the ollama service will read models from and download new models to. - - See also `services.ollama.writablePaths` and `services.ollama.sandbox` - if downloading models or other mutation of the filesystem is required. ''; }; - sandbox = lib.mkOption { - type = types.bool; - default = true; - example = false; - description = '' - Whether to enable systemd's sandboxing capabilities. - This sets [`DynamicUser`]( - https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser= - ), which runs the server as a unique user with read-only access to most of the filesystem. - - See also `services.ollama.writablePaths`. - ''; - }; - writablePaths = lib.mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "/home/foo" "/mnt/foo" ]; - description = '' - Paths that the server should have write access to. - - This sets [`ReadWritePaths`]( - https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#ReadWritePaths= - ), which allows specified paths to be written to through the default sandboxing. - - See also `services.ollama.sandbox`. - ''; - }; host = lib.mkOption { type = types.str; default = "127.0.0.1"; - example = "0.0.0.0"; + example = "[::]"; description = '' The host address which the ollama server HTTP interface listens to. ''; @@ -86,7 +99,13 @@ in ''; }; acceleration = lib.mkOption { - type = types.nullOr (types.enum [ false "rocm" "cuda" ]); + type = types.nullOr ( + types.enum [ + false + "rocm" + "cuda" + ] + ); default = null; example = "rocm"; description = '' @@ -132,6 +151,14 @@ in Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient. ''; }; + loadModels = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The models to download as soon as the service starts. + Search for models of your choice from: https://ollama.com/library + ''; + }; openFirewall = lib.mkOption { type = types.bool; default = false; @@ -144,23 +171,98 @@ in }; config = lib.mkIf cfg.enable { + users = lib.mkIf staticUser { + users.${cfg.user} = { + inherit (cfg) home; + isSystemUser = true; + group = cfg.group; + }; + groups.${cfg.group} = { }; + }; + systemd.services.ollama = { description = "Server for local large language models"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - environment = cfg.environmentVariables // { - HOME = cfg.home; - OLLAMA_MODELS = cfg.models; - OLLAMA_HOST = "${cfg.host}:${toString cfg.port}"; - HSA_OVERRIDE_GFX_VERSION = lib.mkIf (cfg.rocmOverrideGfx != null) cfg.rocmOverrideGfx; - }; - serviceConfig = { - ExecStart = "${lib.getExe ollamaPackage} serve"; - WorkingDirectory = cfg.home; - StateDirectory = [ "ollama" ]; - DynamicUser = cfg.sandbox; - ReadWritePaths = cfg.writablePaths; - }; + environment = + cfg.environmentVariables + // { + HOME = cfg.home; + OLLAMA_MODELS = cfg.models; + OLLAMA_HOST = "${cfg.host}:${toString cfg.port}"; + } + // lib.optionalAttrs (cfg.rocmOverrideGfx != null) { + HSA_OVERRIDE_GFX_VERSION = cfg.rocmOverrideGfx; + }; + serviceConfig = + lib.optionalAttrs staticUser { + User = cfg.user; + Group = cfg.group; + } + // { + DynamicUser = true; + ExecStart = "${lib.getExe ollamaPackage} serve"; + WorkingDirectory = cfg.home; + StateDirectory = [ "ollama" ]; + ReadWritePaths = [ + cfg.home + cfg.models + ]; + + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ + # CUDA + # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf + "char-nvidiactl" + "char-nvidia-caps" + "char-nvidia-frontend" + "char-nvidia-uvm" + # ROCm + "char-drm" + "char-kfd" + ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = false; # hides acceleration devices + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "all"; # /proc/meminfo + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + SupplementaryGroups = [ "render" ]; # for rocm to access /dev/dri/renderD* devices + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service @resources" + "~@privileged" + ]; + UMask = "0077"; + }; + postStart = mkBefore '' + set -x + export OLLAMA_HOST=${lib.escapeShellArg cfg.host}:${builtins.toString cfg.port} + for model in ${lib.escapeShellArgs cfg.loadModels} + do + ${lib.escapeShellArg (lib.getExe ollamaPackage)} pull "$model" + done + ''; }; networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; @@ -168,5 +270,8 @@ in environment.systemPackages = [ ollamaPackage ]; }; - meta.maintainers = with lib.maintainers; [ abysssol onny ]; + meta.maintainers = with lib.maintainers; [ + abysssol + onny + ]; } diff --git a/nixos/modules/services/misc/private-gpt.nix b/nixos/modules/services/misc/private-gpt.nix index ad9b6f5ffa80f..7bd2e492f5b56 100644 --- a/nixos/modules/services/misc/private-gpt.nix +++ b/nixos/modules/services/misc/private-gpt.nix @@ -117,5 +117,5 @@ in }; }; - meta.maintainers = with lib.maintainers; [ ]; + meta.maintainers = [ ]; } diff --git a/nixos/modules/services/misc/radicle.nix b/nixos/modules/services/misc/radicle.nix new file mode 100644 index 0000000000000..3a393bf0f1f20 --- /dev/null +++ b/nixos/modules/services/misc/radicle.nix @@ -0,0 +1,358 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.radicle; + + json = pkgs.formats.json { }; + + env = rec { + # rad fails if it cannot stat $HOME/.gitconfig + HOME = "/var/lib/radicle"; + RAD_HOME = HOME; + }; + + # Convenient wrapper to run `rad` in the namespaces of `radicle-node.service` + rad-system = pkgs.writeShellScriptBin "rad-system" '' + set -o allexport + ${toShellVars env} + # Note that --env is not used to preserve host's envvars like $TERM + exec ${getExe' pkgs.util-linux "nsenter"} -a \ + -t "$(${getExe' config.systemd.package "systemctl"} show -P MainPID radicle-node.service)" \ + -S "$(${getExe' config.systemd.package "systemctl"} show -P UID radicle-node.service)" \ + -G "$(${getExe' config.systemd.package "systemctl"} show -P GID radicle-node.service)" \ + ${getExe' cfg.package "rad"} "$@" + ''; + + commonServiceConfig = serviceName: { + environment = env // { + RUST_LOG = mkDefault "info"; + }; + path = [ + pkgs.gitMinimal + ]; + documentation = [ + "https://docs.radicle.xyz/guides/seeder" + ]; + after = [ + "network.target" + "network-online.target" + ]; + requires = [ + "network-online.target" + ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = mkMerge [ + { + BindReadOnlyPaths = [ + "${cfg.configFile}:${env.RAD_HOME}/config.json" + "${if types.path.check cfg.publicKey then cfg.publicKey else pkgs.writeText "radicle.pub" cfg.publicKey}:${env.RAD_HOME}/keys/radicle.pub" + ]; + KillMode = "process"; + StateDirectory = [ "radicle" ]; + User = config.users.users.radicle.name; + Group = config.users.groups.radicle.name; + WorkingDirectory = env.HOME; + } + # The following options are only for optimizing: + # systemd-analyze security ${serviceName} + { + BindReadOnlyPaths = [ + "-/etc/resolv.conf" + "/etc/ssl/certs/ca-certificates.crt" + "/run/systemd" + ]; + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DeviceAllow = ""; # ProtectClock= adds DeviceAllow=char-rtc r + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RuntimeDirectoryMode = "700"; + SocketBindDeny = [ "any" ]; + StateDirectoryMode = "0750"; + SystemCallFilter = [ + "@system-service" + "~@aio" + "~@chown" + "~@keyring" + "~@memlock" + "~@privileged" + "~@resources" + "~@setuid" + "~@timer" + ]; + SystemCallArchitectures = "native"; + # This is for BindPaths= and BindReadOnlyPaths= + # to allow traversal of directories they create inside RootDirectory= + UMask = "0066"; + } + ]; + confinement = { + enable = true; + mode = "full-apivfs"; + packages = [ + pkgs.gitMinimal + cfg.package + pkgs.iana-etc + (getLib pkgs.nss) + pkgs.tzdata + ]; + }; + }; +in +{ + options = { + services.radicle = { + enable = mkEnableOption "Radicle Seed Node"; + package = mkPackageOption pkgs "radicle-node" { }; + privateKeyFile = mkOption { + # Note that a key encrypted by systemd-creds is not a path but a str. + type = with types; either path str; + description = '' + Absolute file path to an SSH private key, + usually generated by `rad auth`. + + If it contains a colon (`:`) the string before the colon + is taken as the credential name + and the string after as a path encrypted with `systemd-creds`. + ''; + }; + publicKey = mkOption { + type = with types; either path str; + description = '' + An SSH public key (as an absolute file path or directly as a string), + usually generated by `rad auth`. + ''; + }; + node = { + listenAddress = mkOption { + type = types.str; + default = "[::]"; + example = "127.0.0.1"; + description = "The IP address on which `radicle-node` listens."; + }; + listenPort = mkOption { + type = types.port; + default = 8776; + description = "The port on which `radicle-node` listens."; + }; + openFirewall = mkEnableOption "opening the firewall for `radicle-node`"; + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Extra arguments for `radicle-node`"; + }; + }; + configFile = mkOption { + type = types.package; + internal = true; + default = (json.generate "config.json" cfg.settings).overrideAttrs (previousAttrs: { + preferLocalBuild = true; + # None of the usual phases are run here because runCommandWith uses buildCommand, + # so just append to buildCommand what would usually be a checkPhase. + buildCommand = previousAttrs.buildCommand + optionalString cfg.checkConfig '' + ln -s $out config.json + install -D -m 644 /dev/stdin keys/radicle.pub <<<"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBgFMhajUng+Rjj/sCFXI9PzG8BQjru2n7JgUVF1Kbv5 snakeoil" + export RAD_HOME=$PWD + ${getExe' pkgs.buildPackages.radicle-node "rad"} config >/dev/null || { + cat -n config.json + echo "Invalid config.json according to rad." + echo "Please double-check your services.radicle.settings (producing the config.json above)," + echo "some settings may be missing or have the wrong type." + exit 1 + } >&2 + ''; + }); + }; + checkConfig = mkEnableOption "checking the {file}`config.json` file resulting from {option}`services.radicle.settings`" // { default = true; }; + settings = mkOption { + description = '' + See https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5/tree/radicle/src/node/config.rs#L275 + ''; + default = { }; + example = literalExpression '' + { + web.pinned.repositories = [ + "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5" # heartwood + "rad:z3trNYnLWS11cJWC6BbxDs5niGo82" # rips + ]; + } + ''; + type = types.submodule { + freeformType = json.type; + }; + }; + httpd = { + enable = mkEnableOption "Radicle HTTP gateway to radicle-node"; + package = mkPackageOption pkgs "radicle-httpd" { }; + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "The IP address on which `radicle-httpd` listens."; + }; + listenPort = mkOption { + type = types.port; + default = 8080; + description = "The port on which `radicle-httpd` listens."; + }; + nginx = mkOption { + # Type of a single virtual host, or null. + type = types.nullOr (types.submodule ( + recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { + options.serverName = { + default = "radicle-${config.networking.hostName}.${config.networking.domain}"; + defaultText = "radicle-\${config.networking.hostName}.\${config.networking.domain}"; + }; + } + )); + default = null; + example = literalExpression '' + { + serverAliases = [ + "seed.''${config.networking.domain}" + ]; + enableACME = false; + useACMEHost = config.networking.domain; + } + ''; + description = '' + With this option, you can customize an nginx virtual host which already has sensible defaults for `radicle-httpd`. + Set to `{}` if you do not need any customization to the virtual host. + If enabled, then by default, the {option}`serverName` is + `radicle-''${config.networking.hostName}.''${config.networking.domain}`, + TLS is active, and certificates are acquired via ACME. + If this is set to null (the default), no nginx virtual host will be configured. + ''; + }; + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Extra arguments for `radicle-httpd`"; + }; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { + systemd.services.radicle-node = mkMerge [ + (commonServiceConfig "radicle-node") + { + description = "Radicle Node"; + documentation = [ "man:radicle-node(1)" ]; + serviceConfig = { + ExecStart = "${getExe' cfg.package "radicle-node"} --force --listen ${cfg.node.listenAddress}:${toString cfg.node.listenPort} ${escapeShellArgs cfg.node.extraArgs}"; + Restart = mkDefault "on-failure"; + RestartSec = "30"; + SocketBindAllow = [ "tcp:${toString cfg.node.listenPort}" ]; + SystemCallFilter = mkAfter [ + # Needed by git upload-pack which calls alarm() and setitimer() when providing a rad clone + "@timer" + ]; + }; + confinement.packages = [ + cfg.package + ]; + } + # Give only access to the private key to radicle-node. + { + serviceConfig = + let keyCred = builtins.split ":" "${cfg.privateKeyFile}"; in + if length keyCred > 1 + then { + LoadCredentialEncrypted = [ cfg.privateKeyFile ]; + # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths= + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/${head keyCred}:${env.RAD_HOME}/keys/radicle" ]; + } + else { + LoadCredential = [ "radicle:${cfg.privateKeyFile}" ]; + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" ]; + }; + } + ]; + + environment.systemPackages = [ + rad-system + ]; + + networking.firewall = mkIf cfg.node.openFirewall { + allowedTCPPorts = [ cfg.node.listenPort ]; + }; + + users = { + users.radicle = { + description = "Radicle"; + group = "radicle"; + home = env.HOME; + isSystemUser = true; + }; + groups.radicle = { + }; + }; + } + + (mkIf cfg.httpd.enable (mkMerge [ + { + systemd.services.radicle-httpd = mkMerge [ + (commonServiceConfig "radicle-httpd") + { + description = "Radicle HTTP gateway to radicle-node"; + documentation = [ "man:radicle-httpd(1)" ]; + serviceConfig = { + ExecStart = "${getExe' cfg.httpd.package "radicle-httpd"} --listen ${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort} ${escapeShellArgs cfg.httpd.extraArgs}"; + Restart = mkDefault "on-failure"; + RestartSec = "10"; + SocketBindAllow = [ "tcp:${toString cfg.httpd.listenPort}" ]; + SystemCallFilter = mkAfter [ + # Needed by git upload-pack which calls alarm() and setitimer() when providing a git clone + "@timer" + ]; + }; + confinement.packages = [ + cfg.httpd.package + ]; + } + ]; + } + + (mkIf (cfg.httpd.nginx != null) { + services.nginx.virtualHosts.${cfg.httpd.nginx.serverName} = lib.mkMerge [ + cfg.httpd.nginx + { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."/" = { + proxyPass = "http://${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort}"; + recommendedProxySettings = true; + }; + } + ]; + + services.radicle.settings = { + node.alias = mkDefault cfg.httpd.nginx.serverName; + node.externalAddresses = mkDefault [ + "${cfg.httpd.nginx.serverName}:${toString cfg.node.listenPort}" + ]; + }; + }) + ])) + ]); + + meta.maintainers = with lib.maintainers; [ + julm + lorenzleutgeb + ]; +} diff --git a/nixos/modules/services/misc/libreddit.nix b/nixos/modules/services/misc/redlib.nix index c1f6b276ad9fa..0da85df46bf72 100644 --- a/nixos/modules/services/misc/libreddit.nix +++ b/nixos/modules/services/misc/redlib.nix @@ -3,7 +3,7 @@ with lib; let - cfg = config.services.libreddit; + cfg = config.services.redlib; args = concatStringsSep " " ([ "--port ${toString cfg.port}" @@ -11,11 +11,15 @@ let ]); in { + imports = [ + (mkRenamedOptionModule [ "services" "libreddit" ] [ "services" "redlib" ]) + ]; + options = { - services.libreddit = { + services.redlib = { enable = mkEnableOption "Private front-end for Reddit"; - package = mkPackageOption pkgs "libreddit" { }; + package = mkPackageOption pkgs "redlib" { }; address = mkOption { default = "0.0.0.0"; @@ -34,14 +38,14 @@ in openFirewall = mkOption { type = types.bool; default = false; - description = "Open ports in the firewall for the libreddit web interface"; + description = "Open ports in the firewall for the redlib web interface"; }; }; }; config = mkIf cfg.enable { - systemd.services.libreddit = { + systemd.services.redlib = { description = "Private front-end for Reddit"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; diff --git a/nixos/modules/services/misc/renovate.nix b/nixos/modules/services/misc/renovate.nix index 25a719c91cbd8..9062b7424b681 100644 --- a/nixos/modules/services/misc/renovate.nix +++ b/nixos/modules/services/misc/renovate.nix @@ -128,6 +128,7 @@ in RestrictAddressFamilies = [ "AF_INET" "AF_INET6" + "AF_UNIX" ]; RestrictNamespaces = true; RestrictRealtime = true; diff --git a/nixos/modules/services/misc/rkvm.nix b/nixos/modules/services/misc/rkvm.nix index 9d41669e00f61..ac747253635e8 100644 --- a/nixos/modules/services/misc/rkvm.nix +++ b/nixos/modules/services/misc/rkvm.nix @@ -7,7 +7,7 @@ let toml = pkgs.formats.toml { }; in { - meta.maintainers = with maintainers; [ ckie ]; + meta.maintainers = [ ]; options.services.rkvm = { enable = mkOption { diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix index 1b16ef7958ad2..fc57683de3280 100644 --- a/nixos/modules/services/misc/snapper.nix +++ b/nixos/modules/services/misc/snapper.nix @@ -96,48 +96,48 @@ let }; TIMELINE_LIMIT_HOURLY = mkOption { - type = types.str; - default = "10"; + type = types.int; + default = 10; description = '' Limits for timeline cleanup. ''; }; TIMELINE_LIMIT_DAILY = mkOption { - type = types.str; - default = "10"; + type = types.int; + default = 10; description = '' Limits for timeline cleanup. ''; }; TIMELINE_LIMIT_WEEKLY = mkOption { - type = types.str; - default = "0"; + type = types.int; + default = 0; description = '' Limits for timeline cleanup. ''; }; TIMELINE_LIMIT_MONTHLY = mkOption { - type = types.str; - default = "10"; + type = types.int; + default = 10; description = '' Limits for timeline cleanup. ''; }; TIMELINE_LIMIT_QUARTERLY = mkOption { - type = types.str; - default = "0"; + type = types.int; + default = 0; description = '' Limits for timeline cleanup. ''; }; TIMELINE_LIMIT_YEARLY = mkOption { - type = types.str; - default = "10"; + type = types.int; + default = 10; description = '' Limits for timeline cleanup. ''; @@ -353,4 +353,6 @@ in ) (attrNames cfg.configs); } ); + + meta.maintainers = with lib.maintainers; [ Djabx ]; } diff --git a/nixos/modules/services/misc/sonarr.nix b/nixos/modules/services/misc/sonarr.nix index 228a2d48f5a9c..60e73198d60de 100644 --- a/nixos/modules/services/misc/sonarr.nix +++ b/nixos/modules/services/misc/sonarr.nix @@ -1,4 +1,4 @@ -{ config, pkgs, lib, ... }: +{ config, pkgs, lib, utils, ... }: with lib; @@ -54,7 +54,11 @@ in Type = "simple"; User = cfg.user; Group = cfg.group; - ExecStart = "${cfg.package}/bin/NzbDrone -nobrowser -data='${cfg.dataDir}'"; + ExecStart = utils.escapeSystemdExecArgs [ + (lib.getExe cfg.package) + "-nobrowser" + "-data=${cfg.dataDir}" + ]; Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/misc/sssd.nix b/nixos/modules/services/misc/sssd.nix index 4429b20174d94..f2798c1f30b45 100644 --- a/nixos/modules/services/misc/sssd.nix +++ b/nixos/modules/services/misc/sssd.nix @@ -145,7 +145,7 @@ in { # https://github.com/krb5/krb5/blob/krb5-1.19.3-final/src/include/kcm.h#L43 listenStreams = [ "/var/run/.heim_org.h5l.kcm-socket" ]; }; - krb5.libdefaults.default_ccache_name = "KCM:"; + security.krb5.settings.libdefaults.default_ccache_name = "KCM:"; }) (mkIf cfg.sshAuthorizedKeysIntegration { diff --git a/nixos/modules/services/misc/xmr-stak.nix b/nixos/modules/services/misc/xmr-stak.nix deleted file mode 100644 index 3015e3cb12a87..0000000000000 --- a/nixos/modules/services/misc/xmr-stak.nix +++ /dev/null @@ -1,89 +0,0 @@ -{ lib, config, pkgs, ... }: - -with lib; - -let - - cfg = config.services.xmr-stak; - - pkg = pkgs.xmr-stak.override { - inherit (cfg) openclSupport; - }; - -in - -{ - options = { - services.xmr-stak = { - enable = mkEnableOption "xmr-stak miner"; - openclSupport = mkEnableOption "support for OpenCL (AMD/ATI graphics cards)"; - - extraArgs = mkOption { - type = types.listOf types.str; - default = []; - example = [ "--noCPU" "--currency monero" ]; - description = "List of parameters to pass to xmr-stak."; - }; - - configFiles = mkOption { - type = types.attrsOf types.str; - default = {}; - example = literalExpression '' - { - "config.txt" = ''' - "verbose_level" : 4, - "h_print_time" : 60, - "tls_secure_algo" : true, - '''; - "pools.txt" = ''' - "currency" : "monero7", - "pool_list" : - [ { "pool_address" : "pool.supportxmr.com:443", - "wallet_address" : "my-wallet-address", - "rig_id" : "", - "pool_password" : "nixos", - "use_nicehash" : false, - "use_tls" : true, - "tls_fingerprint" : "", - "pool_weight" : 23 - }, - ], - '''; - } - ''; - description = '' - Content of config files like config.txt, pools.txt or cpu.txt. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - systemd.services.xmr-stak = { - wantedBy = [ "multi-user.target" ]; - bindsTo = [ "network-online.target" ]; - after = [ "network-online.target" ]; - - preStart = concatStrings (flip mapAttrsToList cfg.configFiles (fn: content: '' - ln -sf '${pkgs.writeText "xmr-stak-${fn}" content}' '${fn}' - '')); - - serviceConfig = let rootRequired = cfg.openclSupport; in { - ExecStart = "${pkg}/bin/xmr-stak ${concatStringsSep " " cfg.extraArgs}"; - # xmr-stak generates cpu and/or gpu configuration files - WorkingDirectory = "/tmp"; - PrivateTmp = true; - DynamicUser = !rootRequired; - LimitMEMLOCK = toString (1024*1024); - }; - }; - }; - - imports = [ - (mkRemovedOptionModule ["services" "xmr-stak" "configText"] '' - This option was removed in favour of `services.xmr-stak.configFiles` - because the new config file `pools.txt` was introduced. You are - now able to define all other config files like cpu.txt or amd.txt. - '') - ]; -} diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix index d09cd87febfff..5b0b1448f6856 100644 --- a/nixos/modules/services/misc/zoneminder.nix +++ b/nixos/modules/services/misc/zoneminder.nix @@ -202,10 +202,11 @@ in { ]; services = { - fcgiwrap = lib.mkIf useNginx { - enable = true; - preforkProcesses = cfg.cameras; - inherit user group; + fcgiwrap.instances.zoneminder = lib.mkIf useNginx { + process.prefork = cfg.cameras; + process.user = user; + process.group = group; + socket = { inherit (config.services.nginx) user group; }; }; mysql = lib.mkIf cfg.database.createLocally { @@ -225,9 +226,7 @@ in { default = true; root = "${pkg}/share/zoneminder/www"; listen = [ { addr = "0.0.0.0"; inherit (cfg) port; } ]; - extraConfig = let - fcgi = config.services.fcgiwrap; - in '' + extraConfig = '' index index.php; location / { @@ -257,7 +256,7 @@ in { fastcgi_param HTTP_PROXY ""; fastcgi_intercept_errors on; - fastcgi_pass ${fcgi.socketType}:${fcgi.socketAddress}; + fastcgi_pass unix:${config.services.fcgiwrap.instances.zoneminder.socket.address}; } location /cache/ { @@ -374,5 +373,5 @@ in { }; }; - meta.maintainers = with lib.maintainers; [ ]; + meta.maintainers = [ ]; } |