diff options
Diffstat (limited to 'nixos/modules')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/audio/navidrome.nix | 160 | ||||
-rw-r--r-- | nixos/modules/services/cluster/k3s/default.nix | 44 | ||||
-rw-r--r-- | nixos/modules/services/misc/private-gpt.nix | 121 | ||||
-rw-r--r-- | nixos/modules/services/networking/pixiecore.nix | 4 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/pretalx.nix | 49 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/pretix.nix | 12 | ||||
-rw-r--r-- | nixos/modules/services/web-servers/nginx/default.nix | 2 | ||||
-rw-r--r-- | nixos/modules/testing/test-instrumentation.nix | 2 |
9 files changed, 313 insertions, 82 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 76ccf5a341094..3cbb4617517aa 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -776,6 +776,7 @@ ./services/misc/polaris.nix ./services/misc/portunus.nix ./services/misc/preload.nix + ./services/misc/private-gpt.nix ./services/misc/prowlarr.nix ./services/misc/pufferpanel.nix ./services/misc/pykms.nix diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix index a5a7e805e3d61..ca1cd6ca43af0 100644 --- a/nixos/modules/services/audio/navidrome.nix +++ b/nixos/modules/services/audio/navidrome.nix @@ -1,11 +1,17 @@ -{ config, lib, pkgs, ... }: - -with lib; +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib) mkEnableOption mkPackageOption mkOption maintainers; + inherit (lib.types) bool str; cfg = config.services.navidrome; - settingsFormat = pkgs.formats.json {}; -in { + settingsFormat = pkgs.formats.json { }; +in +{ options = { services.navidrome = { @@ -13,9 +19,8 @@ in { package = mkPackageOption pkgs "navidrome" { }; - settings = mkOption rec { + settings = mkOption { type = settingsFormat.type; - apply = recursiveUpdate default; default = { Address = "127.0.0.1"; Port = 4533; @@ -23,62 +28,111 @@ in { example = { MusicFolder = "/mnt/music"; }; - description = '' - Configuration for Navidrome, see <https://www.navidrome.org/docs/usage/configuration-options/> for supported values. - ''; + description = "Configuration for Navidrome, see <https://www.navidrome.org/docs/usage/configuration-options/> for supported values."; + }; + + user = mkOption { + type = str; + default = "navidrome"; + description = "User under which Navidrome runs."; + }; + + group = mkOption { + type = str; + default = "navidrome"; + description = "Group under which Navidrome runs."; }; openFirewall = mkOption { - type = types.bool; + type = bool; default = false; description = "Whether to open the TCP port in the firewall"; }; }; }; - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.settings.Port]; + config = + let + inherit (lib) mkIf optional getExe; + WorkingDirectory = "/var/lib/navidrome"; + in + mkIf cfg.enable { + systemd = { + tmpfiles.settings.navidromeDirs = { + "${cfg.settings.DataFolder or WorkingDirectory}"."d" = { + mode = "700"; + inherit (cfg) user group; + }; + "${cfg.settings.CacheFolder or (WorkingDirectory + "/cache")}"."d" = { + mode = "700"; + inherit (cfg) user group; + }; + }; + services.navidrome = { + description = "Navidrome Media Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = '' + ${getExe cfg.package} --configfile ${settingsFormat.generate "navidrome.json" cfg.settings} + ''; + User = cfg.user; + Group = cfg.group; + StateDirectory = "navidrome"; + inherit WorkingDirectory; + RuntimeDirectory = "navidrome"; + RootDirectory = "/run/navidrome"; + ReadWritePaths = ""; + BindPaths = + optional (cfg.settings ? DataFolder) cfg.settings.DataFolder + ++ optional (cfg.settings ? CacheFolder) cfg.settings.CacheFolder; + BindReadOnlyPaths = [ + # navidrome uses online services to download additional album metadata / covers + "${ + config.environment.etc."ssl/certs/ca-certificates.crt".source + }:/etc/ssl/certs/ca-certificates.crt" + builtins.storeDir + "/etc" + ] ++ optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder; + 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" + ]; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + UMask = "0066"; + ProtectHostname = true; + }; + }; + }; - systemd.services.navidrome = { - description = "Navidrome Media Server"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = '' - ${cfg.package}/bin/navidrome --configfile ${settingsFormat.generate "navidrome.json" cfg.settings} - ''; - DynamicUser = true; - StateDirectory = "navidrome"; - WorkingDirectory = "/var/lib/navidrome"; - RuntimeDirectory = "navidrome"; - RootDirectory = "/run/navidrome"; - ReadWritePaths = ""; - BindPaths = lib.optional (cfg.settings ? DataFolder) cfg.settings.DataFolder; - BindReadOnlyPaths = [ - # navidrome uses online services to download additional album metadata / covers - "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt" - builtins.storeDir - "/etc" - ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder; - 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" ]; - RestrictRealtime = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - UMask = "0066"; - ProtectHostname = true; + users.users = mkIf (cfg.user == "navidrome") { + navidrome = { + inherit (cfg) group; + isSystemUser = true; + }; }; + + users.groups = mkIf (cfg.group == "navidrome") { navidrome = { }; }; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.Port ]; }; - }; + meta.maintainers = with maintainers; [ nu-nu-ko ]; } diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix index 040cf7640de16..4d18d378d7944 100644 --- a/nixos/modules/services/cluster/k3s/default.nix +++ b/nixos/modules/services/cluster/k3s/default.nix @@ -1,15 +1,25 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: with lib; let cfg = config.services.k3s; - removeOption = config: instruction: - lib.mkRemovedOptionModule ([ "services" "k3s" ] ++ config) instruction; + removeOption = + config: instruction: + lib.mkRemovedOptionModule ( + [ + "services" + "k3s" + ] + ++ config + ) instruction; in { - imports = [ - (removeOption [ "docker" ] "k3s docker option is no longer supported.") - ]; + imports = [ (removeOption [ "docker" ] "k3s docker option is no longer supported.") ]; # interface options.services.k3s = { @@ -33,7 +43,10 @@ in - `serverAddr` is required. ''; default = "server"; - type = types.enum [ "server" "agent" ]; + type = types.enum [ + "server" + "agent" + ]; }; serverAddr = mkOption { @@ -125,7 +138,8 @@ in message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'"; } { - assertion = cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != ""; + assertion = + cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != ""; message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'"; } { @@ -142,8 +156,14 @@ in systemd.services.k3s = { description = "k3s service"; - after = [ "firewall.service" "network-online.target" ]; - wants = [ "firewall.service" "network-online.target" ]; + after = [ + "firewall.service" + "network-online.target" + ]; + wants = [ + "firewall.service" + "network-online.target" + ]; wantedBy = [ "multi-user.target" ]; path = optional config.boot.zfs.enabled config.boot.zfs.package; serviceConfig = { @@ -159,9 +179,7 @@ in TasksMax = "infinity"; EnvironmentFile = cfg.environmentFile; ExecStart = concatStringsSep " \\\n " ( - [ - "${cfg.package}/bin/k3s ${cfg.role}" - ] + [ "${cfg.package}/bin/k3s ${cfg.role}" ] ++ (optional cfg.clusterInit "--cluster-init") ++ (optional cfg.disableAgent "--disable-agent") ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}") diff --git a/nixos/modules/services/misc/private-gpt.nix b/nixos/modules/services/misc/private-gpt.nix new file mode 100644 index 0000000000000..9a3e5317cdb14 --- /dev/null +++ b/nixos/modules/services/misc/private-gpt.nix @@ -0,0 +1,121 @@ +{ config +, lib +, pkgs +, ... +}: +let + inherit (lib) types; + + format = pkgs.formats.yaml { }; + cfg = config.services.private-gpt; +in +{ + options = { + services.private-gpt = { + enable = lib.mkEnableOption "private-gpt for local large language models"; + package = lib.mkPackageOption pkgs "private-gpt" { }; + + stateDir = lib.mkOption { + type = types.path; + default = "/var/lib/private-gpt"; + description = "State directory of private-gpt."; + }; + + settings = lib.mkOption { + type = format.type; + default = { + llm = { + mode = "ollama"; + tokenizer = ""; + }; + embedding = { + mode = "ollama"; + }; + ollama = { + llm_model = "llama3"; + embedding_model = "nomic-embed-text"; + api_base = "http://localhost:11434"; + embedding_api_base = "http://localhost:11434"; + keep_alive = "5m"; + tfs_z = 1; + top_k = 40; + top_p = 0.9; + repeat_last_n = 64; + repeat_penalty = 1.2; + request_timeout = 120; + }; + vectorstore = { + database = "qdrant"; + }; + qdrant = { + path = "/var/lib/private-gpt/vectorstore/qdrant"; + }; + data = { + local_data_folder = "/var/lib/private-gpt"; + }; + openai = { }; + azopenai = { }; + }; + description = '' + settings-local.yaml for private-gpt + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.private-gpt = { + description = "Interact with your documents using the power of GPT, 100% privately, no data leaks"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = + let + config = format.generate "settings-local.yaml" (cfg.settings // { server.env_name = "local"; }); + in + '' + mkdir -p ${cfg.stateDir}/{settings,huggingface,matplotlib,tiktoken_cache} + cp ${cfg.package.cl100k_base.tiktoken} ${cfg.stateDir}/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 + cp ${pkgs.python3Packages.private-gpt}/${pkgs.python3.sitePackages}/private_gpt/settings.yaml ${cfg.stateDir}/settings/settings.yaml + cp "${config}" "${cfg.stateDir}/settings/settings-local.yaml" + chmod 600 "${cfg.stateDir}/settings/settings-local.yaml" + ''; + + environment = { + PGPT_PROFILES = "local"; + PGPT_SETTINGS_FOLDER = "${cfg.stateDir}/settings"; + HF_HOME = "${cfg.stateDir}/huggingface"; + TRANSFORMERS_OFFLINE = "1"; + HF_DATASETS_OFFLINE = "1"; + MPLCONFIGDIR = "${cfg.stateDir}/matplotlib"; + }; + + serviceConfig = { + ExecStart = lib.getExe cfg.package; + WorkingDirectory = cfg.stateDir; + StateDirectory = "private-gpt"; + RuntimeDirectory = "private-gpt"; + RuntimeDirectoryMode = "0755"; + PrivateTmp = true; + DynamicUser = true; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateUsers = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProcSubset = "pid"; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ drupol ]; +} diff --git a/nixos/modules/services/networking/pixiecore.nix b/nixos/modules/services/networking/pixiecore.nix index cfdb8014136ed..111cb7e355040 100644 --- a/nixos/modules/services/networking/pixiecore.nix +++ b/nixos/modules/services/networking/pixiecore.nix @@ -82,8 +82,8 @@ in apiServer = mkOption { type = types.str; - example = "localhost:8080"; - description = "host:port to connect to the API. Ignored unless mode is set to 'api'"; + example = "http://localhost:8080"; + description = "URI to connect to the API. Ignored unless mode is set to 'api'"; }; extraArguments = mkOption { diff --git a/nixos/modules/services/web-apps/pretalx.nix b/nixos/modules/services/web-apps/pretalx.nix index b062a8b7eeeac..d0b1512f77c59 100644 --- a/nixos/modules/services/web-apps/pretalx.nix +++ b/nixos/modules/services/web-apps/pretalx.nix @@ -24,7 +24,7 @@ in { meta = with lib; { - maintainers = teams.c3d2.members; + maintainers = with maintainers; [ hexa] ++ teams.c3d2.members; }; options.services.pretalx = { @@ -329,10 +329,47 @@ in serviceConfig = { User = "pretalx"; Group = "pretalx"; - StateDirectory = [ "pretalx" "pretalx/media" ]; + StateDirectory = [ + "pretalx" + "pretalx/media" + ]; + StateDirectoryMode = "0750"; LogsDirectory = "pretalx"; WorkingDirectory = cfg.settings.filesystem.data; SupplementaryGroups = [ "redis-pretalx" ]; + AmbientCapabilities = ""; + CapabilityBoundingSet = [ "" ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "@chown" + ]; + UMask = "0027"; }; }; in { @@ -395,6 +432,8 @@ in wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${lib.getExe' pythonEnv "celery"} -A pretalx.celery_app worker ${cfg.celery.extraArgs}"; }); + + nginx.serviceConfig.SupplementaryGroups = lib.mkIf cfg.nginx.enable [ "pretalx" ]; }; systemd.sockets.pretalx-web.socketConfig = { @@ -403,11 +442,9 @@ in }; users = { - groups."${cfg.group}" = {}; - users."${cfg.user}" = { + groups.${cfg.group} = {}; + users.${cfg.user} = { isSystemUser = true; - createHome = true; - home = cfg.settings.filesystem.data; inherit (cfg) group; }; }; diff --git a/nixos/modules/services/web-apps/pretix.nix b/nixos/modules/services/web-apps/pretix.nix index 22ee9769aa923..498face7456db 100644 --- a/nixos/modules/services/web-apps/pretix.nix +++ b/nixos/modules/services/web-apps/pretix.nix @@ -468,7 +468,7 @@ in StateDirectory = [ "pretix" ]; - StateDirectoryMode = "0755"; + StateDirectoryMode = "0750"; CacheDirectory = "pretix"; LogsDirectory = "pretix"; WorkingDirectory = cfg.settings.pretix.datadir; @@ -507,7 +507,7 @@ in "~@privileged" "@chown" ]; - UMask = "0022"; + UMask = "0027"; }; }; in { @@ -561,6 +561,8 @@ in wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${getExe' pythonEnv "celery"} -A pretix.celery_app worker ${cfg.celery.extraArgs}"; }; + + nginx.serviceConfig.SupplementaryGroups = mkIf cfg.nginx.enable [ "pretix" ]; }; systemd.sockets.pretix-web.socketConfig = { @@ -569,11 +571,9 @@ in }; users = { - groups."${cfg.group}" = {}; - users."${cfg.user}" = { + groups.${cfg.group} = {}; + users.${cfg.user} = { isSystemUser = true; - createHome = true; - home = cfg.settings.pretix.datadir; inherit (cfg) group; }; }; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 08fab09e1e559..fd940cfe459ab 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -352,7 +352,7 @@ let # The acme-challenge location doesn't need to be added if we are not using any automated # certificate provisioning and can also be omitted when we use a certificate obtained via a DNS-01 challenge - acmeName = if vhost.useACMEHost != null then vhost.useACMEHost else vhostName; + acmeName = if vhost.useACMEHost != null then vhost.useACMEHost else vhost.serverName; acmeLocation = optionalString ((vhost.enableACME || vhost.useACMEHost != null) && config.security.acme.certs.${acmeName}.dnsProvider == null) # Rule for legitimate ACME Challenge requests (like /.well-known/acme-challenge/xxxxxxxxx) # We use ^~ here, so that we don't check any regexes (which could diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index dd8816063c70c..2b365bc555855 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -218,7 +218,7 @@ in services.displayManager.logToJournal = true; - services.logrotate.enable = lib.mkDefault false; + services.logrotate.enable = mkOverride 150 false; # Make sure we use the Guest Agent from the QEMU package for testing # to reduce the closure size required for the tests. |