diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services')
118 files changed, 3584 insertions, 1018 deletions
diff --git a/nixpkgs/nixos/modules/services/admin/docuum.nix b/nixpkgs/nixos/modules/services/admin/docuum.nix index 6f6cd4e02733..51a21740b276 100644 --- a/nixpkgs/nixos/modules/services/admin/docuum.nix +++ b/nixpkgs/nixos/modules/services/admin/docuum.nix @@ -2,7 +2,7 @@ let cfg = config.services.docuum; - inherit (lib) mkIf mkEnableOption mkOption getExe types; + inherit (lib) mkIf mkEnableOption mkOption getExe types optionals concatMap; in { options.services.docuum = { @@ -14,6 +14,27 @@ in default = "10 GB"; example = "50%"; }; + + minAge = mkOption { + description = "Sets the minimum age of images to be considered for deletion."; + type = types.nullOr types.str; + default = null; + example = "1d"; + }; + + keep = mkOption { + description = "Prevents deletion of images for which repository:tag matches the specified regex."; + type = types.listOf types.str; + default = []; + example = [ "^my-image" ]; + }; + + deletionChunkSize = mkOption { + description = "Removes specified quantity of images at a time."; + type = types.int; + default = 1; + example = 10; + }; }; config = mkIf cfg.enable { @@ -35,10 +56,13 @@ in DynamicUser = true; StateDirectory = "docuum"; SupplementaryGroups = [ "docker" ]; - ExecStart = utils.escapeSystemdExecArgs [ + ExecStart = utils.escapeSystemdExecArgs ([ (getExe pkgs.docuum) "--threshold" cfg.threshold - ]; + "--deletion-chunk-size" cfg.deletionChunkSize + ] ++ (concatMap (keep: [ "--keep" keep ]) cfg.keep) + ++ (optionals (cfg.minAge != null) [ "--min-age" cfg.minAge ]) + ); }; }; }; diff --git a/nixpkgs/nixos/modules/services/audio/alsa.nix b/nixpkgs/nixos/modules/services/audio/alsa.nix index e53da4b64e7b..b002cb1274ac 100644 --- a/nixpkgs/nixos/modules/services/audio/alsa.nix +++ b/nixpkgs/nixos/modules/services/audio/alsa.nix @@ -106,7 +106,8 @@ in serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - ExecStart = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa"; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa"; + ExecStart = "${alsa-utils}/sbin/alsactl restore --ignore"; ExecStop = "${alsa-utils}/sbin/alsactl store --ignore"; }; }; diff --git a/nixpkgs/nixos/modules/services/audio/mopidy.nix b/nixpkgs/nixos/modules/services/audio/mopidy.nix index 1d6c45b64a16..198ca74359dc 100644 --- a/nixpkgs/nixos/modules/services/audio/mopidy.nix +++ b/nixpkgs/nixos/modules/services/audio/mopidy.nix @@ -78,6 +78,7 @@ in { systemd.services.mopidy = { wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" "sound.target" ]; + wants = [ "network-online.target" ]; description = "mopidy music player daemon"; serviceConfig = { ExecStart = "${mopidyEnv}/bin/mopidy --config ${concatStringsSep ":" ([mopidyConf] ++ cfg.extraConfigFiles)}"; diff --git a/nixpkgs/nixos/modules/services/audio/navidrome.nix b/nixpkgs/nixos/modules/services/audio/navidrome.nix index ca1cd6ca43af..06d2d174a4df 100644 --- a/nixpkgs/nixos/modules/services/audio/navidrome.nix +++ b/nixpkgs/nixos/modules/services/audio/navidrome.nix @@ -6,8 +6,18 @@ }: let - inherit (lib) mkEnableOption mkPackageOption mkOption maintainers; - inherit (lib.types) bool str; + inherit (lib) + mkEnableOption + mkPackageOption + mkOption + maintainers + ; + inherit (lib.types) + bool + port + str + submodule + ; cfg = config.services.navidrome; settingsFormat = pkgs.formats.json { }; in @@ -20,11 +30,24 @@ in package = mkPackageOption pkgs "navidrome" { }; settings = mkOption { - type = settingsFormat.type; - default = { - Address = "127.0.0.1"; - Port = 4533; + type = submodule { + freeformType = settingsFormat.type; + + options = { + Address = mkOption { + default = "127.0.0.1"; + description = "Address to run Navidrome on."; + type = str; + }; + + Port = mkOption { + default = 4533; + description = "Port to run Navidrome on."; + type = port; + }; + }; }; + default = { }; example = { MusicFolder = "/mnt/music"; }; @@ -134,5 +157,5 @@ in networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.Port ]; }; - meta.maintainers = with maintainers; [ nu-nu-ko ]; + meta.maintainers = with maintainers; [ fsnkty ]; } diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix index 04f971008073..a3c0715c9e60 100644 --- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix +++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix @@ -361,7 +361,7 @@ in { type = types.bool; example = true; description = '' - Set the `persistentTimer` option for the + Set the `Persistent` option for the {manpage}`systemd.timer(5)` which triggers the backup immediately if the last trigger was missed (e.g. if the system was powered down). diff --git a/nixpkgs/nixos/modules/services/cluster/rke2/default.nix b/nixpkgs/nixos/modules/services/cluster/rke2/default.nix new file mode 100644 index 000000000000..9ddbd299fdf8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/cluster/rke2/default.nix @@ -0,0 +1,311 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.rke2; +in +{ + imports = [ ]; + + options.services.rke2 = { + enable = mkEnableOption "rke2"; + + package = mkPackageOption pkgs "rke2" { }; + + role = mkOption { + type = types.enum [ "server" "agent" ]; + description = '' + Whether rke2 should run as a server or agent. + + If it's a server: + + - By default it also runs workloads as an agent. + - any optionals is allowed. + + If it's an agent: + + - `serverAddr` is required. + - `token` or `tokenFile` is required. + - `agentToken` or `agentTokenFile` or `disable` or `cni` are not allowed. + ''; + default = "server"; + }; + + configPath = mkOption { + type = types.path; + description = "Load configuration from FILE."; + default = "/etc/rancher/rke2/config.yaml"; + }; + + debug = mkOption { + type = types.bool; + description = "Turn on debug logs."; + default = false; + }; + + dataDir = mkOption { + type = types.path; + description = "The folder to hold state in."; + default = "/var/lib/rancher/rke2"; + }; + + token = mkOption { + type = types.str; + description = '' + Shared secret used to join a server or agent to a cluster. + + > WARNING: This option will expose store your token unencrypted world-readable in the nix store. + If this is undesired use the `tokenFile` option instead. + ''; + default = ""; + }; + + tokenFile = mkOption { + type = types.nullOr types.path; + description = "File path containing rke2 token to use when connecting to the server."; + default = null; + }; + + disable = mkOption { + type = types.listOf types.str; + description = "Do not deploy packaged components and delete any deployed components."; + default = [ ]; + }; + + nodeName = mkOption { + type = types.nullOr types.str; + description = "Node name."; + default = null; + }; + + nodeLabel = mkOption { + type = types.listOf types.str; + description = "Registering and starting kubelet with set of labels."; + default = [ ]; + }; + + nodeTaint = mkOption { + type = types.listOf types.str; + description = "Registering kubelet with set of taints."; + default = [ ]; + }; + + nodeIP = mkOption { + type = types.nullOr types.str; + description = "IPv4/IPv6 addresses to advertise for node."; + default = null; + }; + + agentToken = mkOption { + type = types.str; + description = '' + Shared secret used to join agents to the cluster, but not servers. + + > **WARNING**: This option will expose store your token unencrypted world-readable in the nix store. + If this is undesired use the `agentTokenFile` option instead. + ''; + default = ""; + }; + + agentTokenFile = mkOption { + type = types.nullOr types.path; + description = "File path containing rke2 agent token to use when connecting to the server."; + default = null; + }; + + serverAddr = mkOption { + type = types.str; + description = "The rke2 server to connect to, used to join a cluster."; + example = "https://10.0.0.10:6443"; + default = ""; + }; + + selinux = mkOption { + type = types.bool; + description = "Enable SELinux in containerd."; + default = false; + }; + + cni = mkOption { + type = types.enum [ "none" "canal" "cilium" "calico" "flannel" ]; + description = '' + CNI Plugins to deploy, one of `none`, `calico`, `canal`, `cilium` or `flannel`. + + All CNI plugins get installed via a helm chart after the main components are up and running + and can be [customized by modifying the helm chart options](https://docs.rke2.io/helm). + + [Learn more about RKE2 and CNI plugins](https://docs.rke2.io/networking/basic_network_options) + + > **WARNING**: Flannel support in RKE2 is currently experimental. + ''; + default = "canal"; + }; + + cisHardening = mkOption { + type = types.bool; + description = '' + Enable CIS Hardening for RKE2. + + It will set the configurations and controls required to address Kubernetes benchmark controls + from the Center for Internet Security (CIS). + + Learn more about [CIS Hardening for RKE2](https://docs.rke2.io/security/hardening_guide). + + > **NOTICE**: + > + > You may need restart the `systemd-sysctl` muaually by: + > + > ```shell + > sudo systemctl restart systemd-sysctl + > ``` + ''; + default = false; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + description = '' + Extra flags to pass to the rke2 service/agent. + + Here you can find all the available flags: + + - [Server Configuration Reference](https://docs.rke2.io/reference/server_config) + - [Agent Configuration Reference](https://docs.rke2.io/reference/linux_agent_config) + ''; + example = [ "--disable-kube-proxy" "--cluster-cidr=10.24.0.0/16" ]; + default = [ ]; + }; + + environmentVars = mkOption { + type = types.attrsOf types.str; + description = '' + Environment variables for configuring the rke2 service/agent. + + Here you can find all the available environment variables: + + - [Server Configuration Reference](https://docs.rke2.io/reference/server_config) + - [Agent Configuration Reference](https://docs.rke2.io/reference/linux_agent_config) + + Besides the options above, you can also active environment variables by edit/create those files: + + - `/etc/default/rke2` + - `/etc/sysconfig/rke2` + - `/usr/local/lib/systemd/system/rke2.env` + ''; + # See: https://github.com/rancher/rke2/blob/master/bundle/lib/systemd/system/rke2-server.env#L1 + default = { + HOME = "/root"; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.role == "agent" -> (builtins.pathExists cfg.configPath || cfg.serverAddr != ""); + message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'"; + } + { + assertion = cfg.role == "agent" -> (builtins.pathExists cfg.configPath || cfg.tokenFile != null || cfg.token != ""); + message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'"; + } + { + assertion = cfg.role == "agent" -> ! (cfg.agentTokenFile != null || cfg.agentToken != ""); + message = "agentToken or agentTokenFile should be set if role is 'agent'"; + } + { + assertion = cfg.role == "agent" -> ! (cfg.disable != [ ]); + message = "disable should not be set if role is 'agent'"; + } + { + assertion = cfg.role == "agent" -> ! (cfg.cni != "canal"); + message = "cni should not be set if role is 'agent'"; + } + ]; + + environment.systemPackages = [ config.services.rke2.package ]; + # To configure NetworkManager to ignore calico/flannel related network interfaces. + # See: https://docs.rke2.io/known_issues#networkmanager + environment.etc."NetworkManager/conf.d/rke2-canal.conf" = { + enable = config.networking.networkmanager.enable; + text = '' + [keyfile] + unmanaged-devices=interface-name:cali*;interface-name:flannel* + ''; + }; + # See: https://docs.rke2.io/security/hardening_guide#set-kernel-parameters + boot.kernel.sysctl = mkIf cfg.cisHardening { + "vm.panic_on_oom" = 0; + "vm.overcommit_memory" = 1; + "kernel.panic" = 10; + "kernel.panic_on_oops" = 1; + }; + + systemd.services.rke2 = { + description = "Rancher Kubernetes Engine v2"; + documentation = [ "https://github.com/rancher/rke2#readme" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = if cfg.role == "agent" then "exec" else "notify"; + EnvironmentFile = [ + "-/etc/default/%N" + "-/etc/sysconfig/%N" + "-/usr/local/lib/systemd/system/%N.env" + ]; + Environment = mapAttrsToList (k: v: "${k}=${v}") cfg.environmentVars; + KillMode = "process"; + Delegate = "yes"; + LimitNOFILE = 1048576; + LimitNPROC = "infinity"; + LimitCORE = "infinity"; + TasksMax = "infinity"; + TimeoutStartSec = 0; + Restart = "always"; + RestartSec = "5s"; + ExecStartPre = [ + # There is a conflict between RKE2 and `nm-cloud-setup.service`. This service add a routing table that + # interfere with the CNI plugin's configuration. This script checks if the service is enabled and if so, + # failed the RKE2 start. + # See: https://github.com/rancher/rke2/issues/1053 + (pkgs.writeScript "check-nm-cloud-setup.sh" '' + #! ${pkgs.runtimeShell} + set -x + ! /run/current-system/systemd/bin/systemctl is-enabled --quiet nm-cloud-setup.service + '') + "-${pkgs.kmod}/bin/modprobe br_netfilter" + "-${pkgs.kmod}/bin/modprobe overlay" + ]; + ExecStart = "${cfg.package}/bin/rke2 '${cfg.role}' ${escapeShellArgs ( + (optional (cfg.configPath != "/etc/rancher/rke2/config.yaml") "--config=${cfg.configPath}") + ++ (optional cfg.debug "--debug") + ++ (optional (cfg.dataDir != "/var/lib/rancher/rke2") "--data-dir=${cfg.dataDir}") + ++ (optional (cfg.token != "") "--token=${cfg.token}") + ++ (optional (cfg.tokenFile != null) "--token-file=${cfg.tokenFile}") + ++ (optionals (cfg.role == "server" && cfg.disable != [ ]) (map (d: "--disable=${d}") cfg.disable)) + ++ (optional (cfg.nodeName != null) "--node-name=${cfg.nodeName}") + ++ (optionals (cfg.nodeLabel != [ ]) (map (l: "--node-label=${l}") cfg.nodeLabel)) + ++ (optionals (cfg.nodeTaint != [ ]) (map (t: "--node-taint=${t}") cfg.nodeTaint)) + ++ (optional (cfg.nodeIP != null) "--node-ip=${cfg.nodeIP}") + ++ (optional (cfg.role == "server" && cfg.agentToken != "") "--agent-token=${cfg.agentToken}") + ++ (optional (cfg.role == "server" && cfg.agentTokenFile != null) "--agent-token-file=${cfg.agentTokenFile}") + ++ (optional (cfg.serverAddr != "") "--server=${cfg.serverAddr}") + ++ (optional cfg.selinux "--selinux") + ++ (optional (cfg.role == "server" && cfg.cni != "canal") "--cni=${cfg.cni}") + ++ (optional cfg.cisHardening "--profile=${if cfg.package.version >= "1.25" then "cis-1.23" else "cis-1.6"}") + ++ cfg.extraFlags + )}"; + ExecStopPost = let + killProcess = pkgs.writeScript "kill-process.sh" '' + #! ${pkgs.runtimeShell} + /run/current-system/systemd/bin/systemd-cgls /system.slice/$1 | \ + ${pkgs.gnugrep}/bin/grep -Eo '[0-9]+ (containerd|kubelet)' | \ + ${pkgs.gawk}/bin/awk '{print $1}' | \ + ${pkgs.findutils}/bin/xargs -r ${pkgs.util-linux}/bin/kill + ''; + in "-${killProcess} %n"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix index 23f07eb64b92..b516c3d6192c 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix @@ -335,7 +335,7 @@ in mkdir -m 0700 -p ${baseDir}/queue-runner mkdir -m 0750 -p ${baseDir}/build-logs mkdir -m 0750 -p ${baseDir}/runcommand-logs - chown hydra-queue-runner.hydra \ + chown hydra-queue-runner:hydra \ ${baseDir}/queue-runner \ ${baseDir}/build-logs \ ${baseDir}/runcommand-logs diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.md b/nixpkgs/nixos/modules/services/databases/postgresql.md index 8a587832cd8c..e76f127335c7 100644 --- a/nixpkgs/nixos/modules/services/databases/postgresql.md +++ b/nixpkgs/nixos/modules/services/databases/postgresql.md @@ -244,6 +244,27 @@ The upgrade process is: $ ./delete_old_cluster.sh ``` +## Versioning and End-of-Life {#module-services-postgres-versioning} + +PostgreSQL's versioning policy is described [here](https://www.postgresql.org/support/versioning/). TLDR: + +- Each major version is supported for 5 years. +- Every three months there will be a new minor release, containing bug and security fixes. +- For criticial/security fixes there could be more minor releases inbetween. This happens *very* infrequently. +- After five years, a final minor version is released. This usually happens in early November. +- After that a version is considered end-of-life (EOL). +- Around February each year is the first time an EOL-release will not have received regular updates anymore. + +Technically, we'd not want to have EOL'ed packages in a stable NixOS release, which is to be supported until one month after the previous release. Thus, with NixOS' release schedule in May and November, the oldest PostgreSQL version in nixpkgs would have to be supported until December. It could be argued that a soon-to-be-EOL-ed version should thus be removed in May for the .05 release already. But since new security vulnerabilities are first disclosed in Februrary of the following year, we agreed on keeping the oldest PostgreSQL major version around one more cycle in [#310580](https://github.com/NixOS/nixpkgs/pull/310580#discussion_r1597284693). + +Thus: +- In September/October the new major version will be released and added to nixos-unstable. +- In November the last minor version for the oldest major will be released. +- Both the current stable .05 release and nixos-unstable should be updated to the latest minor. +- In November, before branch-off for the .11 release, the EOL-ed major will be removed from nixos-unstable. + +This leaves a small gap of a couple of weeks after the latest minor release and the end of our support window for the .05 release, in which there could be an emergency release to other major versions of PostgreSQL - but not the oldest major we have in that branch. In that case: If we can't trivially patch the issue, we will mark the package/version as insecure **immediately**. + ## Options {#module-services-postgres-options} A complete list of options for the PostgreSQL module may be found [here](#opt-services.postgresql.enable). diff --git a/nixpkgs/nixos/modules/services/desktop-managers/lomiri.nix b/nixpkgs/nixos/modules/services/desktop-managers/lomiri.nix index e11867b69107..0b871aa38183 100644 --- a/nixpkgs/nixos/modules/services/desktop-managers/lomiri.nix +++ b/nixpkgs/nixos/modules/services/desktop-managers/lomiri.nix @@ -22,6 +22,7 @@ in { libusermetrics lomiri lomiri-download-manager + lomiri-filemanager-app lomiri-schemas # exposes some required dbus interfaces lomiri-session # wrappers to properly launch the session lomiri-sounds @@ -34,10 +35,15 @@ in { morph-browser qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session suru-icon-theme - telephony-service + # telephony-service # currently broken: https://github.com/NixOS/nixpkgs/pull/314043 ]); + variables = { + # To override the keyboard layouts in Lomiri + NIXOS_XKB_LAYOUTS = config.services.xserver.xkb.layout; + }; }; + hardware.pulseaudio.enable = lib.mkDefault true; networking.networkmanager.enable = lib.mkDefault true; systemd.packages = with pkgs.lomiri; [ @@ -57,7 +63,7 @@ in { ]; # Copy-pasted basic stuff - hardware.opengl.enable = lib.mkDefault true; + hardware.graphics.enable = lib.mkDefault true; fonts.enableDefaultPackages = lib.mkDefault true; programs.dconf.enable = lib.mkDefault true; @@ -71,10 +77,14 @@ in { enable = true; packages = (with pkgs; [ ayatana-indicator-datetime + ayatana-indicator-display ayatana-indicator-messages + ayatana-indicator-power ayatana-indicator-session + ] ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [ + ayatana-indicator-sound ]) ++ (with pkgs.lomiri; [ - telephony-service + # telephony-service # currently broken: https://github.com/NixOS/nixpkgs/pull/314043 ] ++ lib.optionals config.networking.networkmanager.enable [ lomiri-indicator-network ]); diff --git a/nixpkgs/nixos/modules/services/desktop-managers/plasma6.nix b/nixpkgs/nixos/modules/services/desktop-managers/plasma6.nix index 842b0716b928..d4f961254f02 100644 --- a/nixpkgs/nixos/modules/services/desktop-managers/plasma6.nix +++ b/nixpkgs/nixos/modules/services/desktop-managers/plasma6.nix @@ -60,10 +60,8 @@ in { qt.enable = true; environment.systemPackages = with kdePackages; let requiredPackages = [ - # Hack? To make everything run on Wayland - qtwayland - # Needed to render SVG icons - qtsvg + qtwayland # Hack? To make everything run on Wayland + qtsvg # Needed to render SVG icons # Frameworks with globally loadable bits frameworkintegration # provides Qt plugin @@ -75,6 +73,9 @@ in { kiconthemes # provides Qt plugins kimageformats # provides Qt plugins kio # provides helper service + a bunch of other stuff + kio-admin # managing files as admin + kio-extras # stuff for MTP, AFC, etc + kio-fuse # fuse interface for KIO kpackage # provides kpackagetool tool kservice # provides kbuildsycoca6 tool kwallet # provides helper service @@ -87,30 +88,26 @@ in { # Core Plasma parts kwin pkgs.xwayland - kscreen libkscreen - kscreenlocker - kactivitymanagerd kde-cli-tools - kglobalacceld + kglobalacceld # keyboard shortcut daemon kwrited # wall message proxy, not to be confused with kwrite - - milou - polkit-kde-agent-1 - + baloo # system indexer + milou # search engine atop baloo + kdegraphics-thumbnailers # pdf etc thumbnailer + polkit-kde-agent-1 # polkit auth ui plasma-desktop plasma-workspace - - # Crash handler - drkonqi + drkonqi # crash handler + kde-inotify-survey # warns the user on low inotifywatch limits # Application integration libplasma # provides Kirigami platform theme plasma-integration # provides Qt platform theme - kde-gtk-config + kde-gtk-config # syncs KDE settings to GTK # Artwork + themes breeze @@ -124,44 +121,32 @@ in { # misc Plasma extras kdeplasma-addons - pkgs.xdg-user-dirs # recommended upstream # Plasma utilities kmenuedit - kinfocenter plasma-systemmonitor ksystemstats libksysguard - - spectacle systemsettings kcmutils - - # Gear - baloo - dolphin - dolphin-plugins - ffmpegthumbs - kdegraphics-thumbnailers - kde-inotify-survey - kio-admin - kio-extras - kio-fuse ]; optionalPackages = [ plasma-browser-integration konsole (lib.getBin qttools) # Expose qdbus in PATH - ark elisa gwenview okular kate khelpcenter - print-manager + dolphin + dolphin-plugins + spectacle + ffmpegthumbs + krdp ]; in requiredPackages @@ -183,12 +168,13 @@ in { ) kio-extras-kf5 ] - # Optional hardware support features + # Optional and hardware support features ++ lib.optionals config.hardware.bluetooth.enable [bluedevil bluez-qt pkgs.openobex pkgs.obexftp] ++ lib.optional config.networking.networkmanager.enable plasma-nm ++ lib.optional config.hardware.pulseaudio.enable plasma-pa ++ lib.optional config.services.pipewire.pulse.enable plasma-pa ++ lib.optional config.powerManagement.enable powerdevil + ++ lib.optional config.services.printing.enable print-manager ++ lib.optional config.services.colord.enable colord-kde ++ lib.optional config.services.hardware.bolt.enable plasma-thunderbolt ++ lib.optional config.services.samba.enable kdenetwork-filesharing @@ -217,7 +203,7 @@ in { environment.sessionVariables.KPACKAGE_DEP_RESOLVERS_PATH = "${kdePackages.frameworkintegration.out}/libexec/kf6/kpackagehandlers"; # Enable GTK applications to load SVG icons - services.xserver.gdk-pixbuf.modulePackages = [pkgs.librsvg]; + programs.gdk-pixbuf.modulePackages = [pkgs.librsvg]; fonts.packages = [cfg.notoPackage pkgs.hack-font]; fonts.fontconfig.defaultFonts = { diff --git a/nixpkgs/nixos/modules/services/desktops/espanso.nix b/nixpkgs/nixos/modules/services/desktops/espanso.nix index 4ef6724dda0a..a6b8a078247b 100644 --- a/nixpkgs/nixos/modules/services/desktops/espanso.nix +++ b/nixpkgs/nixos/modules/services/desktops/espanso.nix @@ -6,19 +6,24 @@ in { meta = { maintainers = with lib.maintainers; [ numkem ]; }; options = { - services.espanso = { enable = options.mkEnableOption "Espanso"; }; + services.espanso = { + enable = mkEnableOption "Espanso"; + package = mkPackageOption pkgs "espanso" { + example = "pkgs.espanso-wayland"; + }; + }; }; config = mkIf cfg.enable { systemd.user.services.espanso = { description = "Espanso daemon"; serviceConfig = { - ExecStart = "${pkgs.espanso}/bin/espanso daemon"; + ExecStart = "${lib.getExe cfg.package} daemon"; Restart = "on-failure"; }; wantedBy = [ "default.target" ]; }; - environment.systemPackages = [ pkgs.espanso ]; + environment.systemPackages = [ cfg.package ]; }; } diff --git a/nixpkgs/nixos/modules/services/display-managers/default.nix b/nixpkgs/nixos/modules/services/display-managers/default.nix index feba4b163ccd..9a7bd6c84b15 100644 --- a/nixpkgs/nixos/modules/services/display-managers/default.nix +++ b/nixpkgs/nixos/modules/services/display-managers/default.nix @@ -212,9 +212,7 @@ in after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ]; restartIfChanged = false; - environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath { - LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; - } // cfg.environment; + environment = cfg.environment; preStart = cfg.preStart; script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd; diff --git a/nixpkgs/nixos/modules/services/editors/emacs.nix b/nixpkgs/nixos/modules/services/editors/emacs.nix index 35f257cee1e3..98d8506e6727 100644 --- a/nixpkgs/nixos/modules/services/editors/emacs.nix +++ b/nixpkgs/nixos/modules/services/editors/emacs.nix @@ -6,8 +6,7 @@ let cfg = config.services.emacs; - editorScript = pkgs.writeScriptBin "emacseditor" '' - #!${pkgs.runtimeShell} + editorScript = pkgs.writeShellScriptBin "emacseditor" '' if [ -z "$1" ]; then exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs else @@ -70,8 +69,8 @@ in description = "Emacs: the extensible, self-documenting text editor"; serviceConfig = { - Type = "forking"; - ExecStart = "${pkgs.bash}/bin/bash -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --daemon'"; + Type = "notify"; + ExecStart = "${pkgs.runtimeShell} -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --fg-daemon'"; ExecStop = "${cfg.package}/bin/emacsclient --eval (kill-emacs)"; Restart = "always"; }; diff --git a/nixpkgs/nixos/modules/services/games/archisteamfarm.nix b/nixpkgs/nixos/modules/services/games/archisteamfarm.nix index 33898f8387e9..7062332db34a 100644 --- a/nixpkgs/nixos/modules/services/games/archisteamfarm.nix +++ b/nixpkgs/nixos/modules/services/games/archisteamfarm.nix @@ -164,8 +164,11 @@ in }; config = lib.mkIf cfg.enable { - # TODO: drop with 24.11 - services.archisteamfarm.dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf"); + services.archisteamfarm = { + # TODO: drop with 24.11 + dataDir = lib.mkIf (lib.versionAtLeast config.system.stateVersion "24.05") (lib.mkDefault "/var/lib/asf"); + settings.IPC = lib.mkIf (!cfg.web-ui.enable) false; + }; users = { users.archisteamfarm = { @@ -193,7 +196,7 @@ in Group = "archisteamfarm"; WorkingDirectory = cfg.dataDir; Type = "simple"; - ExecStart = "${lib.getExe cfg.package} --no-restart --process-required --service --system-required --path ${cfg.dataDir}"; + ExecStart = "${lib.getExe cfg.package} --no-restart --service --system-required --path ${cfg.dataDir}"; Restart = "always"; # copied from the default systemd service at diff --git a/nixpkgs/nixos/modules/services/hardware/amdgpu.nix b/nixpkgs/nixos/modules/services/hardware/amdgpu.nix new file mode 100644 index 000000000000..24016fc64697 --- /dev/null +++ b/nixpkgs/nixos/modules/services/hardware/amdgpu.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.hardware.amdgpu; +in { + options.hardware.amdgpu = { + legacySupport.enable = lib.mkEnableOption '' + using `amdgpu` kernel driver instead of `radeon` for Southern Islands + (Radeon HD 7000) series and Sea Islands (Radeon HD 8000) + series cards. Note: this removes support for analog video outputs, + which is only available in the `radeon` driver + ''; + initrd.enable = lib.mkEnableOption '' + loading `amdgpu` kernelModule in stage 1. + Can fix lower resolution in boot screen during initramfs phase + ''; + opencl.enable = lib.mkEnableOption ''OpenCL support using ROCM runtime library''; + # cfg.amdvlk option is defined in ./amdvlk.nix module + }; + + config = { + boot.kernelParams = lib.optionals cfg.legacySupport.enable [ + "amdgpu.si_support=1" + "amdgpu.cik_support=1" + "radeon.si_support=0" + "radeon.cik_support=0" + ]; + + boot.initrd.kernelModules = lib.optionals cfg.initrd.enable [ "amdgpu" ]; + + hardware.opengl = lib.mkIf cfg.opencl.enable { + enable = lib.mkDefault true; + extraPackages = [ + pkgs.rocmPackages.clr + pkgs.rocmPackages.clr.icd + ]; + }; + }; + + meta = { + maintainers = with lib.maintainers; [ johnrtitor ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/hardware/amdvlk.nix b/nixpkgs/nixos/modules/services/hardware/amdvlk.nix new file mode 100644 index 000000000000..32d6fb3be21d --- /dev/null +++ b/nixpkgs/nixos/modules/services/hardware/amdvlk.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.hardware.amdgpu.amdvlk; +in { + options.hardware.amdgpu.amdvlk = { + enable = lib.mkEnableOption "AMDVLK Vulkan driver"; + + package = lib.mkPackageOption pkgs "amdvlk" { }; + + supportExperimental.enable = lib.mkEnableOption "Experimental features support"; + + support32Bit.enable = lib.mkEnableOption "32-bit driver support"; + support32Bit.package = lib.mkPackageOption pkgs [ "driversi686Linux" "amdvlk" ] { }; + + settings = lib.mkOption { + type = with lib.types; attrsOf (either str int); + default = { }; + example = { + AllowVkPipelineCachingToDisk = 1; + ShaderCacheMode = 1; + IFH = 0; + EnableVmAlwaysValid = 1; + IdleAfterSubmitGpuMask = 1; + }; + description = '' + Runtime settings for AMDVLK to be configured {file}`/etc/amd/amdVulkanSettings.cfg`. + See [AMDVLK GitHub page](https://github.com/GPUOpen-Drivers/AMDVLK?tab=readme-ov-file#runtime-settings). + ''; + }; + }; + + config = lib.mkIf cfg.enable { + hardware.graphics = { + enable = true; + extraPackages = [ cfg.package ]; + extraPackages32 = [ cfg.support32Bit.package ]; + }; + + services.xserver.videoDrivers = [ "amdgpu" ]; + + environment.sessionVariables = lib.mkIf cfg.supportExperimental.enable { + AMDVLK_ENABLE_DEVELOPING_EXT = "all"; + }; + + environment.etc = lib.mkIf (cfg.settings != { }) { + "amd/amdVulkanSettings.cfg".text = lib.concatStrings + (lib.mapAttrsToList + (n: v: '' + ${n},${builtins.toString v} + '') + cfg.settings); + }; + }; + + meta = { + maintainers = with lib.maintainers; [ johnrtitor ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/hardware/handheld-daemon.nix b/nixpkgs/nixos/modules/services/hardware/handheld-daemon.nix index 6c9d5aa3e22c..a5ba1856d015 100644 --- a/nixpkgs/nixos/modules/services/hardware/handheld-daemon.nix +++ b/nixpkgs/nixos/modules/services/hardware/handheld-daemon.nix @@ -8,7 +8,7 @@ with lib; let in { options.services.handheld-daemon = { - enable = mkEnableOption "Enable Handheld Daemon"; + enable = mkEnableOption "Handheld Daemon"; package = mkPackageOption pkgs "handheld-daemon" { }; user = mkOption { diff --git a/nixpkgs/nixos/modules/services/hardware/kanata.nix b/nixpkgs/nixos/modules/services/hardware/kanata.nix index 46af3e36b985..60fb33881f25 100644 --- a/nixpkgs/nixos/modules/services/hardware/kanata.nix +++ b/nixpkgs/nixos/modules/services/hardware/kanata.nix @@ -7,7 +7,7 @@ let upstreamDoc = "See [the upstream documentation](https://github.com/jtroo/kanata/blob/main/docs/config.adoc) and [example config files](https://github.com/jtroo/kanata/tree/main/cfg_samples) for more information."; - keyboard = { + keyboard = { name, config, ... }: { options = { devices = mkOption { type = types.listOf types.str; @@ -48,6 +48,21 @@ let ${upstreamDoc} ''; }; + configFile = mkOption { + type = types.path; + default = mkConfig name config; + defaultText = + "A config file generated by values from other kanata module options."; + description = '' + The config file. + + By default, it is generated by values from other kanata + module options. + + You can also set it to your own full config file which + overrides all other kanata module options. ${upstreamDoc} + ''; + }; extraArgs = mkOption { type = types.listOf types.str; default = [ ]; @@ -85,6 +100,10 @@ let ${keyboard.config} ''; + # Only the config file generated by this module is checked. A + # user-provided one is not checked because it may not be available + # at build time. I think this is a good balance between module + # complexity and functionality. checkPhase = '' ${getExe cfg.package} --cfg "$target" --check --debug ''; @@ -96,7 +115,7 @@ let Type = "notify"; ExecStart = '' ${getExe cfg.package} \ - --cfg ${mkConfig name keyboard} \ + --cfg ${keyboard.configFile} \ --symlink-path ''${RUNTIME_DIRECTORY}/${name} \ ${optionalString (keyboard.port != null) "--port ${toString keyboard.port}"} \ ${utils.escapeSystemdExecArgs keyboard.extraArgs} diff --git a/nixpkgs/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix b/nixpkgs/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix index 6c6bc667e649..bd12667a5647 100644 --- a/nixpkgs/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix +++ b/nixpkgs/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix @@ -69,14 +69,18 @@ virtualisation.docker.daemon.settings = lib.mkIf (config.hardware.nvidia-container-toolkit.enable && (lib.versionAtLeast config.virtualisation.docker.package.version "25")) { - features.cdi = true; - }; + features.cdi = true; + }; hardware.nvidia-container-toolkit.mounts = let nvidia-driver = config.hardware.nvidia.package; in (lib.mkMerge [ [{ hostPath = pkgs.addDriverRunpath.driverLink; containerPath = pkgs.addDriverRunpath.driverLink; } + { hostPath = "${lib.getLib nvidia-driver}/etc"; + containerPath = "${lib.getLib nvidia-driver}/etc"; } + { hostPath = "${lib.getLib nvidia-driver}/share"; + containerPath = "${lib.getLib nvidia-driver}/share"; } { hostPath = "${lib.getLib pkgs.glibc}/lib"; containerPath = "${lib.getLib pkgs.glibc}/lib"; } { hostPath = "${lib.getLib pkgs.glibc}/lib64"; diff --git a/nixpkgs/nixos/modules/services/hardware/nvidia-optimus.nix b/nixpkgs/nixos/modules/services/hardware/nvidia-optimus.nix index d53175052c74..307bc78098d1 100644 --- a/nixpkgs/nixos/modules/services/hardware/nvidia-optimus.nix +++ b/nixpkgs/nixos/modules/services/hardware/nvidia-optimus.nix @@ -23,7 +23,7 @@ let kernel = config.boot.kernelPackages; in ###### implementation config = lib.mkIf config.hardware.nvidiaOptimus.disable { - boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm"]; + boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidiafb" "nvidia-drm" "nvidia-modeset"]; boot.kernelModules = [ "bbswitch" ]; boot.extraModulePackages = [ kernel.bbswitch ]; diff --git a/nixpkgs/nixos/modules/services/hardware/power-profiles-daemon.nix b/nixpkgs/nixos/modules/services/hardware/power-profiles-daemon.nix index 05e5b7a00b42..7651c65b9f18 100644 --- a/nixpkgs/nixos/modules/services/hardware/power-profiles-daemon.nix +++ b/nixpkgs/nixos/modules/services/hardware/power-profiles-daemon.nix @@ -39,6 +39,12 @@ in which conflicts with services.tlp.enable = true; ''; } + { assertion = !config.services.auto-cpufreq.enable; + message = '' + You have set services.power-profiles-daemon.enable = true; + which conflicts with services.auto-cpufreq.enable = true; + ''; + } ]; environment.systemPackages = [ cfg.package ]; diff --git a/nixpkgs/nixos/modules/services/home-automation/ebusd.nix b/nixpkgs/nixos/modules/services/home-automation/ebusd.nix index ac9ec06639c1..f5c5479e8eaf 100644 --- a/nixpkgs/nixos/modules/services/home-automation/ebusd.nix +++ b/nixpkgs/nixos/modules/services/home-automation/ebusd.nix @@ -138,7 +138,7 @@ in after = [ "network.target" ]; serviceConfig = { ExecStart = let - args = cli.toGNUCommandLineShell { } (foldr (a: b: a // b) { } [ + args = cli.toGNUCommandLineShell { optionValueSeparator = "="; } (foldr (a: b: a // b) { } [ { inherit (cfg) device port configpath scanconfig readonly; foreground = true; diff --git a/nixpkgs/nixos/modules/services/home-automation/wyoming/faster-whisper.nix b/nixpkgs/nixos/modules/services/home-automation/wyoming/faster-whisper.nix index d0fca6a41c7b..45664103665f 100644 --- a/nixpkgs/nixos/modules/services/home-automation/wyoming/faster-whisper.nix +++ b/nixpkgs/nixos/modules/services/home-automation/wyoming/faster-whisper.nix @@ -113,6 +113,9 @@ in nameValuePair "wyoming-faster-whisper-${server}" { inherit (options) enable; description = "Wyoming faster-whisper server instance ${server}"; + wants = [ + "network-online.target" + ]; after = [ "network-online.target" ]; diff --git a/nixpkgs/nixos/modules/services/home-automation/wyoming/openwakeword.nix b/nixpkgs/nixos/modules/services/home-automation/wyoming/openwakeword.nix index 856a4ef7366d..f9848970bf73 100644 --- a/nixpkgs/nixos/modules/services/home-automation/wyoming/openwakeword.nix +++ b/nixpkgs/nixos/modules/services/home-automation/wyoming/openwakeword.nix @@ -108,6 +108,9 @@ in config = mkIf cfg.enable { systemd.services."wyoming-openwakeword" = { description = "Wyoming openWakeWord server"; + wants = [ + "network-online.target" + ]; after = [ "network-online.target" ]; diff --git a/nixpkgs/nixos/modules/services/home-automation/wyoming/piper.nix b/nixpkgs/nixos/modules/services/home-automation/wyoming/piper.nix index 5b5f898d7ca3..a26fe8e84f60 100644 --- a/nixpkgs/nixos/modules/services/home-automation/wyoming/piper.nix +++ b/nixpkgs/nixos/modules/services/home-automation/wyoming/piper.nix @@ -117,6 +117,9 @@ in nameValuePair "wyoming-piper-${server}" { inherit (options) enable; description = "Wyoming Piper server instance ${server}"; + wants = [ + "network-online.target" + ]; after = [ "network-online.target" ]; diff --git a/nixpkgs/nixos/modules/services/logging/journalwatch.nix b/nixpkgs/nixos/modules/services/logging/journalwatch.nix index 71b29d57b7eb..48fd992ffb65 100644 --- a/nixpkgs/nixos/modules/services/logging/journalwatch.nix +++ b/nixpkgs/nixos/modules/services/logging/journalwatch.nix @@ -56,6 +56,8 @@ in { ''; }; + package = mkPackageOption pkgs "journalwatch" { }; + priority = mkOption { type = types.int; default = 6; @@ -240,7 +242,7 @@ in { # requires a relative directory name to create beneath /var/lib StateDirectory = user; StateDirectoryMode = "0750"; - ExecStart = "${pkgs.python3Packages.journalwatch}/bin/journalwatch mail"; + ExecStart = "${getExe cfg.package} mail"; # lowest CPU and IO priority, but both still in best-effort class to prevent starvation Nice=19; IOSchedulingPriority=7; diff --git a/nixpkgs/nixos/modules/services/mail/postsrsd.nix b/nixpkgs/nixos/modules/services/mail/postsrsd.nix index 2ebc675ab10a..92f01dd4101e 100644 --- a/nixpkgs/nixos/modules/services/mail/postsrsd.nix +++ b/nixpkgs/nixos/modules/services/mail/postsrsd.nix @@ -120,14 +120,9 @@ in { if [ ! -e "${cfg.secretsFile}" ]; then echo "WARNING: secrets file not found, autogenerating!" DIR="$(dirname "${cfg.secretsFile}")" - if [ ! -d "$DIR" ]; then - mkdir -p -m750 "$DIR" - chown "${cfg.user}:${cfg.group}" "$DIR" - fi - dd if=/dev/random bs=18 count=1 | base64 > "${cfg.secretsFile}" - chmod 600 "${cfg.secretsFile}" + install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR" + install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}" fi - chown "${cfg.user}:${cfg.group}" "${cfg.secretsFile}" ''; }; diff --git a/nixpkgs/nixos/modules/services/mail/public-inbox.nix b/nixpkgs/nixos/modules/services/mail/public-inbox.nix index ea0dfda0e695..332861b78fbd 100644 --- a/nixpkgs/nixos/modules/services/mail/public-inbox.nix +++ b/nixpkgs/nixos/modules/services/mail/public-inbox.nix @@ -468,7 +468,7 @@ in after = [ "public-inbox-init.service" "public-inbox-watch.service" ]; requires = [ "public-inbox-init.service" ]; serviceConfig = { - BindPathsReadOnly = + BindReadOnlyPaths = map (c: c.dir) (lib.attrValues cfg.settings.coderepo); ExecStart = escapeShellArgs ( [ "${cfg.package}/bin/public-inbox-httpd" ] ++ diff --git a/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix b/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix index c69a2ca400ba..ed3c5389354c 100644 --- a/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix +++ b/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix @@ -7,28 +7,13 @@ let configFormat = pkgs.formats.toml { }; configFile = configFormat.generate "stalwart-mail.toml" cfg.settings; dataDir = "/var/lib/stalwart-mail"; - stalwartAtLeast = versionAtLeast cfg.package.version; + useLegacyStorage = versionOlder config.system.stateVersion "24.11"; in { options.services.stalwart-mail = { enable = mkEnableOption "the Stalwart all-in-one email server"; - package = mkOption { - type = types.package; - description = '' - Which package to use for the Stalwart mail server. - - ::: {.note} - Upgrading from version 0.6.0 to version 0.7.0 or higher requires manual - intervention. See <https://github.com/stalwartlabs/mail-server/blob/main/UPGRADING.md> - for upgrade instructions. - ::: - ''; - default = pkgs.stalwart-mail_0_6; - defaultText = lib.literalExpression "pkgs.stalwart-mail_0_6"; - example = lib.literalExpression "pkgs.stalwart-mail"; - relatedPackages = [ "stalwart-mail_0_6" "stalwart-mail" ]; - }; + package = mkPackageOption pkgs "stalwart-mail" { }; settings = mkOption { inherit (configFormat) type; @@ -44,90 +29,109 @@ in { config = mkIf cfg.enable { - warnings = lib.optionals (!stalwartAtLeast "0.7.0") [ - '' - Versions of stalwart-mail < 0.7.0 will get deprecated in NixOS 24.11. - Please set services.stalwart-mail.package to pkgs.stalwart-mail to - upgrade to the latest version. - Please note that upgrading to version >= 0.7 requires manual - intervention, see <https://github.com/stalwartlabs/mail-server/blob/main/UPGRADING.md> - for upgrade instructions. - '' - ]; - # Default config: all local services.stalwart-mail.settings = { - global.tracing.method = mkDefault "stdout"; - global.tracing.level = mkDefault "info"; + tracer.stdout = { + type = mkDefault "stdout"; + level = mkDefault "info"; + ansi = mkDefault false; # no colour markers to journald + enable = mkDefault true; + }; queue.path = mkDefault "${dataDir}/queue"; report.path = mkDefault "${dataDir}/reports"; - store.db.type = mkDefault "sqlite"; - store.db.path = mkDefault "${dataDir}/data/index.sqlite3"; - store.blob.type = mkDefault "fs"; - store.blob.path = mkDefault "${dataDir}/data/blobs"; + store = if useLegacyStorage then { + # structured data in SQLite, blobs on filesystem + db.type = mkDefault "sqlite"; + db.path = mkDefault "${dataDir}/data/index.sqlite3"; + fs.type = mkDefault "fs"; + fs.path = mkDefault "${dataDir}/data/blobs"; + } else { + # everything in RocksDB + db.type = mkDefault "rocksdb"; + db.path = mkDefault "${dataDir}/db"; + db.compression = mkDefault "lz4"; + }; storage.data = mkDefault "db"; storage.fts = mkDefault "db"; storage.lookup = mkDefault "db"; - storage.blob = mkDefault "blob"; + storage.blob = mkDefault (if useLegacyStorage then "fs" else "db"); + directory.internal.type = mkDefault "internal"; + directory.internal.store = mkDefault "db"; + storage.directory = mkDefault "internal"; resolver.type = mkDefault "system"; - resolver.public-suffix = mkDefault ["https://publicsuffix.org/list/public_suffix_list.dat"]; + resolver.public-suffix = lib.mkDefault [ + "file://${pkgs.publicsuffix-list}/share/publicsuffix/public_suffix_list.dat" + ]; }; - systemd.services.stalwart-mail = { - wantedBy = [ "multi-user.target" ]; - after = [ "local-fs.target" "network.target" ]; - - preStart = '' - mkdir -p ${dataDir}/{queue,reports,data/blobs} - ''; + # This service stores a potentially large amount of data. + # Running it as a dynamic user would force chown to be run everytime the + # service is restarted on a potentially large number of files. + # That would cause unnecessary and unwanted delays. + users = { + groups.stalwart-mail = { }; + users.stalwart-mail = { + isSystemUser = true; + group = "stalwart-mail"; + }; + }; - serviceConfig = { - ExecStart = - "${cfg.package}/bin/stalwart-mail --config=${configFile}"; - - # Base from template resources/systemd/stalwart-mail.service - Type = "simple"; - LimitNOFILE = 65536; - KillMode = "process"; - KillSignal = "SIGINT"; - Restart = "on-failure"; - RestartSec = 5; - StandardOutput = "journal"; - StandardError = "journal"; - SyslogIdentifier = "stalwart-mail"; - - DynamicUser = true; - User = "stalwart-mail"; - StateDirectory = "stalwart-mail"; - - # Bind standard privileged ports - AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; - CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; - - # Hardening - DeviceAllow = [ "" ]; - LockPersonality = true; - MemoryDenyWriteExecute = true; - PrivateDevices = true; - PrivateUsers = false; # incompatible with CAP_NET_BIND_SERVICE - ProcSubset = "pid"; - PrivateTmp = true; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ "@system-service" "~@privileged" ]; - UMask = "0077"; + systemd = { + packages = [ cfg.package ]; + services.stalwart-mail = { + wantedBy = [ "multi-user.target" ]; + after = [ "local-fs.target" "network.target" ]; + + preStart = if useLegacyStorage then '' + mkdir -p ${dataDir}/{queue,reports,data/blobs} + '' else '' + mkdir -p ${dataDir}/{queue,reports,db} + ''; + + serviceConfig = { + ExecStart = [ + "" + "${cfg.package}/bin/stalwart-mail --config=${configFile}" + ]; + + StandardOutput = "journal"; + StandardError = "journal"; + + StateDirectory = "stalwart-mail"; + + # Bind standard privileged ports + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + + # Hardening + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = false; # incompatible with CAP_NET_BIND_SERVICE + ProcSubset = "pid"; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + UMask = "0077"; + }; + unitConfig.ConditionPathExists = [ + "" + "${configFile}" + ]; }; }; @@ -136,6 +140,6 @@ in { }; meta = { - maintainers = with maintainers; [ happysalada pacien ]; + maintainers = with maintainers; [ happysalada pacien onny ]; }; } diff --git a/nixpkgs/nixos/modules/services/matrix/synapse.nix b/nixpkgs/nixos/modules/services/matrix/synapse.nix index bc88fb53012b..6d2e6201d66d 100644 --- a/nixpkgs/nixos/modules/services/matrix/synapse.nix +++ b/nixpkgs/nixos/modules/services/matrix/synapse.nix @@ -1121,7 +1121,7 @@ in { The client listener on matrix-synapse is configured to use UNIX domain sockets. This configuration is incompatible with the `register_new_matrix_user` script. - Disable `services.mastrix-synapse.enableRegistrationScript` to continue. + Disable `services.matrix-synapse.enableRegistrationScript` to continue. ''; } ] diff --git a/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix b/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix index 9ab4a7f96d08..0da10621d0a0 100644 --- a/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix +++ b/nixpkgs/nixos/modules/services/misc/amazon-ssm-agent.nix @@ -28,13 +28,7 @@ in { options.services.amazon-ssm-agent = { enable = mkEnableOption "Amazon SSM agent"; - - package = mkOption { - type = types.path; - description = "The Amazon SSM agent package to use"; - default = pkgs.amazon-ssm-agent.override { overrideEtc = false; }; - defaultText = literalExpression "pkgs.amazon-ssm-agent.override { overrideEtc = false; }"; - }; + package = mkPackageOption pkgs "amazon-ssm-agent" {}; }; config = mkIf cfg.enable { diff --git a/nixpkgs/nixos/modules/services/misc/anki-sync-server.md b/nixpkgs/nixos/modules/services/misc/anki-sync-server.md index f58d3d8ad0da..5482a4aa0e5f 100644 --- a/nixpkgs/nixos/modules/services/misc/anki-sync-server.md +++ b/nixpkgs/nixos/modules/services/misc/anki-sync-server.md @@ -52,7 +52,7 @@ following options: ```nix { - services.anki-sync-server.host = "0.0.0.0"; + services.anki-sync-server.address = "0.0.0.0"; services.anki-sync-server.openFirewall = true; } ``` diff --git a/nixpkgs/nixos/modules/services/misc/devpi-server.nix b/nixpkgs/nixos/modules/services/misc/devpi-server.nix index 0234db4bc2c5..92c0c6206c8b 100644 --- a/nixpkgs/nixos/modules/services/misc/devpi-server.nix +++ b/nixpkgs/nixos/modules/services/misc/devpi-server.nix @@ -74,8 +74,9 @@ in # have 0600 permissions. preStart = '' - cp ${cfg.secretFile} ${runtimeDir}/${secretsFileName} - chmod 0600 ${runtimeDir}/*${secretsFileName} + ${optionalString (!isNull cfg.secretFile) + "install -Dm 0600 \${CREDENTIALS_DIRECTORY}/devpi-secret ${runtimeDir}/${secretsFileName}" + } if [ -f ${serverDir}/.nodeinfo ]; then # already initialized the package index, exit gracefully @@ -85,6 +86,9 @@ in + strings.optionalString cfg.replica "--role=replica --master-url=${cfg.primaryUrl}"; serviceConfig = { + LoadCredential = lib.mkIf (! isNull cfg.secretFile) [ + "devpi-secret:${cfg.secretFile}" + ]; Restart = "always"; ExecStart = let diff --git a/nixpkgs/nixos/modules/services/misc/forgejo.nix b/nixpkgs/nixos/modules/services/misc/forgejo.nix index babed2d5acd4..9a102918f35e 100644 --- a/nixpkgs/nixos/modules/services/misc/forgejo.nix +++ b/nixpkgs/nixos/modules/services/misc/forgejo.nix @@ -12,6 +12,15 @@ let usePostgresql = cfg.database.type == "postgres"; useSqlite = cfg.database.type == "sqlite3"; + secrets = let + mkSecret = section: values: lib.mapAttrsToList (key: value: { + env = envEscape "FORGEJO__${section}__${key}__FILE"; + path = value; + }) values; + # https://codeberg.org/forgejo/forgejo/src/tag/v7.0.2/contrib/environment-to-ini/environment-to-ini.go + envEscape = string: lib.replaceStrings [ "." "-" ] [ "_0X2E_" "_0X2D_" ] (lib.strings.toUpper string); + in lib.flatten (lib.mapAttrsToList mkSecret cfg.secrets); + inherit (lib) literalExpression mkChangedOptionModule @@ -34,6 +43,7 @@ in (mkRenamedOptionModule [ "services" "forgejo" "appName" ] [ "services" "forgejo" "settings" "DEFAULT" "APP_NAME" ]) (mkRemovedOptionModule [ "services" "forgejo" "extraConfig" ] "services.forgejo.extraConfig has been removed. Please use the freeform services.forgejo.settings option instead") (mkRemovedOptionModule [ "services" "forgejo" "database" "password" ] "services.forgejo.database.password has been removed. Please use services.forgejo.database.passwordFile instead") + (mkRenamedOptionModule [ "services" "forgejo" "mailerPasswordFile" ] [ "services" "forgejo" "secrets" "mailer" "PASSWD" ]) # copied from services.gitea; remove at some point (mkRenamedOptionModule [ "services" "forgejo" "cookieSecure" ] [ "services" "forgejo" "settings" "session" "COOKIE_SECURE" ]) @@ -224,13 +234,6 @@ in description = "Path to the git repositories."; }; - mailerPasswordFile = mkOption { - type = types.nullOr types.str; - default = null; - example = "/run/keys/forgejo-mailpw"; - description = "Path to a file containing the SMTP password."; - }; - settings = mkOption { default = { }; description = '' @@ -347,6 +350,44 @@ in }; }; }; + + secrets = mkOption { + default = { }; + description = '' + This is a small wrapper over systemd's `LoadCredential`. + + It takes the same sections and keys as {option}`services.forgejo.settings`, + but the value of each key is a path instead of a string or bool. + + The path is then loaded as credential, exported as environment variable + and then feed through + <https://codeberg.org/forgejo/forgejo/src/branch/forgejo/contrib/environment-to-ini/environment-to-ini.go>. + + It does the required environment variable escaping for you. + + ::: {.note} + Keys specified here take priority over the ones in {option}`services.forgejo.settings`! + ::: + ''; + example = literalExpression '' + { + metrics = { + TOKEN = "/run/keys/forgejo-metrics-token"; + }; + camo = { + HMAC_KEY = "/run/keys/forgejo-camo-hmac"; + }; + service = { + HCAPTCHA_SECRET = "/run/keys/forgejo-hcaptcha-secret"; + HCAPTCHA_SITEKEY = "/run/keys/forgejo-hcaptcha-sitekey"; + }; + } + ''; + type = types.submodule { + freeformType = with types; attrsOf (attrsOf path); + options = { }; + }; + }; }; }; @@ -381,7 +422,6 @@ in HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port; NAME = cfg.database.name; USER = cfg.database.user; - PASSWD = "#dbpass#"; }) (mkIf useSqlite { PATH = cfg.database.path; @@ -397,7 +437,6 @@ in server = mkIf cfg.lfs.enable { LFS_START_SERVER = true; - LFS_JWT_SECRET = "#lfsjwtsecret#"; }; session = { @@ -405,21 +444,30 @@ in }; security = { - SECRET_KEY = "#secretkey#"; - INTERNAL_TOKEN = "#internaltoken#"; INSTALL_LOCK = true; }; - mailer = mkIf (cfg.mailerPasswordFile != null) { - PASSWD = "#mailerpass#"; + lfs = mkIf cfg.lfs.enable { + PATH = cfg.lfs.contentDir; + }; + }; + + services.forgejo.secrets = { + security = { + SECRET_KEY = "${cfg.customDir}/conf/secret_key"; + INTERNAL_TOKEN = "${cfg.customDir}/conf/internal_token"; }; oauth2 = { - JWT_SECRET = "#oauth2jwtsecret#"; + JWT_SECRET = "${cfg.customDir}/conf/oauth2_jwt_secret"; }; - lfs = mkIf cfg.lfs.enable { - PATH = cfg.lfs.contentDir; + database = mkIf (cfg.database.passwordFile != null) { + PASSWD = cfg.database.passwordFile; + }; + + server = mkIf cfg.lfs.enable { + LFS_JWT_SECRET = "${cfg.customDir}/conf/lfs_jwt_secret"; }; }; @@ -476,6 +524,37 @@ in "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" ]; + systemd.services.forgejo-secrets = mkIf (!cfg.useWizard) { + description = "Forgejo secret bootstrap helper"; + script = '' + if [ ! -s '${cfg.secrets.security.SECRET_KEY}' ]; then + ${exe} generate secret SECRET_KEY > '${cfg.secrets.security.SECRET_KEY}' + fi + + if [ ! -s '${cfg.secrets.oauth2.JWT_SECRET}' ]; then + ${exe} generate secret JWT_SECRET > '${cfg.secrets.oauth2.JWT_SECRET}' + fi + + ${optionalString cfg.lfs.enable '' + if [ ! -s '${cfg.secrets.server.LFS_JWT_SECRET}' ]; then + ${exe} generate secret LFS_JWT_SECRET > '${cfg.secrets.server.LFS_JWT_SECRET}' + fi + ''} + + if [ ! -s '${cfg.secrets.security.INTERNAL_TOKEN}' ]; then + ${exe} generate secret INTERNAL_TOKEN > '${cfg.secrets.security.INTERNAL_TOKEN}' + fi + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = cfg.user; + Group = cfg.group; + ReadWritePaths = [ cfg.customDir ]; + UMask = "0077"; + }; + }; + systemd.services.forgejo = { description = "Forgejo (Beyond coding. We forge.)"; after = [ @@ -484,11 +563,15 @@ in "postgresql.service" ] ++ optionals useMysql [ "mysql.service" + ] ++ optionals (!cfg.useWizard) [ + "forgejo-secrets.service" ]; requires = optionals (cfg.database.createDatabase && usePostgresql) [ "postgresql.service" ] ++ optionals (cfg.database.createDatabase && useMysql) [ "mysql.service" + ] ++ optionals (!cfg.useWizard) [ + "forgejo-secrets.service" ]; wantedBy = [ "multi-user.target" ]; path = [ cfg.package pkgs.git pkgs.gnupg ]; @@ -501,61 +584,15 @@ in # lfs_jwt_secret. # We have to consider this to stay compatible with older installations. preStart = - let - runConfig = "${cfg.customDir}/conf/app.ini"; - secretKey = "${cfg.customDir}/conf/secret_key"; - oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret"; - oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET - lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET - internalToken = "${cfg.customDir}/conf/internal_token"; - replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret"; - in '' - # copy custom configuration and generate random secrets if needed - ${lib.optionalString (!cfg.useWizard) '' + ${optionalString (!cfg.useWizard) '' function forgejo_setup { - cp -f '${format.generate "app.ini" cfg.settings}' '${runConfig}' - - if [ ! -s '${secretKey}' ]; then - ${exe} generate secret SECRET_KEY > '${secretKey}' - fi - - # Migrate LFS_JWT_SECRET filename - if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then - mv '${oldLfsJwtSecret}' '${lfsJwtSecret}' - fi - - if [ ! -s '${oauth2JwtSecret}' ]; then - ${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}' - fi - - ${optionalString cfg.lfs.enable '' - if [ ! -s '${lfsJwtSecret}' ]; then - ${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}' - fi - ''} - - if [ ! -s '${internalToken}' ]; then - ${exe} generate secret INTERNAL_TOKEN > '${internalToken}' - fi - - chmod u+w '${runConfig}' - ${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}' - ${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}' - ${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}' - - ${optionalString cfg.lfs.enable '' - ${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}' - ''} - - ${optionalString (cfg.database.passwordFile != null) '' - ${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}' - ''} - - ${optionalString (cfg.mailerPasswordFile != null) '' - ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}' - ''} - chmod u-w '${runConfig}' + config='${cfg.customDir}/conf/app.ini' + cp -f '${format.generate "app.ini" cfg.settings}' "$config" + + chmod u+w "$config" + ${lib.getExe' cfg.package "environment-to-ini"} --config "$config" + chmod u-w "$config" } (umask 027; forgejo_setup) ''} @@ -616,6 +653,8 @@ in # System Call Filtering SystemCallArchitectures = "native"; SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ]; + # cfg.secrets + LoadCredential = map (e: "${e.env}:${e.path}") secrets; }; environment = { @@ -625,7 +664,7 @@ in # is resolved. GITEA_WORK_DIR = cfg.stateDir; GITEA_CUSTOM = cfg.customDir; - }; + } // lib.listToAttrs (map (e: lib.nameValuePair e.env "%d/${e.env}") secrets); }; services.openssh.settings.AcceptEnv = mkIf (!cfg.settings.START_SSH_SERVER or false) "GIT_PROTOCOL"; diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix index a8526688b074..d43250c88268 100644 --- a/nixpkgs/nixos/modules/services/misc/gitea.nix +++ b/nixpkgs/nixos/modules/services/misc/gitea.nix @@ -722,5 +722,5 @@ in timerConfig.OnCalendar = cfg.dump.interval; }; }; - meta.maintainers = with lib.maintainers; [ srhb ma27 pyrox0 ]; + meta.maintainers = with lib.maintainers; [ ma27 techknowlogick SuperSandro2000 ]; } diff --git a/nixpkgs/nixos/modules/services/misc/gollum.nix b/nixpkgs/nixos/modules/services/misc/gollum.nix index 3966ef036bec..f320e78a9106 100644 --- a/nixpkgs/nixos/modules/services/misc/gollum.nix +++ b/nixpkgs/nixos/modules/services/misc/gollum.nix @@ -110,7 +110,7 @@ in users.groups."${cfg.group}" = { }; systemd.tmpfiles.rules = [ - "d '${cfg.stateDir}' - ${config.users.users.gollum.name} ${config.users.groups.gollum.name} - -" + "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" ]; systemd.services.gollum = { diff --git a/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix b/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix index a88c02e610bf..c8fe0d921c6a 100644 --- a/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix +++ b/nixpkgs/nixos/modules/services/misc/graphical-desktop.nix @@ -38,7 +38,7 @@ in fonts.enableDefaultPackages = lib.mkDefault true; - hardware.opengl.enable = lib.mkDefault true; + hardware.graphics.enable = lib.mkDefault true; programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3; diff --git a/nixpkgs/nixos/modules/services/misc/invidious-router.nix b/nixpkgs/nixos/modules/services/misc/invidious-router.nix index 33da7e96b523..7a90c6ab9ddc 100644 --- a/nixpkgs/nixos/modules/services/misc/invidious-router.nix +++ b/nixpkgs/nixos/modules/services/misc/invidious-router.nix @@ -8,10 +8,10 @@ settingsFormat = pkgs.formats.yaml {}; configFile = settingsFormat.generate "config.yaml" cfg.settings; in { - meta.maintainers = [lib.maintainers.s1ls]; + meta.maintainers = [lib.maintainers.sils]; options.services.invidious-router = { - enable = lib.mkEnableOption "Enables the invidious-router service"; + enable = lib.mkEnableOption "the invidious-router service"; port = lib.mkOption { type = lib.types.port; default = 8050; diff --git a/nixpkgs/nixos/modules/services/misc/jellyfin.nix b/nixpkgs/nixos/modules/services/misc/jellyfin.nix index a1d3910bd93b..a00609087842 100644 --- a/nixpkgs/nixos/modules/services/misc/jellyfin.nix +++ b/nixpkgs/nixos/modules/services/misc/jellyfin.nix @@ -160,5 +160,5 @@ in }; - meta.maintainers = with maintainers; [ minijackson nu-nu-ko ]; + meta.maintainers = with maintainers; [ minijackson fsnkty ]; } diff --git a/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix index a2d6a2b34a23..925139b449b8 100644 --- a/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix +++ b/nixpkgs/nixos/modules/services/misc/mqtt2influxdb.nix @@ -125,6 +125,7 @@ in { options = { services.mqtt2influxdb = { enable = mkEnableOption "BigClown MQTT to InfluxDB bridge."; + package = mkPackageOption pkgs ["python3Packages" "mqtt2influxdb"] {}; environmentFiles = mkOption { type = types.listOf types.path; default = []; @@ -245,7 +246,7 @@ in { ''; serviceConfig = { EnvironmentFile = cfg.environmentFiles; - ExecStart = "${cfg.package}/bin/mqtt2influxdb -dc ${finalConfig}"; + ExecStart = "${lib.getExe cfg.package} -dc ${finalConfig}"; RuntimeDirectory = "mqtt2influxdb"; }; }; diff --git a/nixpkgs/nixos/modules/services/misc/ollama.nix b/nixpkgs/nixos/modules/services/misc/ollama.nix index c0341984aa35..886eaa180b9e 100644 --- a/nixpkgs/nixos/modules/services/misc/ollama.nix +++ b/nixpkgs/nixos/modules/services/misc/ollama.nix @@ -11,6 +11,11 @@ let }; in { + imports = [ + (lib.mkRemovedOptionModule [ "services" "ollama" "listenAddress" ] + "Use `services.ollama.host` and `services.ollama.port` instead.") + ]; + options = { services.ollama = { enable = lib.mkEnableOption "ollama server for local large language models"; @@ -64,12 +69,20 @@ in See also `services.ollama.sandbox`. ''; }; - listenAddress = lib.mkOption { + host = lib.mkOption { type = types.str; - default = "127.0.0.1:11434"; - example = "0.0.0.0:11111"; + default = "127.0.0.1"; + example = "0.0.0.0"; description = '' - The address which the ollama server HTTP interface binds and listens to. + The host address which the ollama server HTTP interface listens to. + ''; + }; + port = lib.mkOption { + type = types.port; + default = 11434; + example = 11111; + description = '' + Which port the ollama server listens to. ''; }; acceleration = lib.mkOption { @@ -80,9 +93,9 @@ in What interface to use for hardware acceleration. - `null`: default behavior - if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"` - if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"` - otherwise defaults to `false` + - if `nixpkgs.config.rocmSupport` is enabled, uses `"rocm"` + - if `nixpkgs.config.cudaSupport` is enabled, uses `"cuda"` + - otherwise defaults to `false` - `false`: disable GPU, only use CPU - `"rocm"`: supported by most modern AMD GPUs - `"cuda"`: supported by most modern NVIDIA GPUs @@ -103,6 +116,14 @@ in Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient. ''; }; + openFirewall = lib.mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the firewall for ollama. + This adds `services.ollama.port` to `networking.firewall.allowedTCPPorts`. + ''; + }; }; }; @@ -114,7 +135,7 @@ in environment = cfg.environmentVariables // { HOME = cfg.home; OLLAMA_MODELS = cfg.models; - OLLAMA_HOST = cfg.listenAddress; + OLLAMA_HOST = "${cfg.host}:${toString cfg.port}"; }; serviceConfig = { ExecStart = "${lib.getExe ollamaPackage} serve"; @@ -125,6 +146,8 @@ in }; }; + networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; + environment.systemPackages = [ ollamaPackage ]; }; diff --git a/nixpkgs/nixos/modules/services/misc/open-webui.nix b/nixpkgs/nixos/modules/services/misc/open-webui.nix new file mode 100644 index 000000000000..b4016d03f675 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/open-webui.nix @@ -0,0 +1,114 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) types; + + cfg = config.services.open-webui; +in +{ + options = { + services.open-webui = { + enable = lib.mkEnableOption "Open-WebUI server"; + package = lib.mkPackageOption pkgs "open-webui" { }; + + stateDir = lib.mkOption { + type = types.path; + default = "/var/lib/open-webui"; + example = "/home/foo"; + description = "State directory of Open-WebUI."; + }; + + host = lib.mkOption { + type = types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = '' + The host address which the Open-WebUI server HTTP interface listens to. + ''; + }; + + port = lib.mkOption { + type = types.port; + default = 8080; + example = 11111; + description = '' + Which port the Open-WebUI server listens to. + ''; + }; + + environment = lib.mkOption { + type = types.attrsOf types.str; + default = { + SCARF_NO_ANALYTICS = "True"; + DO_NOT_TRACK = "True"; + ANONYMIZED_TELEMETRY = "False"; + }; + example = '' + { + OLLAMA_API_BASE_URL = "http://127.0.0.1:11434"; + # Disable authentication + WEBUI_AUTH = "False"; + } + ''; + description = "Extra environment variables for Open-WebUI"; + }; + + openFirewall = lib.mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the firewall for Open-WebUI. + This adds `services.open-webui.port` to `networking.firewall.allowedTCPPorts`. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.open-webui = { + description = "User-friendly WebUI for LLMs"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + environment = { + STATIC_DIR = "."; + DATA_DIR = "."; + HF_HOME = "."; + SENTENCE_TRANSFORMERS_HOME = "."; + } // cfg.environment; + + serviceConfig = { + ExecStart = "${lib.getExe cfg.package} serve --host ${cfg.host} --port ${toString cfg.port}"; + WorkingDirectory = cfg.stateDir; + StateDirectory = "open-webui"; + RuntimeDirectory = "open-webui"; + RuntimeDirectoryMode = "0755"; + PrivateTmp = true; + DynamicUser = true; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Permission Denied + PrivateUsers = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + }; + + networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; + }; + + meta.maintainers = with lib.maintainers; [ shivaraj-bh ]; +} diff --git a/nixpkgs/nixos/modules/services/misc/pghero.nix b/nixpkgs/nixos/modules/services/misc/pghero.nix new file mode 100644 index 000000000000..39515f10c8e1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/pghero.nix @@ -0,0 +1,142 @@ +{ config, pkgs, lib, utils, ... }: +let + cfg = config.services.pghero; + settingsFormat = pkgs.formats.yaml { }; + settingsFile = settingsFormat.generate "pghero.yaml" cfg.settings; +in +{ + options.services.pghero = { + enable = lib.mkEnableOption "PgHero service"; + package = lib.mkPackageOption pkgs "pghero" { }; + + listenAddress = lib.mkOption { + type = lib.types.str; + example = "[::1]:3000"; + description = '' + `hostname:port` to listen for HTTP traffic. + + This is bound using the systemd socket activation. + ''; + }; + + extraArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Additional command-line arguments for the systemd service. + + Refer to the [Puma web server documentation] for available arguments. + + [Puma web server documentation]: https://puma.io/puma#configuration + ''; + }; + + settings = lib.mkOption { + type = settingsFormat.type; + default = { }; + example = { + databases = { + primary = { + url = "<%= ENV['PRIMARY_DATABASE_URL'] %>"; + }; + }; + }; + description = '' + PgHero configuration. Refer to the [PgHero documentation] for more + details. + + [PgHero documentation]: https://github.com/ankane/pghero/blob/master/guides/Linux.md#multiple-databases + ''; + }; + + environment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = '' + Environment variables to set for the service. Secrets should be + specified using {option}`environmentFile`. + ''; + }; + + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + description = '' + File to load environment variables from. Loaded variables override + values set in {option}`environment`. + ''; + }; + + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "tlskeys" ]; + description = '' + Additional groups for the systemd service. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.sockets.pghero = { + unitConfig.Description = "PgHero HTTP socket"; + wantedBy = [ "sockets.target" ]; + listenStreams = [ cfg.listenAddress ]; + }; + + systemd.services.pghero = { + description = "PgHero performance dashboard for PostgreSQL"; + wantedBy = [ "multi-user.target" ]; + requires = [ "pghero.socket" ]; + after = [ "pghero.socket" "network.target" ]; + + environment = { + RAILS_ENV = "production"; + PGHERO_CONFIG_PATH = settingsFile; + } // cfg.environment; + + serviceConfig = { + Type = "notify"; + WatchdogSec = "10"; + + ExecStart = utils.escapeSystemdExecArgs ([ + (lib.getExe cfg.package) + "--bind-to-activated-sockets" + "only" + ] ++ cfg.extraArgs); + Restart = "always"; + + WorkingDirectory = "${cfg.package}/share/pghero"; + + EnvironmentFile = cfg.environmentFiles; + SupplementaryGroups = cfg.extraGroups; + + DynamicUser = true; + UMask = "0077"; + + ProtectHome = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + PrivateUsers = true; + PrivateDevices = true; + RestrictRealtime = true; + RestrictNamespaces = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + DeviceAllow = [ "" ]; + DevicePolicy = "closed"; + CapabilityBoundingSet = [ "" ]; + MemoryDenyWriteExecute = true; + LockPersonality = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/plex.nix b/nixpkgs/nixos/modules/services/misc/plex.nix index fcd8ebbac6ed..212abda5d1e0 100644 --- a/nixpkgs/nixos/modules/services/misc/plex.nix +++ b/nixpkgs/nixos/modules/services/misc/plex.nix @@ -93,6 +93,17 @@ in ''; }; + accelerationDevices = mkOption { + type = types.listOf types.str; + default = ["*"]; + example = [ "/dev/dri/renderD128" ]; + description = '' + A list of device paths to hardware acceleration devices that Plex should + have access to. This is useful when transcoding media files. + The special value `"*"` will allow all devices. + ''; + }; + package = mkPackageOption pkgs "plex" { extraDescription = '' Plex subscribers may wish to use their own package here, @@ -133,6 +144,24 @@ in KillSignal = "SIGQUIT"; PIDFile = "${cfg.dataDir}/Plex Media Server/plexmediaserver.pid"; Restart = "on-failure"; + + # Hardening + NoNewPrivileges = true; + PrivateTmp = true; + PrivateDevices = cfg.accelerationDevices == []; + DeviceAllow = mkIf (cfg.accelerationDevices != [] && !lib.elem "*" cfg.accelerationDevices) cfg.accelerationDevices; + ProtectSystem = true; + ProtectHome = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK"]; + # This could be made to work if the namespaces needed were known + # RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + MemoryDenyWriteExecute = true; + LockPersonality = true; }; environment = { diff --git a/nixpkgs/nixos/modules/services/misc/portunus.nix b/nixpkgs/nixos/modules/services/misc/portunus.nix index bdb35da788e3..c7abb2cfa2a3 100644 --- a/nixpkgs/nixos/modules/services/misc/portunus.nix +++ b/nixpkgs/nixos/modules/services/misc/portunus.nix @@ -98,6 +98,10 @@ in The OIDC secret must be set as the `DEX_CLIENT_''${id}` environment variable in the [](#opt-services.dex.environmentFile) setting. + + ::: {.note} + Make sure the id only contains characters that are allowed in an environment variable name, e.g. no -. + ::: ''; }; @@ -111,10 +115,7 @@ in ldap = { package = mkOption { type = types.package; - # needs openldap built with a libxcrypt that support crypt sha256 until users have had time to migrate to newer hashes - # Ref: <https://github.com/majewsky/portunus/issues/2> - # TODO: remove in NixOS 24.11 (cf. same note on pkgs/servers/portunus/default.nix) - default = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }; + default = pkgs.openldap; defaultText = lib.literalExpression "pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; }"; description = "The OpenLDAP package to use."; }; diff --git a/nixpkgs/nixos/modules/services/misc/renovate.nix b/nixpkgs/nixos/modules/services/misc/renovate.nix new file mode 100644 index 000000000000..25a719c91cbd --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/renovate.nix @@ -0,0 +1,153 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + mkEnableOption + mkPackageOption + mkOption + types + mkIf + ; + json = pkgs.formats.json { }; + cfg = config.services.renovate; + generateValidatedConfig = + name: value: + pkgs.callPackage ( + { runCommand, jq }: + runCommand name + { + nativeBuildInputs = [ + jq + cfg.package + ]; + value = builtins.toJSON value; + passAsFile = [ "value" ]; + preferLocalBuild = true; + } + '' + jq . "$valuePath"> $out + renovate-config-validator $out + '' + ) { }; + generateConfig = if cfg.validateSettings then generateValidatedConfig else json.generate; +in +{ + meta.maintainers = with lib.maintainers; [ marie natsukium ]; + + options.services.renovate = { + enable = mkEnableOption "renovate"; + package = mkPackageOption pkgs "renovate" { }; + schedule = mkOption { + type = with types; nullOr str; + description = "How often to run renovate. See {manpage}`systemd.time(7)` for the format."; + example = "*:0/10"; + default = null; + }; + credentials = mkOption { + type = with types; attrsOf path; + description = '' + Allows configuring environment variable credentials for renovate, read from files. + This should always be used for passing confidential data to renovate. + ''; + example = { + RENOVATE_TOKEN = "/etc/renovate/token"; + }; + default = { }; + }; + runtimePackages = mkOption { + type = with types; listOf package; + description = "Packages available to renovate."; + default = [ ]; + }; + validateSettings = mkOption { + type = types.bool; + default = true; + description = "Weither to run renovate's config validator on the built configuration."; + }; + settings = mkOption { + type = json.type; + default = { }; + example = { + platform = "gitea"; + endpoint = "https://git.example.com"; + gitAuthor = "Renovate <renovate@example.com>"; + }; + description = '' + Renovate's global configuration. + If you want to pass secrets to renovate, please use {option}`services.renovate.credentials` for that. + ''; + }; + }; + + config = mkIf cfg.enable { + services.renovate.settings = { + cacheDir = "/var/cache/renovate"; + baseDir = "/var/lib/renovate"; + }; + + systemd.services.renovate = { + description = "Renovate dependency updater"; + documentation = [ "https://docs.renovatebot.com/" ]; + after = [ "network.target" ]; + startAt = lib.optional (cfg.schedule != null) cfg.schedule; + path = [ + config.systemd.package + pkgs.git + ] ++ cfg.runtimePackages; + + serviceConfig = { + Type = "oneshot"; + User = "renovate"; + Group = "renovate"; + DynamicUser = true; + LoadCredential = lib.mapAttrsToList (name: value: "SECRET-${name}:${value}") cfg.credentials; + RemainAfterExit = false; + Restart = "on-failure"; + CacheDirectory = "renovate"; + StateDirectory = "renovate"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + PrivateDevices = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + + script = '' + ${lib.concatStringsSep "\n" ( + builtins.map (name: "export ${name}=$(systemd-creds cat 'SECRET-${name}')") ( + lib.attrNames cfg.credentials + ) + )} + exec ${lib.escapeShellArg (lib.getExe cfg.package)} + ''; + + environment = { + RENOVATE_CONFIG_FILE = generateConfig "renovate-config.json" cfg.settings; + HOME = "/var/lib/renovate"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/snapper.nix b/nixpkgs/nixos/modules/services/misc/snapper.nix index 33207ac2b5bd..4e0b4c551e23 100644 --- a/nixpkgs/nixos/modules/services/misc/snapper.nix +++ b/nixpkgs/nixos/modules/services/misc/snapper.nix @@ -108,7 +108,7 @@ in type = types.bool; example = true; description = '' - Set the `persistentTimer` option for the + Set the `Persistent` option for the {manpage}`systemd.timer(5)` which triggers the snapshot immediately if the last trigger was missed (e.g. if the system was powered down). @@ -235,7 +235,7 @@ in timerConfig.OnUnitActiveSec = cfg.cleanupInterval; }; - systemd.services.snapper-boot = lib.optionalAttrs cfg.snapshotRootOnBoot { + systemd.services.snapper-boot = lib.mkIf cfg.snapshotRootOnBoot { description = "Take snapper snapshot of root on boot"; inherit documentation; serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot"; diff --git a/nixpkgs/nixos/modules/services/monitoring/alloy.nix b/nixpkgs/nixos/modules/services/monitoring/alloy.nix new file mode 100644 index 000000000000..abe8fcd7e1be --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/alloy.nix @@ -0,0 +1,80 @@ +{ lib, pkgs, config, ... }: +with lib; +let + cfg = config.services.alloy; +in +{ + meta = { + maintainers = with maintainers; [ flokli hbjydev ]; + }; + + options.services.alloy = { + enable = mkEnableOption "Grafana Alloy"; + + package = mkPackageOption pkgs "grafana-alloy" { }; + + configPath = mkOption { + type = lib.types.path; + default = "/etc/alloy"; + description = '' + Alloy configuration file/directory path. + + We default to `/etc/alloy` here, and expect the user to configure a + configuration file via `environment.etc."alloy/config.alloy"`. + + This allows config reload, contrary to specifying a store path. + A `reloadTrigger` for `config.alloy` is configured. + + Other `*.alloy` files in the same directory (ignoring subdirs) are also + honored, but it's necessary to manually extend + `systemd.services.alloy.reloadTriggers` to enable config reload + during nixos-rebuild switch. + + This can also point to another directory containing `*.alloy` files, or + a single Alloy file in the Nix store (at the cost of reload). + + Component names must be unique across all Alloy configuration files, and + configuration blocks must not be repeated. + + Alloy will continue to run if subsequent reloads of the configuration + file fail, potentially marking components as unhealthy depending on + the nature of the failure. When this happens, Alloy will continue + functioning in the last valid state. + ''; + }; + + extraFlags = mkOption { + type = with lib.types; listOf str; + default = [ ]; + example = [ "--server.http.listen-addr=127.0.0.1:12346" "--disable-reporting" ]; + description = '' + Extra command-line flags passed to {command}`alloy run`. + + See <https://grafana.com/docs/alloy/latest/reference/cli/run/> + ''; + }; + }; + + + config = mkIf cfg.enable { + systemd.services.alloy = { + wantedBy = [ "multi-user.target" ]; + reloadTriggers = [ config.environment.etc."alloy/config.alloy".source or null ]; + serviceConfig = { + Restart = "always"; + DynamicUser = true; + RestartSec = 2; + SupplementaryGroups = [ + # allow to read the systemd journal for loki log forwarding + "systemd-journal" + ]; + ExecStart = "${lib.getExe cfg.package} run ${cfg.configPath} ${escapeShellArgs cfg.extraFlags}"; + ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; + ConfigurationDirectory = "alloy"; + StateDirectory = "alloy"; + WorkingDirectory = "%S/alloy"; + Type = "simple"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix index 9d453c539482..32919950adc1 100644 --- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix +++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix @@ -47,13 +47,6 @@ let datasourceFileOrDir = mkProvisionCfg "datasource" "datasources" cfg.provision.datasources; dashboardFileOrDir = mkProvisionCfg "dashboard" "providers" cfg.provision.dashboards; - notifierConfiguration = { - apiVersion = 1; - notifiers = cfg.provision.notifiers; - }; - - notifierFileOrDir = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration); - generateAlertingProvisioningYaml = x: if (cfg.provision.alerting."${x}".path == null) then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings @@ -74,10 +67,9 @@ let fi ''; provisionConfDir = pkgs.runCommand "grafana-provisioning" { nativeBuildInputs = [ pkgs.xorg.lndir ]; } '' - mkdir -p $out/{alerting,datasources,dashboards,notifiers,plugins} + mkdir -p $out/{alerting,datasources,dashboards,plugins} ${ln { src = datasourceFileOrDir; dir = "datasources"; filename = "datasource"; }} ${ln { src = dashboardFileOrDir; dir = "dashboards"; filename = "dashboard"; }} - ${ln { src = notifierFileOrDir; dir = "notifiers"; filename = "notifier"; }} ${ln { src = rulesFileOrDir; dir = "alerting"; filename = "rules"; }} ${ln { src = contactPointsFileOrDir; dir = "alerting"; filename = "contactPoints"; }} ${ln { src = policiesFileOrDir; dir = "alerting"; filename = "policies"; }} @@ -161,73 +153,13 @@ let }; }; }; - - grafanaTypes.notifierConfig = types.submodule { - options = { - name = mkOption { - type = types.str; - default = "default"; - description = "Notifier name."; - }; - type = mkOption { - type = types.enum [ "dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook" ]; - description = "Notifier type."; - }; - uid = mkOption { - type = types.str; - description = "Unique notifier identifier."; - }; - org_id = mkOption { - type = types.int; - default = 1; - description = "Organization ID."; - }; - org_name = mkOption { - type = types.str; - default = "Main Org."; - description = "Organization name."; - }; - is_default = mkOption { - type = types.bool; - description = "Is the default notifier."; - default = false; - }; - send_reminder = mkOption { - type = types.bool; - default = true; - description = "Should the notifier be sent reminder notifications while alerts continue to fire."; - }; - frequency = mkOption { - type = types.str; - default = "5m"; - description = "How frequently should the notifier be sent reminders."; - }; - disable_resolve_message = mkOption { - type = types.bool; - default = false; - description = "Turn off the message that sends when an alert returns to OK."; - }; - settings = mkOption { - type = types.nullOr types.attrs; - default = null; - description = "Settings for the notifier type."; - }; - secure_settings = mkOption { - type = types.nullOr types.attrs; - default = null; - description = '' - Secure settings for the notifier type. Please note that the contents of this option - will end up in a world-readable Nix store. Use the file provider - pointing at a reasonably secured file in the local filesystem - to work around that. Look at the documentation for details: - <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider> - ''; - }; - }; - }; in { imports = [ + (mkRemovedOptionModule [ "services" "grafana" "provision" "notifiers" ] '' + Notifiers (services.grafana.provision.notifiers) were removed in Grafana 11. + '') + (mkRenamedOptionModule [ "services" "grafana" "protocol" ] [ "services" "grafana" "settings" "server" "protocol" ]) (mkRenamedOptionModule [ "services" "grafana" "addr" ] [ "services" "grafana" "settings" "server" "http_addr" ]) (mkRenamedOptionModule [ "services" "grafana" "port" ] [ "services" "grafana" "settings" "server" "http_port" ]) @@ -1256,15 +1188,6 @@ in }; }; - - notifiers = mkOption { - description = "Grafana notifier configuration."; - default = [ ]; - type = types.listOf grafanaTypes.notifierConfig; - apply = x: map _filter x; - }; - - alerting = { rules = { path = mkOption { @@ -1746,12 +1669,6 @@ in Use file provider or an env-var instead. ''; - # Warn about deprecated notifiers. - deprecatedNotifiers = optional (cfg.provision.notifiers != [ ]) '' - Notifiers are deprecated upstream and will be removed in Grafana 11. - Use `services.grafana.provision.alerting.contactPoints` instead. - ''; - # Ensure that `secureJsonData` of datasources provisioned via `datasources.settings` # only uses file/env providers. secureJsonDataWithoutFileProvider = optional @@ -1770,15 +1687,10 @@ in Declarations in the `secureJsonData`-block of a datasource will be leaked to the Nix store unless a file-provider or an env-var is used! ''; - - notifierSecureSettingsWithoutFileProvider = optional - (any (x: x.secure_settings != null) cfg.provision.notifiers) - "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead."; in passwordWithoutFileProvider - ++ deprecatedNotifiers ++ secureJsonDataWithoutFileProvider - ++ notifierSecureSettingsWithoutFileProvider; + ; environment.systemPackages = [ cfg.package ]; diff --git a/nixpkgs/nixos/modules/services/monitoring/loki.nix b/nixpkgs/nixos/modules/services/monitoring/loki.nix index de4f1bc7aa23..307119ecbf8b 100644 --- a/nixpkgs/nixos/modules/services/monitoring/loki.nix +++ b/nixpkgs/nixos/modules/services/monitoring/loki.nix @@ -94,6 +94,7 @@ in { systemd.services.loki = { description = "Loki Service Daemon"; wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; serviceConfig = let conf = if cfg.configFile == null diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix index 7e707a13b790..4de80acfa9a8 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix @@ -181,6 +181,10 @@ let communicating with external systems (federation, remote storage, Alertmanager). ''; + + query_log_file = mkOpt types.str '' + Path to the file prometheus should write its query log to. + ''; }; }; diff --git a/nixpkgs/nixos/modules/services/monitoring/scrutiny.nix b/nixpkgs/nixos/modules/services/monitoring/scrutiny.nix index 031f5a30cada..c649d333e401 100644 --- a/nixpkgs/nixos/modules/services/monitoring/scrutiny.nix +++ b/nixpkgs/nixos/modules/services/monitoring/scrutiny.nix @@ -140,8 +140,8 @@ in options.api.endpoint = mkOption { type = str; - default = "http://localhost:${toString cfg.settings.web.listen.port}"; - defaultText = literalExpression ''"http://localhost:''${config.services.scrutiny.settings.web.listen.port}"''; + default = "http://${cfg.settings.web.listen.host}:${toString cfg.settings.web.listen.port}"; + defaultText = literalExpression ''"http://''${config.services.scrutiny.settings.web.listen.host}:''${config.services.scrutiny.settings.web.listen.port}"''; description = "Scrutiny app API endpoint for sending metrics to."; }; diff --git a/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix index 7fa471b6404a..dec403df85ea 100644 --- a/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix +++ b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix @@ -103,7 +103,7 @@ in port = mkOption { type = types.port; - default = if cfg.database.type == "mysql" then mysql.port else pgsql.services.port; + default = if cfg.database.type == "mysql" then mysql.port else pgsql.settings.port; defaultText = literalExpression '' if config.${opt.database.type} == "mysql" then config.${options.services.mysql.port} diff --git a/nixpkgs/nixos/modules/services/network-filesystems/davfs2.nix b/nixpkgs/nixos/modules/services/network-filesystems/davfs2.nix index 23c04658031f..9a7d0daa6421 100644 --- a/nixpkgs/nixos/modules/services/network-filesystems/davfs2.nix +++ b/nixpkgs/nixos/modules/services/network-filesystems/davfs2.nix @@ -20,14 +20,10 @@ let else toString value; configFile = pkgs.writeText "davfs2.conf" ( - if (cfg.settings != { }) then - (toINIWithGlobalSection { - mkSectionName = escapeString; - mkKeyValue = k: v: "${k} ${formatValue v}"; - } cfg.settings) - else - cfg.extraConfig - ); + toINIWithGlobalSection { + mkSectionName = escapeString; + mkKeyValue = k: v: "${k} ${formatValue v}"; + } cfg.settings); in { @@ -53,29 +49,6 @@ in ''; }; - extraConfig = mkOption { - type = lines; - default = ""; - example = '' - proxy foo.bar:8080 - use_locks 0 - - [/media/dav] - use_locks 1 - - [/home/otto/mywebspace] - gui_optimize 1 - ''; - description = '' - Extra lines appended to the configuration of davfs2. - See {manpage}`davfs2.conf(5)` for available settings. - - **Note**: Please pass structured settings via - {option}`settings` instead, this option - will get deprecated in the future. - '' ; - }; - settings = mkOption { type = submodule { freeformType = let @@ -109,21 +82,6 @@ in config = mkIf cfg.enable { - assertions = [ - { - assertion = cfg.extraConfig != "" -> cfg.settings == { }; - message = '' - services.davfs2.extraConfig and services.davfs2.settings cannot be used together. - Please prefer using services.davfs2.settings. - ''; - } - ]; - - warnings = optional (cfg.extraConfig != "") '' - services.davfs2.extraConfig will be deprecated in future releases; - please use services.davfs2.settings instead. - ''; - environment.systemPackages = [ pkgs.davfs2 ]; environment.etc."davfs2/davfs2.conf".source = configFile; diff --git a/nixpkgs/nixos/modules/services/network-filesystems/samba.nix b/nixpkgs/nixos/modules/services/network-filesystems/samba.nix index 66ef3f14ed70..c70d0cf7beac 100644 --- a/nixpkgs/nixos/modules/services/network-filesystems/samba.nix +++ b/nixpkgs/nixos/modules/services/network-filesystems/samba.nix @@ -201,14 +201,10 @@ in message = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled"; } ]; - # Always provide a smb.conf to shut up programs like smbclient and smbspool. - environment.etc."samba/smb.conf".source = mkOptionDefault ( - if cfg.enable then configFile - else pkgs.writeText "smb-dummy.conf" "# Samba is disabled." - ); } (mkIf cfg.enable { + environment.etc."samba/smb.conf".source = configFile; system.nssModules = optional cfg.nsswins samba; system.nssDatabases.hosts = optional cfg.nsswins "wins"; diff --git a/nixpkgs/nixos/modules/services/networking/adguardhome.nix b/nixpkgs/nixos/modules/services/networking/adguardhome.nix index df9927351edc..5be3e0bea224 100644 --- a/nixpkgs/nixos/modules/services/networking/adguardhome.nix +++ b/nixpkgs/nixos/modules/services/networking/adguardhome.nix @@ -140,7 +140,7 @@ in { { assertion = cfg.settings != null -> !(hasAttrByPath [ "bind_port" ] cfg.settings); - message = "AdGuard option `settings.bind_host' has been superseded by `services.adguardhome.port'"; + message = "AdGuard option `settings.bind_port' has been superseded by `services.adguardhome.port'"; } { assertion = settings != null -> cfg.mutableSettings @@ -167,8 +167,13 @@ in { preStart = optionalString (settings != null) '' if [ -e "$STATE_DIRECTORY/AdGuardHome.yaml" ] \ && [ "${toString cfg.mutableSettings}" = "1" ]; then + # First run a schema_version update on the existing configuration + # This ensures that both the new config and the existing one have the same schema_version + # Note: --check-config has the side effect of modifying the file at rest! + ${lib.getExe cfg.package} -c "$STATE_DIRECTORY/AdGuardHome.yaml" --check-config + # Writing directly to AdGuardHome.yaml results in empty file - ${pkgs.yaml-merge}/bin/yaml-merge "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" + ${lib.getExe pkgs.yaml-merge} "$STATE_DIRECTORY/AdGuardHome.yaml" "${configFile}" > "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" mv "$STATE_DIRECTORY/AdGuardHome.yaml.tmp" "$STATE_DIRECTORY/AdGuardHome.yaml" else cp --force "${configFile}" "$STATE_DIRECTORY/AdGuardHome.yaml" @@ -178,7 +183,7 @@ in { serviceConfig = { DynamicUser = true; - ExecStart = "${cfg.package}/bin/adguardhome ${args}"; + ExecStart = "${lib.getExe cfg.package} ${args}"; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ] ++ optionals cfg.allowDHCP [ "CAP_NET_RAW" ]; Restart = "always"; diff --git a/nixpkgs/nixos/modules/services/networking/aria2.nix b/nixpkgs/nixos/modules/services/networking/aria2.nix index f32f5682c980..f0d5c5c8a21e 100644 --- a/nixpkgs/nixos/modules/services/networking/aria2.nix +++ b/nixpkgs/nixos/modules/services/networking/aria2.nix @@ -1,98 +1,132 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.services.aria2; homeDir = "/var/lib/aria2"; - - settingsDir = "${homeDir}"; - sessionFile = "${homeDir}/aria2.session"; - downloadDir = "${homeDir}/Downloads"; - - rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to); - - settingsFile = pkgs.writeText "aria2.conf" - '' - dir=${cfg.downloadDir} - listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)} - rpc-listen-port=${toString cfg.rpcListenPort} - ''; - + defaultRpcListenPort = 6800; + defaultDir = "${homeDir}/Downloads"; + + portRangesToString = ranges: lib.concatStringsSep "," (map + (x: + if x.from == x.to + then builtins.toString x.from + else builtins.toString x.from + "-" + builtins.toString x.to + ) + ranges); + + customToKeyValue = lib.generators.toKeyValue { + mkKeyValue = lib.generators.mkKeyValueDefault + { + mkValueString = v: + if builtins.isList v then portRangesToString v + else lib.generators.mkValueStringDefault { } v; + } "="; + }; in { imports = [ - (mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead") + (lib.mkRemovedOptionModule [ "services" "aria2" "rpcSecret" ] "Use services.aria2.rpcSecretFile instead") + (lib.mkRemovedOptionModule [ "services" "aria2" "extraArguments" ] "Use services.aria2.settings instead") + (lib.mkRenamedOptionModule [ "services" "aria2" "downloadDir" ] [ "services" "aria2" "settings" "dir" ]) + (lib.mkRenamedOptionModule [ "services" "aria2" "listenPortRange" ] [ "services" "aria2" "settings" "listen-port" ]) + (lib.mkRenamedOptionModule [ "services" "aria2" "rpcListenPort" ] [ "services" "aria2" "settings" "rpc-listen-port" ]) ]; options = { services.aria2 = { - enable = mkOption { - type = types.bool; + enable = lib.mkOption { + type = lib.types.bool; default = false; description = '' Whether or not to enable the headless Aria2 daemon service. - Aria2 daemon can be controlled via the RPC interface using - one of many WebUI (http://localhost:6800/ by default). + Aria2 daemon can be controlled via the RPC interface using one of many + WebUIs (http://localhost:${toString defaultRpcListenPort}/ by default). - Targets are downloaded to ${downloadDir} by default and are - accessible to users in the "aria2" group. + Targets are downloaded to `${defaultDir}` by default and are + accessible to users in the `aria2` group. ''; }; - openPorts = mkOption { - type = types.bool; + openPorts = lib.mkOption { + type = lib.types.bool; default = false; description = '' - Open listen and RPC ports found in listenPortRange and rpcListenPort - options in the firewall. - ''; - }; - downloadDir = mkOption { - type = types.path; - default = downloadDir; - description = '' - Directory to store downloaded files. - ''; - }; - listenPortRange = mkOption { - type = types.listOf types.attrs; - default = [ { from = 6881; to = 6999; } ]; - description = '' - Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker. + Open listen and RPC ports found in `settings.listen-port` and + `settings.rpc-listen-port` options in the firewall. ''; }; - rpcListenPort = mkOption { - type = types.int; - default = 6800; - description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535"; - }; - rpcSecretFile = mkOption { - type = types.path; + rpcSecretFile = lib.mkOption { + type = lib.types.path; example = "/run/secrets/aria2-rpc-token.txt"; description = '' A file containing the RPC secret authorization token. Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used. ''; }; - extraArguments = mkOption { - type = types.separatedString " "; - example = "--rpc-listen-all --remote-time=true"; - default = ""; + settings = lib.mkOption { description = '' - Additional arguments to be passed to Aria2. + Generates the `aria2.conf` file. Refer to [the documentation][0] for + all possible settings. + + [0]: https://aria2.github.io/manual/en/html/aria2c.html#synopsis ''; + default = { }; + type = lib.types.submodule { + freeformType = with lib.types; attrsOf (oneOf [ bool int float singleLineStr ]); + options = { + save-session = lib.mkOption { + type = lib.types.singleLineStr; + default = "${homeDir}/aria2.session"; + description = "Save error/unfinished downloads to FILE on exit."; + }; + dir = lib.mkOption { + type = lib.types.singleLineStr; + default = defaultDir; + description = "Directory to store downloaded files."; + }; + conf-path = lib.mkOption { + type = lib.types.singleLineStr; + default = "${homeDir}/aria2.conf"; + description = "Configuration file path."; + }; + enable-rpc = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Enable JSON-RPC/XML-RPC server."; + }; + listen-port = lib.mkOption { + type = with lib.types; listOf (attrsOf port); + default = [{ from = 6881; to = 6999; }]; + description = "Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker."; + }; + rpc-listen-port = lib.mkOption { + type = lib.types.port; + default = defaultRpcListenPort; + description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535"; + }; + }; + }; }; }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = cfg.settings.enable-rpc; + message = "RPC has to be enabled, the default module option takes care of that."; + } + { + assertion = !(cfg.settings ? rpc-secret); + message = "Set the RPC secret through services.aria2.rpcSecretFile so it will not end up in the world-readable nix store."; + } + ]; # Need to open ports for proper functioning - networking.firewall = mkIf cfg.openPorts { - allowedUDPPortRanges = config.services.aria2.listenPortRange; - allowedTCPPorts = [ config.services.aria2.rpcListenPort ]; + networking.firewall = lib.mkIf cfg.openPorts { + allowedUDPPortRanges = config.services.aria2.settings.listen-port; + allowedTCPPorts = [ config.services.aria2.settings.rpc-listen-port ]; }; users.users.aria2 = { @@ -107,7 +141,7 @@ in systemd.tmpfiles.rules = [ "d '${homeDir}' 0770 aria2 aria2 - -" - "d '${config.services.aria2.downloadDir}' 0770 aria2 aria2 - -" + "d '${config.services.aria2.settings.dir}' 0770 aria2 aria2 - -" ]; systemd.services.aria2 = { @@ -115,22 +149,25 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; preStart = '' - if [[ ! -e "${sessionFile}" ]] + if [[ ! -e "${cfg.settings.save-session}" ]] then - touch "${sessionFile}" + touch "${cfg.settings.save-session}" fi - cp -f "${settingsFile}" "${settingsDir}/aria2.conf" - echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${settingsDir}/aria2.conf" + cp -f "${pkgs.writeText "aria2.conf" (customToKeyValue cfg.settings)}" "${cfg.settings.conf-path}" + chmod +w "${cfg.settings.conf-path}" + echo "rpc-secret=$(cat "$CREDENTIALS_DIRECTORY/rpcSecretFile")" >> "${cfg.settings.conf-path}" ''; serviceConfig = { Restart = "on-abort"; - ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}"; + ExecStart = "${pkgs.aria2}/bin/aria2c --conf-path=${cfg.settings.conf-path}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; User = "aria2"; Group = "aria2"; - LoadCredential="rpcSecretFile:${cfg.rpcSecretFile}"; + LoadCredential = "rpcSecretFile:${cfg.rpcSecretFile}"; }; }; }; + + meta.maintainers = [ lib.maintainers.timhae ]; } diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix index b912550e1155..272a50eb92de 100644 --- a/nixpkgs/nixos/modules/services/networking/ddclient.nix +++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix @@ -11,7 +11,9 @@ let # This file can be used as a template for configFile or is automatically generated by Nix options. cache=${dataDir}/ddclient.cache foreground=YES - use=${cfg.use} + ${lib.optionalString (cfg.use != "") "use=${cfg.use}"} + ${lib.optionalString (cfg.use == "" && cfg.usev4 != "") "usev4=${cfg.usev4}"} + ${lib.optionalString (cfg.use == "" && cfg.usev6 != "") "usev6=${cfg.usev6}"} login=${cfg.username} password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"} protocol=${cfg.protocol} @@ -163,12 +165,26 @@ with lib; }; use = mkOption { - default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '"; + default = ""; type = str; description = '' Method to determine the IP address to send to the dynamic DNS provider. ''; }; + usev4 = mkOption { + default = "webv4, webv4=checkip.dyndns.com/, webv4-skip='Current IP Address: '"; + type = str; + description = '' + Method to determine the IPv4 address to send to the dynamic DNS provider. Only used if `use` is not set. + ''; + }; + usev6 = mkOption { + default = "webv6, webv6=checkipv6.dyndns.com/, webv6-skip='Current IP Address: '"; + type = str; + description = '' + Method to determine the IPv6 address to send to the dynamic DNS provider. Only used if `use` is not set. + ''; + }; verbose = mkOption { default = false; @@ -204,6 +220,8 @@ with lib; ###### implementation config = mkIf config.services.ddclient.enable { + warnings = lib.optional (cfg.use != "") "Setting `use` is deprecated, ddclient now supports `usev4` and `usev6` for separate IPv4/IPv6 configuration."; + systemd.services.ddclient = { description = "Dynamic DNS Client"; wantedBy = [ "multi-user.target" ]; diff --git a/nixpkgs/nixos/modules/services/networking/frr.nix b/nixpkgs/nixos/modules/services/networking/frr.nix index 7f611ce7b1c7..df2b4035d2f0 100644 --- a/nixpkgs/nixos/modules/services/networking/frr.nix +++ b/nixpkgs/nixos/modules/services/networking/frr.nix @@ -23,10 +23,9 @@ let "pbr" "bfd" "fabric" - "mgmt" ]; - allServices = services ++ [ "zebra" ]; + allServices = services ++ [ "zebra" "mgmt" ]; isEnabled = service: cfg.${service}.enable; @@ -137,6 +136,20 @@ in ''; }; }; + mgmt = (serviceOptions "mgmt") // { + enable = mkOption { + type = types.bool; + default = isEnabled "static"; + defaultText = lib.literalExpression "config.services.frr.static.enable"; + description = '' + Whether to enable the Configuration management daemon. + + The Configuration management daemon is automatically + enabled if needed, at the moment this is when staticd + is enabled. + ''; + }; + }; }; } { options.services.frr = (genAttrs services serviceOptions); } @@ -164,7 +177,7 @@ in environment.etc = let mkEtcLink = service: { - name = "frr/${service}.conf"; + name = "frr/${daemonName service}.conf"; value.source = configFile service; }; in @@ -196,18 +209,18 @@ in unitConfig.Documentation = if service == "zebra" then "man:zebra(8)" else "man:${daemon}(8) man:zebra(8)"; - restartTriggers = [ + restartTriggers = mkIf (service != "mgmt") [ (configFile service) ]; - reloadIfChanged = true; + reloadIfChanged = (service != "mgmt"); serviceConfig = { PIDFile = "frr/${daemon}.pid"; - ExecStart = "${pkgs.frr}/libexec/frr/${daemon} -f /etc/frr/${service}.conf" + ExecStart = "${pkgs.frr}/libexec/frr/${daemon}" + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}" + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}" + " " + (concatStringsSep " " scfg.extraOptions); - ExecReload = "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemonName service} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${service}.conf"; + ExecReload = mkIf (service != "mgmt") "${pkgs.python3.interpreter} ${pkgs.frr}/libexec/frr/frr-reload.py --reload --daemon ${daemon} --bindir ${pkgs.frr}/bin --rundir /run/frr /etc/frr/${daemon}.conf"; Restart = "on-abnormal"; }; }); diff --git a/nixpkgs/nixos/modules/services/networking/git-daemon.nix b/nixpkgs/nixos/modules/services/networking/git-daemon.nix index 6be72505c216..522e6b14f868 100644 --- a/nixpkgs/nixos/modules/services/networking/git-daemon.nix +++ b/nixpkgs/nixos/modules/services/networking/git-daemon.nix @@ -27,6 +27,8 @@ in ''; }; + package = mkPackageOption pkgs "git" { }; + basePath = mkOption { type = types.str; default = ""; @@ -119,7 +121,7 @@ in systemd.services.git-daemon = { after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - script = "${pkgs.git}/bin/git daemon --reuseaddr " + script = "${getExe cfg.package} daemon --reuseaddr " + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ") + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ") + "--port=${toString cfg.port} --user=${cfg.user} --group=${cfg.group} ${cfg.options} " diff --git a/nixpkgs/nixos/modules/services/networking/inadyn.nix b/nixpkgs/nixos/modules/services/networking/inadyn.nix index baa4302096c2..7022673538c8 100644 --- a/nixpkgs/nixos/modules/services/networking/inadyn.nix +++ b/nixpkgs/nixos/modules/services/networking/inadyn.nix @@ -202,7 +202,7 @@ in startAt = cfg.interval; serviceConfig = { Type = "oneshot"; - ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY}/inadyn -1 --foreground -l ${cfg.logLevel}''; + ExecStart = ''${lib.getExe pkgs.inadyn} -f ${configFile} --cache-dir ''${CACHE_DIRECTORY} -1 --foreground -l ${cfg.logLevel}''; LoadCredential = "config:${configFile}"; CacheDirectory = "inadyn"; diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix index 66173c145d16..11add600b66f 100644 --- a/nixpkgs/nixos/modules/services/networking/kea.nix +++ b/nixpkgs/nixos/modules/services/networking/kea.nix @@ -278,6 +278,9 @@ in "https://kea.readthedocs.io/en/kea-${package.version}/arm/agent.html" ]; + wants = [ + "network-online.target" + ]; after = [ "network-online.target" "time-sync.target" diff --git a/nixpkgs/nixos/modules/services/networking/mihomo.nix b/nixpkgs/nixos/modules/services/networking/mihomo.nix index 312530caeaad..d4bb10496279 100644 --- a/nixpkgs/nixos/modules/services/networking/mihomo.nix +++ b/nixpkgs/nixos/modules/services/networking/mihomo.nix @@ -25,6 +25,7 @@ in webui = lib.mkOption { default = null; type = lib.types.nullOr lib.types.path; + example = lib.literalExpression "pkgs.metacubexd"; description = '' Local web interface to use. diff --git a/nixpkgs/nixos/modules/services/networking/mycelium.nix b/nixpkgs/nixos/modules/services/networking/mycelium.nix index 9487a5daafee..0d0b2945af4c 100644 --- a/nixpkgs/nixos/modules/services/networking/mycelium.nix +++ b/nixpkgs/nixos/modules/services/networking/mycelium.nix @@ -60,6 +60,8 @@ in networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ 9651 ]; networking.firewall.allowedUDPPorts = lib.optionals cfg.openFirewall [ 9650 9651 ]; + environment.systemPackages = [ cfg.package ]; + systemd.services.mycelium = { description = "Mycelium network"; after = [ "network.target" ]; diff --git a/nixpkgs/nixos/modules/services/networking/netbird/coturn.nix b/nixpkgs/nixos/modules/services/networking/netbird/coturn.nix index 746d70a07250..29ff1e8fc15e 100644 --- a/nixpkgs/nixos/modules/services/networking/netbird/coturn.nix +++ b/nixpkgs/nixos/modules/services/networking/netbird/coturn.nix @@ -60,6 +60,7 @@ in default = null; description = '' The password of the user used by netbird to connect to the coturn server. + Be advised this will be world readable in the nix store. ''; }; @@ -142,7 +143,11 @@ in ]; }); - security.acme.certs.${cfg.domain}.postRun = optionalString cfg.useAcmeCertificates "systemctl restart coturn.service"; + security.acme.certs = mkIf cfg.useAcmeCertificates { + ${cfg.domain}.postRun = '' + systemctl restart coturn.service + ''; + }; networking.firewall = { allowedUDPPorts = cfg.openPorts; diff --git a/nixpkgs/nixos/modules/services/networking/netbird/server.nix b/nixpkgs/nixos/modules/services/networking/netbird/server.nix index a4de0fda6a13..2b6ad696646e 100644 --- a/nixpkgs/nixos/modules/services/networking/netbird/server.nix +++ b/nixpkgs/nixos/modules/services/networking/netbird/server.nix @@ -2,6 +2,7 @@ let inherit (lib) + mkDefault mkEnableOption mkIf mkOption @@ -15,7 +16,7 @@ in { meta = { - maintainers = with lib.maintainers; [ thubrecht ]; + maintainers = with lib.maintainers; [thubrecht patrickdag]; doc = ./server.md; }; @@ -41,26 +42,46 @@ in config = mkIf cfg.enable { services.netbird.server = { dashboard = { - inherit (cfg) enable domain enableNginx; + domain = mkDefault cfg.domain; + enable = mkDefault cfg.enable; + enableNginx = mkDefault cfg.enableNginx; managementServer = "https://${cfg.domain}"; }; management = { - inherit (cfg) enable domain enableNginx; + domain = mkDefault cfg.domain; + enable = mkDefault cfg.enable; + enableNginx = mkDefault cfg.enableNginx; } - // (optionalAttrs cfg.coturn.enable { + // (optionalAttrs cfg.coturn.enable rec { turnDomain = cfg.domain; turnPort = config.services.coturn.tls-listening-port; + # We cannot merge a list of attrsets so we have to redefine the whole list + settings = { + TURNConfig.Turns = mkDefault [ + { + Proto = "udp"; + URI = "turn:${turnDomain}:${builtins.toString turnPort}"; + Username = "netbird"; + Password = + if (cfg.coturn.password != null) + then cfg.coturn.password + else {_secret = cfg.coturn.passwordFile;}; + } + ]; + }; }); signal = { - inherit (cfg) enable domain enableNginx; + domain = mkDefault cfg.domain; + enable = mkDefault cfg.enable; + enableNginx = mkDefault cfg.enableNginx; }; coturn = { - inherit (cfg) domain; + domain = mkDefault cfg.domain; }; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/oink.nix b/nixpkgs/nixos/modules/services/networking/oink.nix new file mode 100644 index 000000000000..cd0fdf172331 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/oink.nix @@ -0,0 +1,84 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.oink; + makeOinkConfig = attrs: (pkgs.formats.json { }).generate + "oink.json" (mapAttrs' (k: v: nameValuePair (toLower k) v) attrs); + oinkConfig = makeOinkConfig { + global = cfg.settings; + domains = cfg.domains; + }; +in +{ + options.services.oink = { + enable = mkEnableOption "Oink, a dynamic DNS client for Porkbun"; + package = mkPackageOption pkgs "oink" { }; + settings = { + apiKey = mkOption { + type = types.str; + description = "API key to use when modifying DNS records."; + }; + secretApiKey = mkOption { + type = types.str; + description = "Secret API key to use when modifying DNS records."; + }; + interval = mkOption { + # https://github.com/rlado/oink/blob/v1.1.1/src/main.go#L364 + type = types.ints.between 60 172800; # 48 hours + default = 900; + description = "Seconds to wait before sending another request."; + }; + ttl = mkOption { + type = types.ints.between 600 172800; + default = 600; + description = '' + The TTL ("Time to Live") value to set for your DNS records. + + The TTL controls how long in seconds your records will be cached + for. A smaller value will allow the record to update quicker. + ''; + }; + }; + domains = mkOption { + type = with types; listOf (attrsOf anything); + default = []; + example = [ + { + domain = "nixos.org"; + subdomain = ""; + ttl = 1200; + } + { + domain = "nixos.org"; + subdomain = "hydra"; + } + ]; + description = '' + List of attribute sets containing configuration for each domain. + + Each attribute set must have two attributes, one named *domain* + and another named *subdomain*. The domain attribute must specify + the root domain that you want to configure, and the subdomain + attribute must specify its subdomain if any. If you want to + configure the root domain rather than a subdomain, leave the + subdomain attribute as an empty string. + + Additionally, you can use attributes from *services.oink.settings* + to override settings per-domain. + + Every domain listed here *must* have API access enabled in + Porkbun's control panel. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.oink = { + description = "Dynamic DNS client for Porkbun"; + wantedBy = [ "multi-user.target" ]; + script = "${cfg.package}/bin/oink -c ${oinkConfig}"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/rosenpass.nix b/nixpkgs/nixos/modules/services/networking/rosenpass.nix index 66b6f960a81a..92ecc1cb31a3 100644 --- a/nixpkgs/nixos/modules/services/networking/rosenpass.nix +++ b/nixpkgs/nixos/modules/services/networking/rosenpass.nix @@ -130,8 +130,8 @@ in relevant = config.systemd.network.enable; root = config.systemd.network.netdevs; peer = (x: x.wireguardPeers); - key = (x: if x.wireguardPeerConfig ? PublicKey then x.wireguardPeerConfig.PublicKey else null); - description = "${options.systemd.network.netdevs}.\"<name>\".wireguardPeers.*.wireguardPeerConfig.PublicKey"; + key = x: x.PublicKey or null; + description = "${options.systemd.network.netdevs}.\"<name>\".wireguardPeers.*.PublicKey"; } { relevant = config.networking.wireguard.enable; diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix index 57ca2b85bed2..528cb9b2bc96 100644 --- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix @@ -5,11 +5,11 @@ with lib; let # The splicing information needed for nativeBuildInputs isn't available - # on the derivations likely to be used as `cfgc.package`. + # on the derivations likely to be used as `cfg.package`. # This middle-ground solution ensures *an* sshd can do their basic validation # on the configuration. validationPackage = if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform - then cfgc.package + then cfg.package else pkgs.buildPackages.openssh; # dont use the "=" operator @@ -169,6 +169,13 @@ in ''; }; + package = mkOption { + type = types.package; + default = config.programs.ssh.package; + defaultText = literalExpression "programs.ssh.package"; + description = "OpenSSH package to use for sshd."; + }; + startWhenNeeded = mkOption { type = types.bool; default = false; @@ -342,7 +349,7 @@ in freeformType = settingsFormat.type; options = { AuthorizedPrincipalsFile = mkOption { - type = types.str; + type = types.nullOr types.str; default = "none"; # upstream default description = '' Specifies a file that lists principal names that are accepted for certificate authentication. The default @@ -350,16 +357,18 @@ in ''; }; LogLevel = mkOption { - type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ]; + type = types.nullOr (types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ]); default = "INFO"; # upstream default description = '' Gives the verbosity level that is used when logging messages from sshd(8). Logging with a DEBUG level violates the privacy of users and is not recommended. ''; }; - UsePAM = mkEnableOption "PAM authentication" // { default = true; }; + UsePAM = + mkEnableOption "PAM authentication" + // { default = true; type = types.nullOr types.bool; }; UseDns = mkOption { - type = types.bool; + type = types.nullOr types.bool; # apply if cfg.useDns then "yes" else "no" default = false; description = '' @@ -370,14 +379,14 @@ in ''; }; X11Forwarding = mkOption { - type = types.bool; + type = types.nullOr types.bool; default = false; description = '' Whether to allow X11 connections to be forwarded. ''; }; PasswordAuthentication = mkOption { - type = types.bool; + type = types.nullOr types.bool; default = true; description = '' Specifies whether password authentication is allowed. @@ -385,20 +394,20 @@ in }; PermitRootLogin = mkOption { default = "prohibit-password"; - type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]; + type = types.nullOr (types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]); description = '' Whether the root user can login using ssh. ''; }; KbdInteractiveAuthentication = mkOption { - type = types.bool; + type = types.nullOr types.bool; default = true; description = '' Specifies whether keyboard-interactive authentication is allowed. ''; }; GatewayPorts = mkOption { - type = types.str; + type = types.nullOr types.str; default = "no"; description = '' Specifies whether remote hosts are allowed to connect to @@ -407,7 +416,7 @@ in ''; }; KexAlgorithms = mkOption { - type = types.listOf types.str; + type = types.nullOr (types.listOf types.str); default = [ "sntrup761x25519-sha512@openssh.com" "curve25519-sha256" @@ -424,7 +433,7 @@ in ''; }; Macs = mkOption { - type = types.listOf types.str; + type = types.nullOr (types.listOf types.str); default = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha2-256-etm@openssh.com" @@ -440,14 +449,14 @@ in ''; }; StrictModes = mkOption { - type = types.bool; + type = types.nullOr (types.bool); default = true; description = '' Whether sshd should check file modes and ownership of directories ''; }; Ciphers = mkOption { - type = types.listOf types.str; + type = types.nullOr (types.listOf types.str); default = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" @@ -502,7 +511,9 @@ in ''; }; # Disabled by default, since pam_motd handles this. - PrintMotd = mkEnableOption "printing /etc/motd when a user logs in interactively"; + PrintMotd = + mkEnableOption "printing /etc/motd when a user logs in interactively" + // { type = types.nullOr types.bool; }; }; }); }; @@ -546,8 +557,8 @@ in }); users.groups.sshd = {}; - services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli"; - services.openssh.sftpServerExecutable = mkDefault "${cfgc.package}/libexec/sftp-server"; + services.openssh.moduliFile = mkDefault "${cfg.package}/etc/ssh/moduli"; + services.openssh.sftpServerExecutable = mkDefault "${cfg.package}/libexec/sftp-server"; environment.etc = authKeysFiles // authPrincipalsFiles // { "ssh/moduli".source = cfg.moduliFile; @@ -561,7 +572,7 @@ in wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target"; after = [ "network.target" ]; stopIfChanged = false; - path = [ cfgc.package pkgs.gawk ]; + path = [ cfg.package pkgs.gawk ]; environment.LD_LIBRARY_PATH = nssModulesPath; restartTriggers = optionals (!cfg.startWhenNeeded) [ @@ -595,7 +606,7 @@ in serviceConfig = { ExecStart = (optionalString cfg.startWhenNeeded "-") + - "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + + "${cfg.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + "-D " + # don't detach into a daemon process "-f /etc/ssh/sshd_config"; KillMode = "process"; @@ -641,7 +652,10 @@ in security.pam.services.sshd = lib.mkIf cfg.settings.UsePAM { startSession = true; showMotd = true; - unixAuth = cfg.settings.PasswordAuthentication; + unixAuth = + if cfg.settings.PasswordAuthentication == true + then true + else false; }; # These values are merged with the ones defined externally, see: @@ -703,6 +717,10 @@ in assertions = [{ assertion = if cfg.settings.X11Forwarding then cfgc.setXAuthLocation else true; message = "cannot enable X11 forwarding without setting xauth location";} + { assertion = (builtins.match "(.*\n)?(\t )*[Kk][Ee][Rr][Bb][Ee][Rr][Oo][Ss][Aa][Uu][Tt][Hh][Ee][Nn][Tt][Ii][Cc][Aa][Tt][Ii][Oo][Nn][ |\t|=|\"]+yes.*" "${configFile}\n${cfg.extraConfig}") != null -> cfgc.package.withKerberos; + message = "cannot enable Kerberos authentication without using a package with Kerberos support";} + { assertion = (builtins.match "(.*\n)?(\t )*[Gg][Ss][Ss][Aa][Pp][Ii][Aa][Uu][Tt][Hh][Ee][Nn][Tt][Ii][Cc][Aa][Tt][Ii][Oo][Nn][ |\t|=|\"]+yes.*" "${configFile}\n${cfg.extraConfig}") != null -> cfgc.package.withKerberos; + message = "cannot enable GSSAPI authentication without using a package with Kerberos support";} (let duplicates = # Filter out the groups with more than 1 element diff --git a/nixpkgs/nixos/modules/services/networking/tailscale-auth.nix b/nixpkgs/nixos/modules/services/networking/tailscale-auth.nix index c3a515212e78..f21d1f108911 100644 --- a/nixpkgs/nixos/modules/services/networking/tailscale-auth.nix +++ b/nixpkgs/nixos/modules/services/networking/tailscale-auth.nix @@ -14,7 +14,7 @@ let in { options.services.tailscaleAuth = { - enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate users via tailscale."; + enable = mkEnableOption "tailscale.nginx-auth, to authenticate users via tailscale"; package = mkPackageOption pkgs "tailscale-nginx-auth" {}; diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix index a79e47d8491b..a690dc610e82 100644 --- a/nixpkgs/nixos/modules/services/networking/tailscale.nix +++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix @@ -61,12 +61,21 @@ in { }; extraUpFlags = mkOption { - description = "Extra flags to pass to {command}`tailscale up`."; + description = '' + Extra flags to pass to {command}`tailscale up`. Only applied if `authKeyFile` is specified."; + ''; type = types.listOf types.str; default = []; example = ["--ssh"]; }; + extraSetFlags = mkOption { + description = "Extra flags to pass to {command}`tailscale set`."; + type = types.listOf types.str; + default = []; + example = ["--advertise-exit-node"]; + }; + extraDaemonFlags = mkOption { description = "Extra flags to pass to {command}`tailscaled`."; type = types.listOf types.str; @@ -120,6 +129,18 @@ in { ''; }; + systemd.services.tailscaled-set = mkIf (cfg.extraSetFlags != []) { + after = ["tailscaled.service"]; + wants = ["tailscaled.service"]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + }; + script = '' + ${cfg.package}/bin/tailscale set ${escapeShellArgs cfg.extraSetFlags} + ''; + }; + boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") { "net.ipv4.conf.all.forwarding" = mkOverride 97 true; "net.ipv6.conf.all.forwarding" = mkOverride 97 true; diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix index 3f68af3a86c9..81abae2c9303 100644 --- a/nixpkgs/nixos/modules/services/networking/wireguard.nix +++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix @@ -80,6 +80,15 @@ let description = "Commands called at the end of the interface setup."; }; + preShutdown = mkOption { + example = literalExpression ''"''${pkgs.iproute2}/bin/ip netns del foo"''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Commands called before shutting down the interface. + ''; + }; + postShutdown = mkOption { example = literalExpression ''"''${pkgs.openresolv}/bin/resolvconf -d wg0"''; default = ""; @@ -497,6 +506,7 @@ let ''; postStop = '' + ${values.preShutdown} ${ipPostMove} link del dev "${name}" ${values.postShutdown} ''; diff --git a/nixpkgs/nixos/modules/services/networking/wstunnel.nix b/nixpkgs/nixos/modules/services/networking/wstunnel.nix index efb65aead116..1b169567624c 100644 --- a/nixpkgs/nixos/modules/services/networking/wstunnel.nix +++ b/nixpkgs/nixos/modules/services/networking/wstunnel.nix @@ -7,6 +7,9 @@ let (name: value: if value == true then "--${name}" else "--${name}=${value}") attrs ); + + hostPortToString = { host, port }: "${host}:${builtins.toString port}"; + hostPortSubmodule = { options = { host = mkOption { @@ -19,28 +22,7 @@ let }; }; }; - localRemoteSubmodule = { - options = { - local = mkOption { - description = "Local address and port to listen on."; - type = types.submodule hostPortSubmodule; - example = { - host = "127.0.0.1"; - port = 51820; - }; - }; - remote = mkOption { - description = "Address and port on remote to forward traffic to."; - type = types.submodule hostPortSubmodule; - example = { - host = "127.0.0.1"; - port = 51820; - }; - }; - }; - }; - hostPortToString = { host, port }: "${host}:${builtins.toString port}"; - localRemoteToString = { local, remote }: utils.escapeSystemdExecArg "${hostPortToString local}:${hostPortToString remote}"; + commonOptions = { enable = mkOption { description = "Whether to enable this `wstunnel` instance."; @@ -66,10 +48,16 @@ let }; }; - verboseLogging = mkOption { - description = "Enable verbose logging."; - type = types.bool; - default = false; + loggingLevel = mkOption { + description = '' + Passed to --log-lvl + + Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF + For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) + ''; + type = types.nullOr types.str; + example = "INFO"; + default = null; }; environmentFile = mkOption { @@ -99,11 +87,12 @@ let restrictTo = mkOption { description = "Accepted traffic will be forwarded only to this service. Set to `null` to allow forwarding to arbitrary addresses."; - type = types.nullOr (types.submodule hostPortSubmodule); - example = { + type = types.listOf (types.submodule hostPortSubmodule); + default = []; + example = [{ host = "127.0.0.1"; port = 51820; - }; + }]; }; enableHTTPS = mkOption { @@ -134,59 +123,36 @@ let }; }; }; + clientSubmodule = { config, ... }: { options = commonOptions // { connectTo = mkOption { description = "Server address and port to connect to."; - type = types.submodule hostPortSubmodule; - example = { - host = "example.com"; - }; - }; - - enableHTTPS = mkOption { - description = "Enable HTTPS when connecting to the server."; - type = types.bool; - default = true; + type = types.str; + example = "https://wstunnel.server.com:8443"; }; localToRemote = mkOption { - description = "Local hosts and ports to listen on, plus the hosts and ports on remote to forward traffic to. Setting a local port to a value less than 1024 will additionally give the process the required CAP_NET_BIND_SERVICE capability."; - type = types.listOf (types.submodule localRemoteSubmodule); + description = ''Listen on local and forwards traffic from remote.''; + type = types.listOf (types.str); default = []; - example = [ { - local = { - host = "127.0.0.1"; - port = 8080; - }; - remote = { - host = "127.0.0.1"; - port = 8080; - }; - } ]; + example = [ + "tcp://1212:google.com:443" + "unix:///tmp/wstunnel.sock:g.com:443" + ]; }; - dynamicToRemote = mkOption { - description = "Host and port for the SOCKS5 proxy to dynamically forward traffic to. Leave this at `null` to disable the SOCKS5 proxy. Setting the port to a value less than 1024 will additionally give the service the required CAP_NET_BIND_SERVICE capability."; - type = types.nullOr (types.submodule hostPortSubmodule); - default = null; - example = { - host = "127.0.0.1"; - port = 1080; - }; - }; - - udp = mkOption { - description = "Whether to forward UDP instead of TCP traffic."; - type = types.bool; - default = false; + remoteToLocal = mkOption { + description = "Listen on remote and forwards traffic from local. Only tcp is supported"; + type = types.listOf (types.str); + default = []; + example = [ + "tcp://1212:google.com:443" + "unix://wstunnel.sock:g.com:443" + ]; }; - udpTimeout = mkOption { - description = "When using UDP forwarding, timeout in seconds after which the tunnel connection is closed. `-1` means no timeout."; - type = types.int; - default = 30; - }; + addNetBind = mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024"; httpProxy = mkOption { description = '' @@ -214,12 +180,6 @@ let example = "wstunnel"; }; - hostHeader = mkOption { - description = "Use this as the HTTP host header instead of the real hostname. Useful for circumventing hostname-based firewalls."; - type = types.nullOr types.str; - default = null; - }; - tlsSNI = mkOption { description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls."; type = types.nullOr types.str; @@ -234,7 +194,7 @@ let # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval. websocketPingInterval = mkOption { - description = "Do a heartbeat ping every N seconds to keep up the websocket connection."; + description = "Frequency at which the client will send websocket ping to the server."; type = types.nullOr types.ints.unsigned; default = null; }; @@ -261,6 +221,7 @@ let }; }; }; + generateServerUnit = name: serverCfg: { name = "wstunnel-server-${name}"; value = { @@ -282,11 +243,11 @@ let else tlsKey; in '' ${package}/bin/wstunnel \ - --server \ - ${optionalString (restrictTo != null) "--restrictTo=${utils.escapeSystemdExecArg (hostPortToString restrictTo)}"} \ - ${optionalString (resolvedTlsCertificate != null) "--tlsCertificate=${utils.escapeSystemdExecArg resolvedTlsCertificate}"} \ - ${optionalString (resolvedTlsKey != null) "--tlsKey=${utils.escapeSystemdExecArg resolvedTlsKey}"} \ - ${optionalString verboseLogging "--verbose"} \ + server \ + ${concatStringsSep " " (builtins.map (hostPair: "--restrict-to ${utils.escapeSystemdExecArg (hostPortToString hostPair)}") restrictTo)} \ + ${optionalString (resolvedTlsCertificate != null) "--tls-certificate ${utils.escapeSystemdExecArg resolvedTlsCertificate}"} \ + ${optionalString (resolvedTlsKey != null) "--tls-private-key ${utils.escapeSystemdExecArg resolvedTlsKey}"} \ + ${optionalString (loggingLevel != null) "--log-lvl ${loggingLevel}"} \ ${attrsToArgs extraArgs} \ ${utils.escapeSystemdExecArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"} ''; @@ -304,10 +265,10 @@ let ProtectControlGroups = true; PrivateDevices = true; RestrictSUIDSGID = true; - }; }; }; + generateClientUnit = name: clientCfg: { name = "wstunnel-client-${name}"; value = { @@ -319,28 +280,25 @@ let serviceConfig = { Type = "simple"; ExecStart = with clientCfg; '' - ${package}/bin/wstunnel \ - ${concatStringsSep " " (builtins.map (x: "--localToRemote=${localRemoteToString x}") localToRemote)} \ - ${concatStringsSep " " (mapAttrsToList (n: v: "--customHeaders=\"${n}: ${v}\"") customHeaders)} \ - ${optionalString (dynamicToRemote != null) "--dynamicToRemote=${utils.escapeSystemdExecArg (hostPortToString dynamicToRemote)}"} \ - ${optionalString udp "--udp"} \ - ${optionalString (httpProxy != null) "--httpProxy=${httpProxy}"} \ - ${optionalString (soMark != null) "--soMark=${toString soMark}"} \ - ${optionalString (upgradePathPrefix != null) "--upgradePathPrefix=${upgradePathPrefix}"} \ - ${optionalString (hostHeader != null) "--hostHeader=${hostHeader}"} \ - ${optionalString (tlsSNI != null) "--tlsSNI=${tlsSNI}"} \ - ${optionalString tlsVerifyCertificate "--tlsVerifyCertificate"} \ - ${optionalString (websocketPingInterval != null) "--websocketPingFrequency=${toString websocketPingInterval}"} \ - ${optionalString (upgradeCredentials != null) "--upgradeCredentials=${upgradeCredentials}"} \ - --udpTimeoutSec=${toString udpTimeout} \ - ${optionalString verboseLogging "--verbose"} \ + ${package}/bin/wstunnel client \ + ${concatStringsSep " " (builtins.map (x: "--local-to-remote ${x}") localToRemote)} \ + ${concatStringsSep " " (builtins.map (x: "--remote-to-local ${x}") remoteToLocal)} \ + ${concatStringsSep " " (mapAttrsToList (n: v: "--http-headers \"${n}: ${v}\"") customHeaders)} \ + ${optionalString (httpProxy != null) "--http-proxy ${httpProxy}"} \ + ${optionalString (soMark != null) "--socket-so-mark=${toString soMark}"} \ + ${optionalString (upgradePathPrefix != null) "--http-upgrade-path-prefix ${upgradePathPrefix}"} \ + ${optionalString (tlsSNI != null) "--tls-sni-override ${tlsSNI}"} \ + ${optionalString tlsVerifyCertificate "--tls-verify-certificate"} \ + ${optionalString (websocketPingInterval != null) "--websocket-ping-frequency-sec ${toString websocketPingInterval}"} \ + ${optionalString (upgradeCredentials != null) "--http-upgrade-credentials ${upgradeCredentials}"} \ + ${optionalString (loggingLevel != null) "--log-lvl ${loggingLevel}"} \ ${attrsToArgs extraArgs} \ - ${utils.escapeSystemdExecArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString connectTo}"} + ${utils.escapeSystemdExecArg connectTo} ''; EnvironmentFile = optional (clientCfg.environmentFile != null) clientCfg.environmentFile; DynamicUser = true; PrivateTmp = true; - AmbientCapabilities = (optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]) ++ (optionals ((clientCfg.dynamicToRemote.port or 1024) < 1024 || (any (x: x.local.port < 1024) clientCfg.localToRemote)) [ "CAP_NET_BIND_SERVICE" ]); + AmbientCapabilities = (optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]) ++ (optionals (clientCfg.addNetBind) [ "CAP_NET_BIND_SERVICE" ]); NoNewPrivileges = true; RestrictNamespaces = "uts ipc pid user cgroup"; ProtectSystem = "strict"; @@ -363,14 +321,17 @@ in { default = {}; example = { "wg-tunnel" = { - listen.port = 8080; + listen = { + host = "0.0.0.0"; + port = 8080; + }; enableHTTPS = true; tlsCertificate = "/var/lib/secrets/fullchain.pem"; tlsKey = "/var/lib/secrets/key.pem"; - restrictTo = { + restrictTo = [{ host = "127.0.0.1"; port = 51820; - }; + }]; }; }; }; @@ -381,22 +342,15 @@ in { default = {}; example = { "wg-tunnel" = { - connectTo = { - host = "example.com"; - port = 8080; - }; - enableHTTPS = true; - localToRemote = { - local = { - host = "127.0.0.1"; - port = 51820; - }; - remote = { - host = "127.0.0.1"; - port = 51820; - }; - }; - udp = true; + connectTo = "https://wstunnel.server.com:8443"; + localToRemote = [ + "tcp://1212:google.com:443" + "tcp://2:n.lan:4?proxy_protocol" + ]; + remoteToLocal = [ + "socks5://[::1]:1212" + "unix://wstunnel.sock:g.com:443" + ]; }; }; }; @@ -418,12 +372,12 @@ in { ''; }) cfg.servers) ++ (mapAttrsToList (name: clientCfg: { - assertion = !(clientCfg.localToRemote == [] && clientCfg.dynamicToRemote == null); + assertion = !(clientCfg.localToRemote == [] && clientCfg.remoteToLocal == []); message = '' - Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".dynamicToRemote must be set. + Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set. ''; }) cfg.clients); }; - meta.maintainers = with maintainers; [ alyaeanyx ]; + meta.maintainers = with maintainers; [ alyaeanyx neverbehave ]; } diff --git a/nixpkgs/nixos/modules/services/search/qdrant.nix b/nixpkgs/nixos/modules/services/search/qdrant.nix index f28178a5f175..41a4e9b41f6d 100644 --- a/nixpkgs/nixos/modules/services/search/qdrant.nix +++ b/nixpkgs/nixos/modules/services/search/qdrant.nix @@ -60,6 +60,7 @@ in { config = mkIf cfg.enable { services.qdrant.settings = { + service.static_content_dir = mkDefault pkgs.qdrant-web-ui; storage.storage_path = mkDefault "/var/lib/qdrant/storage"; storage.snapshots_path = mkDefault "/var/lib/qdrant/snapshots"; # The following default values are the same as in the default config, diff --git a/nixpkgs/nixos/modules/services/search/quickwit.nix b/nixpkgs/nixos/modules/services/search/quickwit.nix new file mode 100644 index 000000000000..6b2db935cf0b --- /dev/null +++ b/nixpkgs/nixos/modules/services/search/quickwit.nix @@ -0,0 +1,190 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.quickwit; + + settingsFormat = pkgs.formats.yaml {}; + quickwitYml = settingsFormat.generate "quickwit.yml" cfg.settings; + + usingDefaultDataDir = cfg.dataDir == "/var/lib/quickwit"; + usingDefaultUserAndGroup = cfg.user == "quickwit" && cfg.group == "quickwit"; +in +{ + + options.services.quickwit = { + enable = mkEnableOption "Quickwit"; + + package = lib.mkPackageOption pkgs "Quickwit" { + default = [ "quickwit" ]; + }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options."rest" = lib.mkOption { + default = {}; + description = '' + Rest server configuration for Quickwit + ''; + + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options."listen_port" = lib.mkOption { + type = lib.types.port; + default = 7280; + description = '' + The port to listen on for HTTP REST traffic. + ''; + }; + }; + }; + + options."grpc_listen_port" = lib.mkOption { + type = lib.types.port; + default = 7281; + description = '' + The port to listen on for gRPC traffic. + ''; + }; + + options."listen_address" = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = '' + Listen address of Quickwit. + ''; + }; + + options."version" = lib.mkOption { + type = lib.types.float; + default = 0.7; + description = '' + Configuration file version. + ''; + }; + }; + + default = {}; + + description = '' + Quickwit configuration. + ''; + }; + + dataDir = lib.mkOption { + type = lib.types.path; + default = "/var/lib/quickwit"; + apply = converge (removeSuffix "/"); + description = '' + Data directory for Quickwit. If you change this, you need to + manually create the directory. You also need to create the + `quickwit` user and group, or change + [](#opt-services.quickwit.user) and + [](#opt-services.quickwit.group) to existing ones with + access to the directory. + ''; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "quickwit"; + description = '' + The user Quickwit runs as. Should be left at default unless + you have very specific needs. + ''; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "quickwit"; + description = '' + The group quickwit runs as. Should be left at default unless + you have very specific needs. + ''; + }; + + extraFlags = lib.mkOption { + description = "Extra command line options to pass to Quickwit."; + default = [ ]; + type = lib.types.listOf lib.types.str; + }; + + restartIfChanged = lib.mkOption { + type = lib.types.bool; + description = '' + Automatically restart the service on config change. + This can be set to false to defer restarts on a server or cluster. + Please consider the security implications of inadvertently running an older version, + and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option. + ''; + default = true; + }; + }; + + config = mkIf cfg.enable { + systemd.services.quickwit = { + description = "Quickwit"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + inherit (cfg) restartIfChanged; + environment = { + QW_DATA_DIR = cfg.dataDir; + }; + serviceConfig = { + ExecStart = '' + ${cfg.package}/bin/quickwit run --config ${quickwitYml} \ + ${escapeShellArgs cfg.extraFlags} + ''; + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + DynamicUser = usingDefaultUserAndGroup && usingDefaultDataDir; + CapabilityBoundingSet = [ "" ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadWritePaths = [ + "/var/lib/quickwit" + ]; + RestrictAddressFamilies = [ + "AF_NETLINK" + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + # 1. allow a reasonable set of syscalls + "@system-service @resources" + # 2. and deny unreasonable ones + "~@privileged" + # 3. then allow the required subset within denied groups + "@chown" + ]; + } // (optionalAttrs (usingDefaultDataDir) { + StateDirectory = "quickwit"; + StateDirectoryMode = "0700"; + }); + }; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/security/bitwarden-directory-connector-cli.nix b/nixpkgs/nixos/modules/services/security/bitwarden-directory-connector-cli.nix index d21322caf4c3..fef4a8864897 100644 --- a/nixpkgs/nixos/modules/services/security/bitwarden-directory-connector-cli.nix +++ b/nixpkgs/nixos/modules/services/security/bitwarden-directory-connector-cli.nix @@ -260,6 +260,7 @@ in { description = "Sync timer for Bitwarden Directory Connector"; wantedBy = ["timers.target"]; after = ["network-online.target"]; + wants = ["network-online.target"]; timerConfig = { OnCalendar = cfg.interval; Unit = "bitwarden-directory-connector-cli.service"; diff --git a/nixpkgs/nixos/modules/services/security/oauth2-proxy-nginx.nix b/nixpkgs/nixos/modules/services/security/oauth2-proxy-nginx.nix index 07192e7287b0..2dffeb993803 100644 --- a/nixpkgs/nixos/modules/services/security/oauth2-proxy-nginx.nix +++ b/nixpkgs/nixos/modules/services/security/oauth2-proxy-nginx.nix @@ -73,6 +73,7 @@ in virtualHosts.${cfg.domain}.locations."/oauth2/" = { proxyPass = cfg.proxy; extraConfig = '' + auth_request off; proxy_set_header X-Scheme $scheme; proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; ''; @@ -83,6 +84,15 @@ in } ++ (lib.mapAttrsToList (vhost: conf: { virtualHosts.${vhost} = { locations = { + "/".extraConfig = '' + # pass information via X-User and X-Email headers to backend, requires running with --set-xauthrequest flag + proxy_set_header X-User $user; + proxy_set_header X-Email $email; + + # if you enabled --cookie-refresh, this is needed for it to work with auth_request + add_header Set-Cookie $auth_cookie; + ''; + "/oauth2/auth" = let maybeQueryArg = name: value: if value == null then null @@ -102,6 +112,7 @@ in proxy_pass_request_body off; ''; }; + "@redirectToAuth2ProxyLogin" = { return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri"; extraConfig = '' @@ -114,16 +125,10 @@ in auth_request /oauth2/auth; error_page 401 = @redirectToAuth2ProxyLogin; - # pass information via X-User and X-Email headers to backend, - # requires running with --set-xauthrequest flag + # set variables being used in locations."/".extraConfig auth_request_set $user $upstream_http_x_auth_request_user; auth_request_set $email $upstream_http_x_auth_request_email; - proxy_set_header X-User $user; - proxy_set_header X-Email $email; - - # if you enabled --cookie-refresh, this is needed for it to work with auth_request auth_request_set $auth_cookie $upstream_http_set_cookie; - add_header Set-Cookie $auth_cookie; ''; }; }) cfg.virtualHosts))); diff --git a/nixpkgs/nixos/modules/services/security/oauth2-proxy.nix b/nixpkgs/nixos/modules/services/security/oauth2-proxy.nix index 3079a1d030c5..a897f04ea633 100644 --- a/nixpkgs/nixos/modules/services/security/oauth2-proxy.nix +++ b/nixpkgs/nixos/modules/services/security/oauth2-proxy.nix @@ -586,11 +586,11 @@ in wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ] ++ lib.optionals needsKeycloak [ "keycloak.service" ]; after = [ "network-online.target" ] ++ lib.optionals needsKeycloak [ "keycloak.service" ]; - + restartTriggers = [ cfg.keyFile ]; serviceConfig = { User = "oauth2-proxy"; Restart = "always"; - ExecStart = "${cfg.package}/bin/oauth2-proxy ${configString}"; + ExecStart = "${lib.getExe cfg.package} ${configString}"; EnvironmentFile = lib.mkIf (cfg.keyFile != null) cfg.keyFile; }; }; diff --git a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix index 33957be437b3..41f7de5d80fa 100644 --- a/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix +++ b/nixpkgs/nixos/modules/services/security/vaultwarden/default.nix @@ -5,6 +5,8 @@ let user = config.users.users.vaultwarden.name; group = config.users.groups.vaultwarden.name; + StateDirectory = if lib.versionOlder config.system.stateVersion "24.11" then "bitwarden_rs" else "vaultwarden"; + # Convert name from camel case (e.g. disable2FARemember) to upper case snake case (e.g. DISABLE_2FA_REMEMBER). nameToEnvVar = name: let @@ -23,7 +25,7 @@ let configEnv = lib.concatMapAttrs (name: value: lib.optionalAttrs (value != null) { ${nameToEnvVar name} = if lib.isBool value then lib.boolToString value else toString value; }) cfg.config; - in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // lib.optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") { + in { DATA_FOLDER = "/var/lib/${StateDirectory}"; } // lib.optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") { WEB_VAULT_FOLDER = "${cfg.webVaultPackage}/share/vaultwarden/vault"; } // configEnv; @@ -176,16 +178,45 @@ in { User = user; Group = group; EnvironmentFile = [ configFile ] ++ lib.optional (cfg.environmentFile != null) cfg.environmentFile; - ExecStart = "${vaultwarden}/bin/vaultwarden"; + ExecStart = lib.getExe vaultwarden; LimitNOFILE = "1048576"; - PrivateTmp = "true"; - PrivateDevices = "true"; - ProtectHome = "true"; + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; ProtectSystem = "strict"; - AmbientCapabilities = "CAP_NET_BIND_SERVICE"; - StateDirectory = "bitwarden_rs"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + inherit StateDirectory; StateDirectoryMode = "0700"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; Restart = "always"; + UMask = "0077"; }; wantedBy = [ "multi-user.target" ]; }; @@ -193,7 +224,7 @@ in { systemd.services.backup-vaultwarden = lib.mkIf (cfg.backupDir != null) { description = "Backup vaultwarden"; environment = { - DATA_FOLDER = "/var/lib/bitwarden_rs"; + DATA_FOLDER = "/var/lib/${StateDirectory}"; BACKUP_FOLDER = cfg.backupDir; }; path = with pkgs; [ sqlite ]; diff --git a/nixpkgs/nixos/modules/services/system/dbus.nix b/nixpkgs/nixos/modules/services/system/dbus.nix index 26f4eba707f9..d84136125f93 100644 --- a/nixpkgs/nixos/modules/services/system/dbus.nix +++ b/nixpkgs/nixos/modules/services/system/dbus.nix @@ -128,10 +128,14 @@ in contents."/etc/dbus-1".source = pkgs.makeDBusConf { inherit (cfg) apparmor; suidHelper = "/bin/false"; - serviceDirectories = [ pkgs.dbus ]; + serviceDirectories = [ pkgs.dbus config.boot.initrd.systemd.package ]; }; packages = [ pkgs.dbus ]; - storePaths = [ "${pkgs.dbus}/bin/dbus-daemon" ]; + storePaths = [ + "${pkgs.dbus}/bin/dbus-daemon" + "${config.boot.initrd.systemd.package}/share/dbus-1/system-services" + "${config.boot.initrd.systemd.package}/share/dbus-1/system.d" + ]; targets.sockets.wants = [ "dbus.socket" ]; }; }) diff --git a/nixpkgs/nixos/modules/services/system/kerberos/default.nix b/nixpkgs/nixos/modules/services/system/kerberos/default.nix index 7fe970c9609a..34c7c6c84f86 100644 --- a/nixpkgs/nixos/modules/services/system/kerberos/default.nix +++ b/nixpkgs/nixos/modules/services/system/kerberos/default.nix @@ -1,75 +1,59 @@ -{config, lib, ...}: +{ config, pkgs, lib, ... }: let - inherit (lib) mkOption mkIf types length attrNames; + inherit (lib) mkOption types; cfg = config.services.kerberos_server; - kerberos = config.security.krb5.package; + inherit (config.security.krb5) package; - aclEntry = { - options = { - principal = mkOption { - type = types.str; - description = "Which principal the rule applies to"; - }; - access = mkOption { - type = types.either - (types.listOf (types.enum ["add" "cpw" "delete" "get" "list" "modify"])) - (types.enum ["all"]); - default = "all"; - description = "The changes the principal is allowed to make."; - }; - target = mkOption { - type = types.str; - default = "*"; - description = "The principals that 'access' applies to."; - }; - }; - }; - - realm = { - options = { - acl = mkOption { - type = types.listOf (types.submodule aclEntry); - default = [ - { principal = "*/admin"; access = "all"; } - { principal = "admin"; access = "all"; } - ]; - description = '' - The privileges granted to a user. - ''; - }; - }; - }; + format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; }; in { imports = [ + (lib.mkRenamedOptionModule [ "services" "kerberos_server" "realms" ] [ "services" "kerberos_server" "settings" "realms" ]) + ./mit.nix ./heimdal.nix ]; - ###### interface options = { services.kerberos_server = { enable = lib.mkEnableOption "the kerberos authentication server"; - realms = mkOption { - type = types.attrsOf (types.submodule realm); + settings = mkOption { + type = format.type; description = '' - The realm(s) to serve keys for. + Settings for the kerberos server of choice. + + See the following documentation: + - Heimdal: {manpage}`kdc.conf(5)` + - MIT Kerberos: <https://web.mit.edu/kerberos/krb5-1.21/doc/admin/conf_files/kdc_conf.html> ''; + default = { }; }; }; }; + config = lib.mkIf cfg.enable { + environment.systemPackages = [ package ]; + assertions = [ + { + assertion = cfg.settings.realms != { }; + message = "The server needs at least one realm"; + } + { + assertion = lib.length (lib.attrNames cfg.settings.realms) <= 1; + message = "Only one realm per server is currently supported."; + } + ]; + + systemd.slices.system-kerberos-server = { }; + systemd.targets.kerberos-server = { + wantedBy = [ "multi-user.target" ]; + }; + }; - ###### implementation - - config = mkIf cfg.enable { - environment.systemPackages = [ kerberos ]; - assertions = [{ - assertion = length (attrNames cfg.realms) <= 1; - message = "Only one realm per server is currently supported."; - }]; + meta = { + doc = ./kerberos-server.md; }; } diff --git a/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix b/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix index ecafc9276670..cec4dd276e6b 100644 --- a/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix +++ b/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix @@ -1,68 +1,87 @@ { pkgs, config, lib, ... } : let - inherit (lib) mkIf concatStringsSep concatMapStrings toList mapAttrs - mapAttrsToList; + inherit (lib) mapAttrs; cfg = config.services.kerberos_server; - kerberos = config.security.krb5.package; - stateDir = "/var/heimdal"; - aclFiles = mapAttrs - (name: {acl, ...}: pkgs.writeText "${name}.acl" (concatMapStrings (( - {principal, access, target, ...} : - "${principal}\t${concatStringsSep "," (toList access)}\t${target}\n" - )) acl)) cfg.realms; + package = config.security.krb5.package; - kdcConfigs = mapAttrsToList (name: value: '' - database = { - dbname = ${stateDir}/heimdal - acl_file = ${value} - } - '') aclFiles; - kdcConfFile = pkgs.writeText "kdc.conf" '' - [kdc] - ${concatStringsSep "\n" kdcConfigs} - ''; + aclConfigs = lib.pipe cfg.settings.realms [ + (mapAttrs (name: { acl, ... }: lib.concatMapStringsSep "\n" ( + { principal, access, target, ... }: + "${principal}\t${lib.concatStringsSep "," (lib.toList access)}\t${target}" + ) acl)) + (lib.mapAttrsToList (name: text: + { + dbname = "/var/lib/heimdal/heimdal"; + acl_file = pkgs.writeText "${name}.acl" text; + } + )) + ]; + + finalConfig = cfg.settings // { + realms = mapAttrs (_: v: removeAttrs v [ "acl" ]) (cfg.settings.realms or { }); + kdc = (cfg.settings.kdc or { }) // { + database = aclConfigs; + }; + }; + + format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; }; + + kdcConfFile = format.generate "kdc.conf" finalConfig; in { - # No documentation about correct triggers, so guessing at them. + config = lib.mkIf (cfg.enable && package.passthru.implementation == "heimdal") { + environment.etc."heimdal-kdc/kdc.conf".source = kdcConfFile; + + systemd.tmpfiles.settings."10-heimdal" = let + databases = lib.pipe finalConfig.kdc.database [ + (map (dbAttrs: dbAttrs.dbname or null)) + (lib.filter (x: x != null)) + lib.unique + ]; + in lib.genAttrs databases (_: { + d = { + user = "root"; + group = "root"; + mode = "0700"; + }; + }); - config = mkIf (cfg.enable && kerberos == pkgs.heimdal) { systemd.services.kadmind = { description = "Kerberos Administration Daemon"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - mkdir -m 0755 -p ${stateDir} - ''; - serviceConfig.ExecStart = - "${kerberos}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf"; + partOf = [ "kerberos-server.target" ]; + wantedBy = [ "kerberos-server.target" ]; + serviceConfig = { + ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf"; + Slice = "system-kerberos-server.slice"; + StateDirectory = "heimdal"; + }; restartTriggers = [ kdcConfFile ]; }; systemd.services.kdc = { description = "Key Distribution Center daemon"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - mkdir -m 0755 -p ${stateDir} - ''; - serviceConfig.ExecStart = - "${kerberos}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf"; + partOf = [ "kerberos-server.target" ]; + wantedBy = [ "kerberos-server.target" ]; + serviceConfig = { + ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf"; + Slice = "system-kerberos-server.slice"; + StateDirectory = "heimdal"; + }; restartTriggers = [ kdcConfFile ]; }; systemd.services.kpasswdd = { description = "Kerberos Password Changing daemon"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - mkdir -m 0755 -p ${stateDir} - ''; - serviceConfig.ExecStart = "${kerberos}/libexec/kpasswdd"; + partOf = [ "kerberos-server.target" ]; + wantedBy = [ "kerberos-server.target" ]; + serviceConfig = { + ExecStart = "${package}/libexec/kpasswdd"; + Slice = "system-kerberos-server.slice"; + StateDirectory = "heimdal"; + }; restartTriggers = [ kdcConfFile ]; }; - - environment.etc = { - # Can be set via the --config-file option to KDC - "heimdal-kdc/kdc.conf".source = kdcConfFile; - }; }; } diff --git a/nixpkgs/nixos/modules/services/system/kerberos/kerberos-server.md b/nixpkgs/nixos/modules/services/system/kerberos/kerberos-server.md new file mode 100644 index 000000000000..80c71be1541e --- /dev/null +++ b/nixpkgs/nixos/modules/services/system/kerberos/kerberos-server.md @@ -0,0 +1,55 @@ +# kerberos_server {#module-services-kerberos-server} + +Kerberos is a computer-network authentication protocol that works on the basis of tickets to allow nodes communicating over a non-secure network to prove their identity to one another in a secure manner. + +This module provides both the MIT and Heimdal implementations of the a Kerberos server. + +## Usage {#module-services-kerberos-server-usage} + +To enable a Kerberos server: + +```nix +{ + security.krb5 = { + # Here you can choose between the MIT and Heimdal implementations. + package = pkgs.krb5; + # package = pkgs.heimdal; + + # Optionally set up a client on the same machine as the server + enable = true; + settings = { + libdefaults.default_realm = "EXAMPLE.COM"; + realms."EXAMPLE.COM" = { + kdc = "kerberos.example.com"; + admin_server = "kerberos.example.com"; + }; + }; + } + + services.kerberos-server = { + enable = true; + settings = { + realms."EXAMPLE.COM" = { + acl = [{ principal = "adminuser"; access= ["add" "cpw"]; }]; + }; + }; + }; +} +``` + +## Notes {#module-services-kerberos-server-notes} + +- The Heimdal documentation will sometimes assume that state is stored in `/var/heimdal`, but this module uses `/var/lib/heimdal` instead. +- Due to the heimdal implementation being chosen through `security.krb5.package`, it is not possible to have a system with one implementation of the client and another of the server. +- While `services.kerberos_server.settings` has a common freeform type between the two implementations, the actual settings that can be set can vary between the two implementations. To figure out what settings are available, you should consult the upstream documentation for the implementation you are using. + +## Upstream Documentation {#module-services-kerberos-server-upstream-documentation} + +- MIT Kerberos homepage: https://web.mit.edu/kerberos +- MIT Kerberos docs: https://web.mit.edu/kerberos/krb5-latest/doc/index.html + +- Heimdal Kerberos GitHub wiki: https://github.com/heimdal/heimdal/wiki +- Heimdal kerberos doc manpages (Debian unstable): https://manpages.debian.org/unstable/heimdal-docs/index.html +- Heimdal Kerberos kdc manpages (Debian unstable): https://manpages.debian.org/unstable/heimdal-kdc/index.html + +Note the version number in the URLs, it may be different for the latest version. diff --git a/nixpkgs/nixos/modules/services/system/kerberos/mit.nix b/nixpkgs/nixos/modules/services/system/kerberos/mit.nix index a654bd1fe7e1..9ce58986e27a 100644 --- a/nixpkgs/nixos/modules/services/system/kerberos/mit.nix +++ b/nixpkgs/nixos/modules/services/system/kerberos/mit.nix @@ -1,31 +1,37 @@ { pkgs, config, lib, ... } : let - inherit (lib) mkIf concatStrings concatStringsSep concatMapStrings toList - mapAttrs mapAttrsToList; + inherit (lib) mapAttrs; cfg = config.services.kerberos_server; - kerberos = config.security.krb5.package; - stateDir = "/var/lib/krb5kdc"; + package = config.security.krb5.package; PIDFile = "/run/kdc.pid"; + + format = import ../../../security/krb5/krb5-conf-format.nix { inherit pkgs lib; } { enableKdcACLEntries = true; }; + aclMap = { add = "a"; cpw = "c"; delete = "d"; get = "i"; list = "l"; modify = "m"; all = "*"; }; - aclFiles = mapAttrs - (name: {acl, ...}: (pkgs.writeText "${name}.acl" (concatMapStrings ( - {principal, access, target, ...} : - let access_code = map (a: aclMap.${a}) (toList access); in - "${principal} ${concatStrings access_code} ${target}\n" - ) acl))) cfg.realms; - kdcConfigs = mapAttrsToList (name: value: '' - ${name} = { - acl_file = ${value} - } - '') aclFiles; - kdcConfFile = pkgs.writeText "kdc.conf" '' - [realms] - ${concatStringsSep "\n" kdcConfigs} - ''; + + aclConfigs = lib.pipe cfg.settings.realms [ + (mapAttrs (name: { acl, ... }: lib.concatMapStringsSep "\n" ( + { principal, access, target, ... }: let + access_code = map (a: aclMap.${a}) (lib.toList access); + in "${principal} ${lib.concatStrings access_code} ${target}" + ) acl)) + + (lib.concatMapAttrs (name: text: { + ${name} = { + acl_file = pkgs.writeText "${name}.acl" text; + }; + })) + ]; + + finalConfig = cfg.settings // { + realms = mapAttrs (n: v: (removeAttrs v [ "acl" ]) // aclConfigs.${n}) (cfg.settings.realms or { }); + }; + + kdcConfFile = format.generate "kdc.conf" finalConfig; env = { # What Debian uses, could possibly link directly to Nix store? KRB5_KDC_PROFILE = "/etc/krb5kdc/kdc.conf"; @@ -33,36 +39,38 @@ let in { - config = mkIf (cfg.enable && kerberos == pkgs.krb5) { + config = lib.mkIf (cfg.enable && package.passthru.implementation == "krb5") { + environment = { + etc."krb5kdc/kdc.conf".source = kdcConfFile; + variables = env; + }; + systemd.services.kadmind = { description = "Kerberos Administration Daemon"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - mkdir -m 0755 -p ${stateDir} - ''; - serviceConfig.ExecStart = "${kerberos}/bin/kadmind -nofork"; + partOf = [ "kerberos-server.target" ]; + wantedBy = [ "kerberos-server.target" ]; + serviceConfig = { + ExecStart = "${package}/bin/kadmind -nofork"; + Slice = "system-kerberos-server.slice"; + StateDirectory = "krb5kdc"; + }; restartTriggers = [ kdcConfFile ]; environment = env; }; systemd.services.kdc = { description = "Key Distribution Center daemon"; - wantedBy = [ "multi-user.target" ]; - preStart = '' - mkdir -m 0755 -p ${stateDir} - ''; + partOf = [ "kerberos-server.target" ]; + wantedBy = [ "kerberos-server.target" ]; serviceConfig = { Type = "forking"; PIDFile = PIDFile; - ExecStart = "${kerberos}/bin/krb5kdc -P ${PIDFile}"; + ExecStart = "${package}/bin/krb5kdc -P ${PIDFile}"; + Slice = "system-kerberos-server.slice"; + StateDirectory = "krb5kdc"; }; restartTriggers = [ kdcConfFile ]; environment = env; }; - - environment.etc = { - "krb5kdc/kdc.conf".source = kdcConfFile; - }; - environment.variables = env; }; } diff --git a/nixpkgs/nixos/modules/services/system/nix-daemon.nix b/nixpkgs/nixos/modules/services/system/nix-daemon.nix index 0a5b0e2fcb80..3d44bdac34bf 100644 --- a/nixpkgs/nixos/modules/services/system/nix-daemon.nix +++ b/nixpkgs/nixos/modules/services/system/nix-daemon.nix @@ -164,7 +164,7 @@ in nixPackage pkgs.nix-info ] - ++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions; + ++ optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions; systemd.packages = [ nixPackage ]; diff --git a/nixpkgs/nixos/modules/services/torrent/flood.nix b/nixpkgs/nixos/modules/services/torrent/flood.nix new file mode 100644 index 000000000000..213f4ef04648 --- /dev/null +++ b/nixpkgs/nixos/modules/services/torrent/flood.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.services.flood; +in +{ + meta.maintainers = with lib.maintainers; [ thiagokokada ]; + + options.services.flood = { + enable = lib.mkEnableOption "flood"; + package = lib.mkPackageOption pkgs "flood" { }; + openFirewall = lib.mkEnableOption "" // { + description = "Whether to open the firewall for the port in {option}`services.flood.port`."; + }; + port = lib.mkOption { + type = lib.types.int; + description = "Port to bind webserver."; + default = 3000; + example = 3001; + }; + host = lib.mkOption { + type = lib.types.str; + description = "Host to bind webserver."; + default = "localhost"; + example = "::"; + }; + extraArgs = lib.mkOption { + type = with lib.types; listOf str; + description = "Extra arguments passed to `flood`."; + default = [ ]; + example = [ "--baseuri=/" ]; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.flood = { + description = "A modern web UI for various torrent clients."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig = { + Documentation = "https://github.com/jesec/flood/wiki"; + }; + serviceConfig = { + Restart = "on-failure"; + RestartSec = "3s"; + ExecStart = utils.escapeSystemdExecArgs ([ + (lib.getExe cfg.package) + "--host" + cfg.host + "--port" + (toString cfg.port) + "--rundir=/var/lib/flood" + ] ++ cfg.extraArgs); + + CapabilityBoundingSet = [ "" ]; + DynamicUser = true; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + StateDirectory = "flood"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "@pkey" "~@privileged" ]; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ + cfg.port + ]; + }; +} + diff --git a/nixpkgs/nixos/modules/services/ttys/getty.nix b/nixpkgs/nixos/modules/services/ttys/getty.nix index 011016dd5fd1..e88bb4628635 100644 --- a/nixpkgs/nixos/modules/services/ttys/getty.nix +++ b/nixpkgs/nixos/modules/services/ttys/getty.nix @@ -101,7 +101,7 @@ in config = { # Note: this is set here rather than up there so that changing # nixos.label would not rebuild manual pages - services.getty.greetingLine = mkDefault ''<<< Welcome to NixOS ${config.system.nixos.label} (\m) - \l >>>''; + services.getty.greetingLine = mkDefault ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>''; services.getty.helpLine = mkIf (config.documentation.nixos.enable && config.documentation.doc.enable) "\nRun 'nixos-help' for the NixOS manual."; systemd.services."getty@" = @@ -158,4 +158,5 @@ in }; + meta.maintainers = with maintainers; [ RossComputerGuy ]; } diff --git a/nixpkgs/nixos/modules/services/ttys/kmscon.nix b/nixpkgs/nixos/modules/services/ttys/kmscon.nix index 74314e1e76e4..031c5bbb383e 100644 --- a/nixpkgs/nixos/modules/services/ttys/kmscon.nix +++ b/nixpkgs/nixos/modules/services/ttys/kmscon.nix @@ -107,7 +107,7 @@ in { fonts = optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}"; in lib.concatStringsSep "\n" (render ++ fonts); - hardware.opengl.enable = mkIf cfg.hwRender true; + hardware.graphics.enable = mkIf cfg.hwRender true; fonts = mkIf (cfg.fonts != null) { fontconfig.enable = true; diff --git a/nixpkgs/nixos/modules/services/video/frigate.nix b/nixpkgs/nixos/modules/services/video/frigate.nix index 0e6bde447c03..c3ec4a3c76c3 100644 --- a/nixpkgs/nixos/modules/services/video/frigate.nix +++ b/nixpkgs/nixos/modules/services/video/frigate.nix @@ -427,10 +427,6 @@ in PrivateTmp = true; CacheDirectory = "frigate"; CacheDirectoryMode = "0750"; - - BindPaths = [ - "/migrations:${cfg.package}/share/frigate/migrations:ro" - ]; }; }; }; diff --git a/nixpkgs/nixos/modules/services/video/photonvision.nix b/nixpkgs/nixos/modules/services/video/photonvision.nix index d4568258db7d..e2b27b3cc410 100644 --- a/nixpkgs/nixos/modules/services/video/photonvision.nix +++ b/nixpkgs/nixos/modules/services/video/photonvision.nix @@ -6,7 +6,7 @@ in { options = { services.photonvision = { - enable = lib.mkEnableOption "Enable PhotonVision"; + enable = lib.mkEnableOption "PhotonVision"; package = lib.mkPackageOption pkgs "photonvision" {}; diff --git a/nixpkgs/nixos/modules/services/wayland/cage.nix b/nixpkgs/nixos/modules/services/wayland/cage.nix index 91949f197cfe..870ae58f8646 100644 --- a/nixpkgs/nixos/modules/services/wayland/cage.nix +++ b/nixpkgs/nixos/modules/services/wayland/cage.nix @@ -101,7 +101,7 @@ in { session required ${config.systemd.package}/lib/security/pam_systemd.so ''; - hardware.opengl.enable = mkDefault true; + hardware.graphics.enable = mkDefault true; systemd.targets.graphical.wants = [ "cage-tty1.service" ]; diff --git a/nixpkgs/nixos/modules/services/wayland/hypridle.nix b/nixpkgs/nixos/modules/services/wayland/hypridle.nix new file mode 100644 index 000000000000..5442802df987 --- /dev/null +++ b/nixpkgs/nixos/modules/services/wayland/hypridle.nix @@ -0,0 +1,26 @@ +{ lib, pkgs, config, ... }: + +let + cfg = config.services.hypridle; +in +{ + options.services.hypridle = { + enable = lib.mkEnableOption "hypridle, Hyprland's idle daemon"; + package = lib.mkPackageOption pkgs "hypridle" { }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ + cfg.package + ]; + + systemd.user.services.hypridle = { + description = "Hypridle idle daemon"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + script = lib.getExe cfg.package; + }; + }; + + meta.maintainers = with lib.maintainers; [ johnrtitor ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/akkoma.nix b/nixpkgs/nixos/modules/services/web-apps/akkoma.nix index 7c9bf6c46516..8ba3c7eaa1e6 100644 --- a/nixpkgs/nixos/modules/services/web-apps/akkoma.nix +++ b/nixpkgs/nixos/modules/services/web-apps/akkoma.nix @@ -119,7 +119,7 @@ let -o ${escapeShellArg cfg.user } \ -g ${escapeShellArg cfg.group} \ <(hexdump -n 16 -e '"%02x"' /dev/urandom) \ - "$RUNTIME_DIRECTORY/cookie" + "''${RUNTIME_DIRECTORY%%:*}/cookie" ''; }; @@ -131,7 +131,7 @@ let -o ${escapeShellArg cfg.user} \ -g ${escapeShellArg cfg.group} \ ${escapeShellArg cfg.dist.cookie._secret} \ - "$RUNTIME_DIRECTORY/cookie" + "''${RUNTIME_DIRECTORY%%:*}/cookie" ''; }; @@ -181,7 +181,7 @@ let name = "akkoma-config"; runtimeInputs = with pkgs; [ coreutils replace-secret ]; text = '' - cd "$RUNTIME_DIRECTORY" + cd "''${RUNTIME_DIRECTORY%%:*}" tmp="$(mktemp config.exs.XXXXXXXXXX)" trap 'rm -f "$tmp"' EXIT TERM @@ -279,7 +279,7 @@ let cd "${cfg.package}" RUNTIME_DIRECTORY="''${RUNTIME_DIRECTORY:-/run/akkoma}" - AKKOMA_CONFIG_PATH="$RUNTIME_DIRECTORY/config.exs" \ + AKKOMA_CONFIG_PATH="''${RUNTIME_DIRECTORY%%:*}/config.exs" \ ERL_EPMD_ADDRESS="${cfg.dist.address}" \ ERL_EPMD_PORT="${toString cfg.dist.epmdPort}" \ ERL_FLAGS=${lib.escapeShellArg (lib.escapeShellArgs ([ @@ -287,7 +287,7 @@ let "-kernel" "inet_dist_listen_min" (toString cfg.dist.portMin) "-kernel" "inet_dist_listen_max" (toString cfg.dist.portMax) ] ++ cfg.dist.extraFlags))} \ - RELEASE_COOKIE="$(<"$RUNTIME_DIRECTORY/cookie")" \ + RELEASE_COOKIE="$(<"''${RUNTIME_DIRECTORY%%:*}/cookie")" \ RELEASE_NAME="akkoma" \ exec "${cfg.package}/bin/$(basename "$0")" "$@" ''; @@ -984,7 +984,7 @@ in { RemainAfterExit = true; UMask = "0077"; - RuntimeDirectory = "akkoma"; + RuntimeDirectory = mkBefore "akkoma"; ExecStart = mkMerge [ (mkIf (cfg.dist.cookie == null) [ genScript ]) @@ -1072,7 +1072,7 @@ in { ProtectProc = "noaccess"; ProcSubset = "pid"; - ProtectSystem = mkIf (!isConfined) "strict"; + ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; PrivateDevices = true; @@ -1136,6 +1136,6 @@ in { }; }; - meta.maintainers = with maintainers; [ mvs tcmal ]; + meta.maintainers = with maintainers; [ mvs ]; meta.doc = ./akkoma.md; } diff --git a/nixpkgs/nixos/modules/services/web-apps/filesender.md b/nixpkgs/nixos/modules/services/web-apps/filesender.md new file mode 100644 index 000000000000..44d066761b9a --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/filesender.md @@ -0,0 +1,49 @@ +# FileSender {#module-services-filesender} + +[FileSender](https://filesender.org/software/) is a software that makes it easy to send and receive big files. + +## Quickstart {#module-services-filesender-quickstart} + +FileSender uses [SimpleSAMLphp](https://simplesamlphp.org/) for authentication, which needs to be configured separately. + +Minimal working instance of FileSender that uses password-authentication would look like this: + +```nix +{ + networking.firewall.allowedTCPPorts = [ 80 443 ]; + services.filesender = { + enable = true; + localDomain = "filesender.example.com"; + configureNginx = true; + database.createLocally = true; + + settings = { + auth_sp_saml_authentication_source = "default"; + auth_sp_saml_uid_attribute = "uid"; + storage_filesystem_path = "<STORAGE PATH FOR UPLOADED FILES>"; + admin = "admin"; + admin_email = "admin@example.com"; + email_reply_to = "noreply@example.com"; + }; + }; + services.simplesamlphp.filesender = { + settings = { + "module.enable".exampleauth = true; + }; + authSources = { + admin = [ "core:AdminPassword" ]; + default = format.lib.mkMixedArray [ "exampleauth:UserPass" ] { + "admin:admin123" = { + uid = [ "admin" ]; + cn = [ "admin" ]; + mail = [ "admin@example.com" ]; + }; + }; + }; + }; +} +``` + +::: {.warning} +Example above uses hardcoded clear-text password, in production you should use other authentication method like LDAP. You can check supported authentication methods [in SimpleSAMLphp documentation](https://simplesamlphp.org/docs/stable/simplesamlphp-idp.html). +::: diff --git a/nixpkgs/nixos/modules/services/web-apps/filesender.nix b/nixpkgs/nixos/modules/services/web-apps/filesender.nix new file mode 100644 index 000000000000..bc8d465643f2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/filesender.nix @@ -0,0 +1,253 @@ +{ + config, + lib, + pkgs, + ... +}: +let + format = pkgs.formats.php { finalVariable = "config"; }; + + cfg = config.services.filesender; + simpleSamlCfg = config.services.simplesamlphp.filesender; + fpm = config.services.phpfpm.pools.filesender; + + filesenderConfigDirectory = pkgs.runCommand "filesender-config" { } '' + mkdir $out + cp ${format.generate "config.php" cfg.settings} $out/config.php + ''; +in +{ + meta = { + maintainers = with lib.maintainers; [ nhnn ]; + doc = ./filesender.md; + }; + + options.services.filesender = with lib; { + enable = mkEnableOption "FileSender"; + package = mkPackageOption pkgs "filesender" { }; + user = mkOption { + description = "User under which filesender runs."; + type = types.str; + default = "filesender"; + }; + database = { + createLocally = mkOption { + type = types.bool; + default = true; + description = '' + Create the PostgreSQL database and database user locally. + ''; + }; + hostname = mkOption { + type = types.str; + default = "/run/postgresql"; + description = "Database hostname."; + }; + port = mkOption { + type = types.port; + default = 5432; + description = "Database port."; + }; + name = mkOption { + type = types.str; + default = "filesender"; + description = "Database name."; + }; + user = mkOption { + type = types.str; + default = "filesender"; + description = "Database user."; + }; + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/filesender-dbpassword"; + description = '' + A file containing the password corresponding to + [](#opt-services.filesender.database.user). + ''; + }; + }; + settings = mkOption { + type = types.submodule { + freeformType = format.type; + options = { + site_url = mkOption { + type = types.str; + description = "Site URL. Used in emails, to build URLs for logging in, logging out, build URL for upload endpoint for web workers, to include scripts etc."; + }; + admin = mkOption { + type = types.commas; + description = '' + UIDs (as per the configured saml_uid_attribute) of FileSender administrators. + Accounts with these UIDs can access the Admin page through the web UI. + ''; + }; + admin_email = mkOption { + type = types.commas; + description = '' + Email address of FileSender administrator(s). + Emails regarding disk full etc. are sent here. + You should use a role-address here. + ''; + }; + storage_filesystem_path = mkOption { + type = types.nullOr types.str; + description = "When using storage type filesystem this is the absolute path to the file system where uploaded files are stored until they expire. Your FileSender storage root."; + }; + log_facilities = mkOption { + type = format.type; + default = [ { type = "error_log"; } ]; + description = "Defines where FileSender logging is sent. You can sent logging to a file, to syslog or to the default PHP log facility (as configured through your webserver's PHP module). The directive takes an array of one or more logging targets. Logging can be sent to multiple targets simultaneously. Each logging target is a list containing the name of the logging target and a number of attributes which vary per log target. See below for the exact definiation of each log target."; + }; + }; + }; + default = { }; + description = '' + Configuration options used by FileSender. + See [](https://docs.filesender.org/filesender/v2.0/admin/configuration/) + for available options. + ''; + }; + configureNginx = mkOption { + type = types.bool; + default = true; + description = "Configure nginx as a reverse proxy for FileSender."; + }; + localDomain = mkOption { + type = types.str; + example = "filesender.example.org"; + description = "The domain serving your FileSender instance."; + }; + poolSettings = mkOption { + type = + with types; + attrsOf (oneOf [ + str + int + bool + ]); + default = { + "pm" = "dynamic"; + "pm.max_children" = "32"; + "pm.start_servers" = "2"; + "pm.min_spare_servers" = "2"; + "pm.max_spare_servers" = "4"; + "pm.max_requests" = "500"; + }; + description = '' + Options for FileSender's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives. + ''; + }; + }; + config = lib.mkIf cfg.enable { + services.simplesamlphp.filesender = { + phpfpmPool = "filesender"; + localDomain = cfg.localDomain; + settings.baseurlpath = lib.mkDefault "https://${cfg.localDomain}/saml"; + }; + + services.phpfpm = { + pools.filesender = { + user = cfg.user; + group = config.services.nginx.group; + phpEnv = { + FILESENDER_CONFIG_DIR = toString filesenderConfigDirectory; + SIMPLESAMLPHP_CONFIG_DIR = toString simpleSamlCfg.configDir; + }; + settings = { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + } // cfg.poolSettings; + }; + }; + + services.nginx = lib.mkIf cfg.configureNginx { + enable = true; + virtualHosts.${cfg.localDomain} = { + root = "${cfg.package}/www"; + extraConfig = '' + index index.php; + ''; + locations = { + "/".extraConfig = '' + try_files $uri $uri/ /index.php?args; + ''; + "~ [^/]\\.php(/|$)" = { + extraConfig = '' + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${fpm.socket}; + include ${pkgs.nginx}/conf/fastcgi.conf; + fastcgi_intercept_errors on; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + ''; + }; + "~ /\\.".extraConfig = "deny all;"; + }; + }; + }; + + services.postgresql = lib.mkIf cfg.database.createLocally { + enable = true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { + name = cfg.database.user; + ensureDBOwnership = true; + } + ]; + }; + + services.filesender.settings = lib.mkMerge [ + (lib.mkIf cfg.database.createLocally { + db_host = "/run/postgresql"; + db_port = "5432"; + db_password = "."; # FileSender requires it even when on UNIX socket auth. + }) + (lib.mkIf (!cfg.database.createLocally) { + db_host = cfg.database.hostname; + db_port = toString cfg.database.port; + db_password = format.lib.mkRaw "file_get_contents('${cfg.database.passwordFile}')"; + }) + { + site_url = lib.mkDefault "https://${cfg.localDomain}"; + db_type = "pgsql"; + db_username = cfg.database.user; + db_database = cfg.database.name; + "auth_sp_saml_simplesamlphp_url" = "/saml"; + "auth_sp_saml_simplesamlphp_location" = "${simpleSamlCfg.libDir}"; + } + ]; + + systemd.services.filesender-initdb = { + description = "Init filesender DB"; + + wantedBy = [ + "multi-user.target" + "phpfpm-filesender.service" + ]; + after = [ "postgresql.service" ]; + + restartIfChanged = true; + + serviceConfig = { + Environment = [ + "FILESENDER_CONFIG_DIR=${toString filesenderConfigDirectory}" + "SIMPLESAMLPHP_CONFIG_DIR=${toString simpleSamlCfg.configDir}" + ]; + Type = "oneshot"; + Group = config.services.nginx.group; + User = "filesender"; + ExecStart = "${fpm.phpPackage}/bin/php ${cfg.package}/scripts/upgrade/database.php"; + }; + }; + + users.extraUsers.filesender = lib.mkIf (cfg.user == "filesender") { + home = "/var/lib/filesender"; + group = config.services.nginx.group; + createHome = true; + isSystemUser = true; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix b/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix index b0024ce09c38..3e51bd226b02 100644 --- a/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix +++ b/nixpkgs/nixos/modules/services/web-apps/firefly-iii.nix @@ -3,8 +3,8 @@ let inherit (lib) optionalString mkDefault mkIf mkOption mkEnableOption literalExpression; inherit (lib.types) nullOr attrsOf oneOf str int bool path package enum submodule; - inherit (lib.strings) concatMapStringsSep removePrefix toShellVars removeSuffix hasSuffix; - inherit (lib.attrsets) attrValues genAttrs filterAttrs mapAttrs' nameValuePair; + inherit (lib.strings) concatLines removePrefix toShellVars removeSuffix hasSuffix; + inherit (lib.attrsets) mapAttrsToList attrValues genAttrs filterAttrs mapAttrs' nameValuePair; inherit (builtins) isInt isString toString typeOf; cfg = config.services.firefly-iii; @@ -21,18 +21,10 @@ let (filterAttrs (n: v: hasSuffix "_FILE" n) cfg.settings); env-nonfile-values = filterAttrs (n: v: ! hasSuffix "_FILE" n) cfg.settings; - envfile = pkgs.writeText "firefly-iii-env" '' - ${toShellVars env-file-values} - ${toShellVars env-nonfile-values} - ''; - fileenv-func = '' - cp --no-preserve=mode ${envfile} /tmp/firefly-iii-env - ${concatMapStringsSep "\n" - (n: "${pkgs.replace-secret}/bin/replace-secret ${n} ${n} /tmp/firefly-iii-env") - (attrValues env-file-values)} set -a - . /tmp/firefly-iii-env + ${toShellVars env-nonfile-values} + ${concatLines (mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values)} set +a ''; @@ -41,22 +33,21 @@ let ${optionalString (cfg.settings.DB_CONNECTION == "sqlite") "touch ${cfg.dataDir}/storage/database/database.sqlite"} - ${artisan} migrate --seed --no-interaction --force - ${artisan} firefly-iii:decrypt-all + ${artisan} package:discover ${artisan} firefly-iii:upgrade-database - ${artisan} firefly-iii:correct-database - ${artisan} firefly-iii:report-integrity ${artisan} firefly-iii:laravel-passport-keys ${artisan} cache:clear - - mv /tmp/firefly-iii-env /run/phpfpm/firefly-iii-env + ${artisan} view:cache + ${artisan} route:cache + ${artisan} config:cache ''; commonServiceConfig = { Type = "oneshot"; User = user; Group = group; - StateDirectory = "${removePrefix "/var/lib/" cfg.dataDir}"; + StateDirectory = "firefly-iii"; + ReadWritePaths = [cfg.dataDir]; WorkingDirectory = cfg.package; PrivateTmp = true; PrivateDevices = true; @@ -146,6 +137,7 @@ in { virtualHost = mkOption { type = str; + default = "localhost"; description = '' The hostname at which you wish firefly-iii to be served. If you have enabled nginx using `services.firefly-iii.enableNginx` then this will @@ -170,14 +162,15 @@ in { }; settings = mkOption { + default = {}; description = '' Options for firefly-iii configuration. Refer to <https://github.com/firefly-iii/firefly-iii/blob/main/.env.example> for details on supported values. All <option>_FILE values supported by upstream are supported here. - APP_URL will be set by `services.firefly-iii.virtualHost`, do not - redefine it here. + APP_URL will be the same as `services.firefly-iii.virtualHost` if the + former is unset in `services.firefly-iii.settings`. ''; example = literalExpression '' { @@ -192,7 +185,6 @@ in { DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt; } ''; - default = {}; type = submodule { freeformType = attrsOf (oneOf [str int bool]); options = { @@ -216,9 +208,9 @@ in { }; DB_PORT = mkOption { type = nullOr int; - default = if cfg.settings.DB_CONNECTION == "sqlite" then null + default = if cfg.settings.DB_CONNECTION == "pgsql" then 5432 else if cfg.settings.DB_CONNECTION == "mysql" then 3306 - else 5432; + else null; defaultText = '' `null` if DB_CONNECTION is "sqlite", `3306` if "mysql", `5432` if "pgsql" ''; @@ -227,6 +219,21 @@ in { this value to be filled. ''; }; + DB_HOST = mkOption { + type = str; + default = if cfg.settings.DB_CONNECTION == "pgsql" then "/run/postgresql" + else "localhost"; + defaultText = '' + "localhost" if DB_CONNECTION is "sqlite" or "mysql", "/run/postgresql" if "pgsql". + ''; + description = '' + The machine which hosts your database. This is left at the + default value for "mysql" because we use the "DB_SOCKET" option + to connect to a unix socket instead. "pgsql" requires that the + unix socket location be specified here instead of at "DB_SOCKET". + This option does not affect "sqlite". + ''; + }; APP_KEY_FILE = mkOption { type = path; description = '' @@ -235,6 +242,20 @@ in { /dev/urandom | base64)" > /path/to/key-file`. ''; }; + APP_URL = mkOption { + type = str; + default = if cfg.virtualHost == "localhost" then "http://${cfg.virtualHost}" + else "https://${cfg.virtualHost}"; + defaultText = '' + http(s)://''${config.services.firefly-iii.virtualHost} + ''; + description = '' + The APP_URL used by firefly-iii internally. Please make sure this + URL matches the external URL of your Firefly III installation. It + is used to validate specific requests and to generate URLs in + emails. + ''; + }; }; }; }; @@ -242,12 +263,6 @@ in { config = mkIf cfg.enable { - services.firefly-iii = { - settings = { - APP_URL = cfg.virtualHost; - }; - }; - services.phpfpm.pools.firefly-iii = { inherit user group; phpPackage = cfg.package.phpPackage; @@ -262,29 +277,27 @@ in { } // cfg.poolConfig; }; - systemd.services.phpfpm-firefly-iii.serviceConfig = { - EnvironmentFile = "/run/phpfpm/firefly-iii-env"; - ExecStartPost = "${pkgs.coreutils}/bin/rm /run/phpfpm/firefly-iii-env"; - }; - systemd.services.firefly-iii-setup = { + after = [ "postgresql.service" "mysql.service" ]; requiredBy = [ "phpfpm-firefly-iii.service" ]; before = [ "phpfpm-firefly-iii.service" ]; serviceConfig = { ExecStart = firefly-iii-maintenance; RuntimeDirectory = "phpfpm"; RuntimeDirectoryPreserve = true; + RemainAfterExit = true; } // commonServiceConfig; unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service"; + restartTriggers = [ cfg.package ]; }; systemd.services.firefly-iii-cron = { + after = [ "firefly-iii-setup.service" "postgresql.service" "mysql.service" ]; + wants = [ "firefly-iii-setup.service" ]; description = "Daily Firefly III cron job"; - script = '' - ${fileenv-func} - ${artisan} firefly-iii:cron - ''; - serviceConfig = commonServiceConfig; + serviceConfig = { + ExecStart = "${artisan} firefly-iii:cron"; + } // commonServiceConfig; }; systemd.timers.firefly-iii-cron = { @@ -295,6 +308,7 @@ in { Persistent = true; }; wantedBy = [ "timers.target" ]; + restartTriggers = [ cfg.package ]; }; services.nginx = mkIf cfg.enableNginx { diff --git a/nixpkgs/nixos/modules/services/web-apps/flarum.nix b/nixpkgs/nixos/modules/services/web-apps/flarum.nix new file mode 100644 index 000000000000..a967c3b121bd --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/flarum.nix @@ -0,0 +1,210 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.services.flarum; + + flarumInstallConfig = pkgs.writeText "config.json" (builtins.toJSON { + debug = false; + offline = false; + + baseUrl = cfg.baseUrl; + databaseConfiguration = cfg.database; + adminUser = { + username = cfg.adminUser; + password = cfg.initialAdminPassword; + email = cfg.adminEmail; + }; + settings = { + forum_title = cfg.forumTitle; + }; + }); +in { + options.services.flarum = { + enable = mkEnableOption "Flarum discussion platform"; + + package = mkPackageOption pkgs "flarum" { }; + + forumTitle = mkOption { + type = types.str; + default = "A Flarum Forum on NixOS"; + description = "Title of the forum."; + }; + + domain = mkOption { + type = types.str; + default = "localhost"; + example = "forum.example.com"; + description = "Domain to serve on."; + }; + + baseUrl = mkOption { + type = types.str; + default = "http://localhost"; + example = "https://forum.example.com"; + description = "Change `domain` instead."; + }; + + adminUser = mkOption { + type = types.str; + default = "flarum"; + description = "Username for first web application administrator"; + }; + + adminEmail = mkOption { + type = types.str; + default = "admin@example.com"; + description = "Email for first web application administrator"; + }; + + initialAdminPassword = mkOption { + type = types.str; + default = "flarum"; + description = "Initial password for the adminUser"; + }; + + user = mkOption { + type = types.str; + default = "flarum"; + description = "System user to run Flarum"; + }; + + group = mkOption { + type = types.str; + default = "flarum"; + description = "System group to run Flarum"; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/flarum"; + description = "Home directory for writable storage"; + }; + + database = mkOption rec { + type = with types; attrsOf (oneOf [str bool int]); + description = "MySQL database parameters"; + default = { + # the database driver; i.e. MySQL; MariaDB... + driver = "mysql"; + # the host of the connection; localhost in most cases unless using an external service + host = "localhost"; + # the name of the database in the instance + database = "flarum"; + # database username + username = "flarum"; + # database password + password = ""; + # the prefix for the tables; useful if you are sharing the same database with another service + prefix = ""; + # the port of the connection; defaults to 3306 with MySQL + port = 3306; + strict = false; + }; + }; + + createDatabaseLocally = mkOption { + type = types.bool; + default = true; + description = "Create the database and database user locally, and run installation."; + }; + }; + + config = mkIf cfg.enable { + users.users.${cfg.user} = { + isSystemUser = true; + home = cfg.stateDir; + createHome = true; + group = cfg.group; + }; + users.groups.${cfg.group} = {}; + + services.phpfpm.pools.flarum = { + user = cfg.user; + settings = { + "listen.owner" = config.services.nginx.user; + "listen.group" = config.services.nginx.group; + "listen.mode" = "0600"; + "pm" = mkDefault "dynamic"; + "pm.max_children" = mkDefault 10; + "pm.max_requests" = mkDefault 500; + "pm.start_servers" = mkDefault 2; + "pm.min_spare_servers" = mkDefault 1; + "pm.max_spare_servers" = mkDefault 3; + }; + phpOptions = '' + error_log = syslog + log_errors = on + ''; + }; + + services.nginx = { + enable = true; + virtualHosts."${cfg.domain}" = { + root = "${cfg.stateDir}/public"; + locations."~ \.php$".extraConfig = '' + fastcgi_pass unix:${config.services.phpfpm.pools.flarum.socket}; + fastcgi_index site.php; + ''; + extraConfig = '' + index index.php; + include ${cfg.package}/share/php/flarum/.nginx.conf; + ''; + }; + }; + + services.mysql = mkIf cfg.enable { + enable = true; + package = pkgs.mysql; + ensureDatabases = [cfg.database.database]; + ensureUsers = [ + { + name = cfg.database.username; + ensurePermissions = { + "${cfg.database.database}.*" = "ALL PRIVILEGES"; + }; + } + ]; + }; + + assertions = [ + { + assertion = !cfg.createDatabaseLocally || cfg.database.driver == "mysql"; + message = "Flarum can only be automatically installed in MySQL/MariaDB."; + } + ]; + + systemd.services.flarum-install = { + description = "Flarum installation"; + requiredBy = ["phpfpm-flarum.service"]; + before = ["phpfpm-flarum.service"]; + requires = ["mysql.service"]; + after = ["mysql.service"]; + serviceConfig = { + Type = "oneshot"; + User = cfg.user; + Group = cfg.group; + }; + path = [config.services.phpfpm.phpPackage]; + script = '' + mkdir -p ${cfg.stateDir}/{extensions,public/assets/avatars} + mkdir -p ${cfg.stateDir}/storage/{cache,formatter,sessions,views} + cd ${cfg.stateDir} + cp -f ${cfg.package}/share/php/flarum/{extend.php,site.php,flarum} . + ln -sf ${cfg.package}/share/php/flarum/vendor . + ln -sf ${cfg.package}/share/php/flarum/public/index.php public/ + chmod a+x . public + chmod +x site.php extend.php flarum + '' + optionalString (cfg.createDatabaseLocally && cfg.database.driver == "mysql") '' + if [ ! -f config.php ]; then + php flarum install --file=${flarumInstallConfig} + fi + php flarum migrate + php flarum cache:clear + ''; + }; + }; + + meta.maintainers = with lib.maintainers; [ fsagbuya jasonodoom ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix index 77c5ecb24617..021101fecaa4 100644 --- a/nixpkgs/nixos/modules/services/web-apps/freshrss.nix +++ b/nixpkgs/nixos/modules/services/web-apps/freshrss.nix @@ -10,7 +10,7 @@ in meta.maintainers = with maintainers; [ etu stunkymonkey mattchrist ]; options.services.freshrss = { - enable = mkEnableOption "FreshRSS feed reader"; + enable = mkEnableOption "FreshRSS RSS aggregator and reader with php-fpm backend."; package = mkPackageOption pkgs "freshrss" { }; @@ -108,7 +108,7 @@ in type = types.str; default = poolName; description = '' - Name of the phpfpm pool to use and setup. If not specified, a pool will be created + Name of the php-fpm pool to use and setup. If not specified, a pool will be created with default values. ''; }; @@ -255,13 +255,10 @@ in { description = "Set up the state directory for FreshRSS before use"; wantedBy = [ "multi-user.target" ]; - serviceConfig = defaultServiceConfig //{ - Type = "oneshot"; - User = "freshrss"; - Group = "freshrss"; - StateDirectory = "freshrss"; - WorkingDirectory = cfg.package; + serviceConfig = defaultServiceConfig // { + RemainAfterExit = true; }; + restartIfChanged = true; environment = { DATA_PATH = cfg.dataDir; }; @@ -299,7 +296,7 @@ in environment = { DATA_PATH = cfg.dataDir; }; - serviceConfig = defaultServiceConfig //{ + serviceConfig = defaultServiceConfig // { ExecStart = "${cfg.package}/app/actualize_script.php"; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix index 4d0e25958e35..9a9f180b2102 100644 --- a/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix +++ b/nixpkgs/nixos/modules/services/web-apps/invoiceplane.nix @@ -39,20 +39,17 @@ let extraConfig = hostName: cfg: let settings = mapAttrsToList (k: v: "${k}=${mkPhpValue v}") cfg.settings; - in pkgs.writeText "extraConfig.php" '' - ${concatStringsSep "\n" settings} - ${toString cfg.extraConfig} - ''; + in pkgs.writeText "extraConfig.php" (concatStringsSep "\n" settings); pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec { pname = "invoiceplane-${hostName}"; version = src.version; src = pkgs.invoiceplane; - postPhase = '' + postPatch = '' # Patch index.php file to load additional config file substituteInPlace index.php \ - --replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();"; + --replace-fail "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();"; ''; installPhase = '' @@ -182,25 +179,6 @@ let ''; }; - extraConfig = mkOption { - type = types.nullOr types.lines; - default = null; - example = '' - SETUP_COMPLETED=true - DISABLE_SETUP=true - IP_URL=https://invoice.example.com - ''; - description = '' - InvoicePlane configuration. Refer to - <https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example> - for details on supported values. - - **Note**: Please pass structured settings via - `services.invoiceplane.sites.${name}.settings` instead, this option - will get deprecated in the future. - ''; - }; - settings = mkOption { type = types.attrsOf types.anything; default = {}; @@ -269,12 +247,6 @@ in # implementation config = mkIf (eachSite != {}) (mkMerge [{ - warnings = flatten (mapAttrsToList (hostName: cfg: [ - (optional (cfg.extraConfig != null) '' - services.invoiceplane.sites."${hostName}".extraConfig will be deprecated in future releases, please use the settings option now. - '') - ]) eachSite); - assertions = flatten (mapAttrsToList (hostName: cfg: [ { assertion = cfg.database.createLocally -> cfg.database.user == user; message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned''; diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix index 570f2770fb29..daebd6441cb5 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix @@ -20,8 +20,6 @@ let DB_USER = cfg.database.user; - REDIS_HOST = cfg.redis.host; - REDIS_PORT = toString(cfg.redis.port); DB_HOST = cfg.database.host; DB_NAME = cfg.database.name; LOCAL_DOMAIN = cfg.localDomain; @@ -34,6 +32,8 @@ let TRUSTED_PROXY_IP = cfg.trustedProxy; } + // lib.optionalAttrs (cfg.redis.host != null) { REDIS_HOST = cfg.redis.host; } + // lib.optionalAttrs (cfg.redis.port != null) { REDIS_PORT = toString(cfg.redis.port); } // lib.optionalAttrs (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { REDIS_URL = "unix://${config.services.redis.servers.mastodon.unixSocket}"; } // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; } // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN = cfg.smtp.user; } @@ -90,6 +90,11 @@ let SystemCallArchitectures = "native"; }; + # Services that all Mastodon units After= and Requires= on + commonServices = lib.optional redisActuallyCreateLocally "redis-mastodon.service" + ++ lib.optional databaseActuallyCreateLocally "postgresql.service" + ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + envFile = pkgs.writeText "mastodon.env" (lib.concatMapStrings (s: s + "\n") ( (lib.concatLists (lib.mapAttrsToList (name: value: lib.optional (value != null) ''${name}="${toString value}"'' @@ -117,14 +122,8 @@ let jobClassLabel = toString ([""] ++ processCfg.jobClasses); threads = toString (if processCfg.threads == null then cfg.sidekiqThreads else processCfg.threads); in { - after = [ "network.target" "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; - requires = [ "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices; + requires = [ "mastodon-init-dirs.service" ] ++ commonServices; description = "Mastodon sidekiq${jobClassLabel}"; wantedBy = [ "mastodon.target" ]; environment = env // { @@ -149,14 +148,8 @@ let (map (i: { name = "mastodon-streaming-${toString i}"; value = { - after = [ "network.target" "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; - requires = [ "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices; + requires = [ "mastodon-init-dirs.service" ] ++ commonServices; wantedBy = [ "mastodon.target" "mastodon-streaming.target" ]; description = "Mastodon streaming ${toString i}"; environment = env // { SOCKET = "/run/mastodon-streaming/streaming-${toString i}.socket"; }; @@ -401,14 +394,20 @@ in { host = lib.mkOption { description = "Redis host."; - type = lib.types.str; - default = "127.0.0.1"; + type = lib.types.nullOr lib.types.str; + default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then "127.0.0.1" else null; + defaultText = lib.literalExpression '' + if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket} then "127.0.0.1" else null + ''; }; port = lib.mkOption { description = "Redis port."; - type = lib.types.port; - default = 31637; + type = lib.types.nullOr lib.types.port; + default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then 31637 else null; + defaultText = lib.literalExpression '' + if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket} then 31637 else null + ''; }; passwordFile = lib.mkOption { @@ -632,6 +631,20 @@ in { config = lib.mkIf cfg.enable (lib.mkMerge [{ assertions = [ { + assertion = !redisActuallyCreateLocally -> (cfg.redis.host != "127.0.0.1" && cfg.redis.port != null); + message = '' + `services.mastodon.redis.host` and `services.mastodon.redis.port` need to be set if + `services.mastodon.redis.createLocally` is not enabled. + ''; + } + { + assertion = redisActuallyCreateLocally -> (!cfg.redis.enableUnixSocket || (cfg.redis.host == null && cfg.redis.port == null)); + message = '' + `services.mastodon.redis.enableUnixSocket` needs to be disabled if + `services.mastodon.redis.host` and `services.mastodon.redis.port` is used. + ''; + } + { assertion = redisActuallyCreateLocally -> (!cfg.redis.enableUnixSocket || cfg.redis.passwordFile == null); message = '' <option>services.mastodon.redis.enableUnixSocket</option> needs to be disabled if @@ -783,14 +796,8 @@ in { }; systemd.services.mastodon-web = { - after = [ "network.target" "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; - requires = [ "mastodon-init-dirs.service" ] - ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service" - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + after = [ "network.target" "mastodon-init-dirs.service" ] ++ commonServices; + requires = [ "mastodon-init-dirs.service" ] ++ commonServices; wantedBy = [ "mastodon.target" ]; description = "Mastodon web"; environment = env // (if cfg.enableUnixSocket diff --git a/nixpkgs/nixos/modules/services/web-apps/mealie.nix b/nixpkgs/nixos/modules/services/web-apps/mealie.nix index 8f68828e7a0b..2484b2489c0d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/mealie.nix +++ b/nixpkgs/nixos/modules/services/web-apps/mealie.nix @@ -28,8 +28,6 @@ in Configuration of the Mealie service. See [the mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values. - - In addition to the official documentation, you can set {env}`MEALIE_LOG_FILE`. ''; example = { ALLOW_SIGNUP = "false"; @@ -61,6 +59,7 @@ in PRODUCTION = "true"; ALEMBIC_CONFIG_FILE="${pkg}/config/alembic.ini"; API_PORT = toString cfg.port; + BASE_URL = "http://localhost:${toString cfg.port}"; DATA_DIR = "/var/lib/mealie"; CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel"; } // (builtins.mapAttrs (_: val: toString val) cfg.settings); diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix index d6d17158a559..4da5aff0c83e 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud-notify_push.nix @@ -90,7 +90,7 @@ in export DATABASE_PASSWORD="$(<"${cfg.dbpassFile}")" '' + '' export DATABASE_URL="${dbUrl}" - ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php' + exec ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php' ''; serviceConfig = { User = "nextcloud"; @@ -98,6 +98,7 @@ in RuntimeDirectory = [ "nextcloud-notify_push" ]; Restart = "on-failure"; RestartSec = "5s"; + Type = "notify"; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix index 91d03dc16077..d7eb2c6cb734 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix @@ -793,6 +793,16 @@ in { ''; }; }; + + cron.memoryLimit = mkOption { + type = types.nullOr types.str; + default = null; + example = "1G"; + description = '' + The `memory_limit` of PHP is equal to [](#opt-services.nextcloud.maxUploadSize). + The value can be customized for `nextcloud-cron.service` using this option. + ''; + }; }; config = mkIf cfg.enable (mkMerge [ @@ -1001,7 +1011,13 @@ in { Type = "exec"; User = "nextcloud"; ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e"; - ExecStart = "${lib.getExe phpPackage} -f ${webroot}/cron.php"; + ExecStart = lib.concatStringsSep " " ([ + (lib.getExe phpPackage) + ] ++ optional (cfg.cron.memoryLimit != null) "-dmemory_limit=${cfg.cron.memoryLimit}" + ++ [ + "-f" + "${webroot}/cron.php" + ]); KillMode = "process"; }; }; diff --git a/nixpkgs/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix b/nixpkgs/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix new file mode 100644 index 000000000000..9bd2cf310c0a --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/nextjs-ollama-llm-ui.nix @@ -0,0 +1,87 @@ +{ + config, + pkgs, + lib, + ... +}: +let + cfg = config.services.nextjs-ollama-llm-ui; + # we have to override the URL to a Ollama service here, because it gets baked into the web app. + nextjs-ollama-llm-ui = cfg.package.override { inherit (cfg) ollamaUrl; }; +in +{ + options = { + services.nextjs-ollama-llm-ui = { + enable = lib.mkEnableOption '' + Simple Ollama web UI service; an easy to use web frontend for a Ollama backend service. + Run state-of-the-art AI large language models (LLM) similar to ChatGPT locally with privacy + on your personal computer. + This service is stateless and doesn't store any data on the server; all data is kept + locally in your web browser. + See https://github.com/jakobhoeg/nextjs-ollama-llm-ui. + + Required: You need the Ollama backend service running by having + "services.nextjs-ollama-llm-ui.ollamaUrl" point to the correct url. + You can host such a backend service with NixOS through "services.ollama". + ''; + package = lib.mkPackageOption pkgs "nextjs-ollama-llm-ui" { }; + + hostname = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + example = "ui.example.org"; + description = '' + The hostname under which the Ollama UI interface should be accessible. + By default it uses localhost/127.0.0.1 to be accessible only from the local machine. + Change to "0.0.0.0" to make it directly accessible from the local network. + + Note: You should keep it at 127.0.0.1 and only serve to the local + network or internet from a (home) server behind a reverse-proxy and secured encryption. + See https://wiki.nixos.org/wiki/Nginx for instructions on how to set up a reverse-proxy. + ''; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 3000; + example = 3000; + description = '' + The port under which the Ollama UI interface should be accessible. + ''; + }; + + ollamaUrl = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1:11434"; + example = "https://ollama.example.org"; + description = '' + The address (including host and port) under which we can access the Ollama backend server. + !Note that if the the UI service is running under a domain "https://ui.example.org", + the Ollama backend service must allow "CORS" requests from this domain, e.g. by adding + "services.ollama.environment.OLLAMA_ORIGINS = [ ... "https://ui.example.org" ];"! + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services = { + + nextjs-ollama-llm-ui = { + wantedBy = [ "multi-user.target" ]; + description = "Nextjs Ollama LLM Ui."; + after = [ "network.target" ]; + environment = { + HOSTNAME = cfg.hostname; + PORT = toString cfg.port; + NEXT_PUBLIC_OLLAMA_URL = cfg.ollamaUrl; + }; + serviceConfig = { + ExecStart = "${lib.getExe nextjs-ollama-llm-ui}"; + DynamicUser = true; + }; + }; + }; + }; + meta.maintainers = with lib.maintainers; [ malteneuss ]; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix index 7c8a2a6687b9..4c095ea79bbd 100644 --- a/nixpkgs/nixos/modules/services/web-apps/node-red.nix +++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix @@ -5,15 +5,6 @@ with lib; let cfg = config.services.node-red; defaultUser = "node-red"; - finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package; - node-red_withNpmAndGcc = pkgs.runCommand "node-red" { - nativeBuildInputs = [ pkgs.makeWrapper ]; - } - '' - mkdir -p $out/bin - makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \ - --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \ - ''; in { options.services.node-red = { @@ -127,11 +118,12 @@ in environment = { HOME = cfg.userDir; }; + path = lib.optionals cfg.withNpmAndGcc [ pkgs.nodePackages.npm pkgs.gcc ]; serviceConfig = mkMerge [ { User = cfg.user; Group = cfg.group; - ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}"; + ExecStart = "${cfg.package}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}"; PrivateTmp = true; Restart = "always"; WorkingDirectory = cfg.userDir; diff --git a/nixpkgs/nixos/modules/services/web-apps/pretix.nix b/nixpkgs/nixos/modules/services/web-apps/pretix.nix index 498face7456d..9786b6116026 100644 --- a/nixpkgs/nixos/modules/services/web-apps/pretix.nix +++ b/nixpkgs/nixos/modules/services/web-apps/pretix.nix @@ -310,7 +310,7 @@ in type = types.str; default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=1"; defaultText = literalExpression '' - optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1" + redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1 ''; description = '' URI to the celery backend used for the asynchronous job queue. @@ -321,7 +321,7 @@ in type = types.str; default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=2"; defaultText = literalExpression '' - optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2" + redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2 ''; description = '' URI to the celery broker used for the asynchronous job queue. diff --git a/nixpkgs/nixos/modules/services/web-apps/simplesamlphp.nix b/nixpkgs/nixos/modules/services/web-apps/simplesamlphp.nix new file mode 100644 index 000000000000..e970266fc17d --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/simplesamlphp.nix @@ -0,0 +1,128 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.simplesamlphp; + + format = pkgs.formats.php { finalVariable = "config"; }; + + generateConfig = + opts: + pkgs.runCommand "simplesamlphp-config" { } '' + mkdir $out + cp ${format.generate "config.php" opts.settings} $out/config.php + cp ${format.generate "authsources.php" opts.authSources} $out/authsources.php + ''; +in +{ + meta = { + maintainers = with lib.maintainers; [ nhnn ]; + }; + + options.services.simplesamlphp = + with lib; + mkOption { + type = types.attrsOf ( + types.submodule ( + { config, ... }: + { + options = { + package = mkPackageOption pkgs "simplesamlphp" { }; + configureNginx = mkOption { + type = types.bool; + default = true; + description = "Configure nginx as a reverse proxy for SimpleSAMLphp."; + }; + phpfpmPool = mkOption { + type = types.str; + description = "The PHP-FPM pool that serves SimpleSAMLphp instance."; + }; + localDomain = mkOption { + type = types.str; + description = "The domain serving your SimpleSAMLphp instance. This option modifies only /saml route."; + }; + settings = mkOption { + type = types.submodule { + freeformType = format.type; + options = { + baseurlpath = mkOption { + type = types.str; + example = "https://filesender.example.com/saml/"; + description = "URL where SimpleSAMLphp can be reached."; + }; + }; + }; + default = { }; + description = '' + Configuration options used by SimpleSAMLphp. + See [](https://simplesamlphp.org/docs/stable/simplesamlphp-install) + for available options. + ''; + }; + + authSources = mkOption { + type = format.type; + default = { }; + description = '' + Auth sources options used by SimpleSAMLphp. + ''; + }; + + libDir = mkOption { + type = types.str; + readOnly = true; + description = '' + Path to the SimpleSAMLphp library directory. + ''; + }; + configDir = mkOption { + type = types.str; + readOnly = true; + description = '' + Path to the SimpleSAMLphp config directory. + ''; + }; + }; + config = { + libDir = "${config.package}/share/php/simplesamlphp/"; + configDir = "${generateConfig config}"; + }; + } + ) + ); + default = { }; + description = "Instances of SimpleSAMLphp. This module is designed to work with already existing PHP-FPM pool and NGINX virtualHost."; + }; + + config = { + services.phpfpm.pools = lib.mapAttrs' ( + phpfpmName: opts: + lib.nameValuePair opts.phpfpmPool { phpEnv.SIMPLESAMLPHP_CONFIG_DIR = "${generateConfig opts}"; } + ) cfg; + + services.nginx.virtualHosts = lib.mapAttrs' ( + phpfpmName: opts: + lib.nameValuePair opts.localDomain ( + lib.mkIf opts.configureNginx { + locations."^~ /saml/" = { + alias = "${opts.package}/share/php/simplesamlphp/www/"; + extraConfig = '' + location ~ ^(?<prefix>/saml)(?<phpfile>.+?\.php)(?<pathinfo>/.*)?$ { + include ${pkgs.nginx}/conf/fastcgi.conf; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${config.services.phpfpm.pools.${phpfpmName}.socket}; + fastcgi_intercept_errors on; + fastcgi_param SCRIPT_FILENAME $document_root$phpfile; + fastcgi_param SCRIPT_NAME /saml$phpfile; + fastcgi_param PATH_INFO $pathinfo if_not_empty; + } + ''; + }; + } + ) + ) cfg; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/slskd.nix b/nixpkgs/nixos/modules/services/web-apps/slskd.nix index 15a5fd1177ad..6254fe294eee 100644 --- a/nixpkgs/nixos/modules/services/web-apps/slskd.nix +++ b/nixpkgs/nixos/modules/services/web-apps/slskd.nix @@ -5,7 +5,7 @@ let defaultUser = "slskd"; in { options.services.slskd = with lib; with types; { - enable = mkEnableOption "enable slskd"; + enable = mkEnableOption "slskd"; package = mkPackageOptionMD pkgs "slskd" { }; diff --git a/nixpkgs/nixos/modules/services/web-servers/bluemap.nix b/nixpkgs/nixos/modules/services/web-servers/bluemap.nix new file mode 100644 index 000000000000..28eaad3db313 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-servers/bluemap.nix @@ -0,0 +1,311 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.bluemap; + format = pkgs.formats.hocon { }; + + coreConfig = format.generate "core.conf" cfg.coreSettings; + webappConfig = format.generate "webapp.conf" cfg.webappSettings; + webserverConfig = format.generate "webserver.conf" cfg.webserverSettings; + + mapsFolder = pkgs.linkFarm "maps" + (lib.attrsets.mapAttrs' (name: value: + lib.nameValuePair "${name}.conf" + (format.generate "${name}.conf" value)) + cfg.maps); + + storageFolder = pkgs.linkFarm "storage" + (lib.attrsets.mapAttrs' (name: value: + lib.nameValuePair "${name}.conf" + (format.generate "${name}.conf" value)) + cfg.storage); + + configFolder = pkgs.linkFarm "bluemap-config" { + "maps" = mapsFolder; + "storages" = storageFolder; + "core.conf" = coreConfig; + "webapp.conf" = webappConfig; + "webserver.conf" = webserverConfig; + "resourcepacks" = pkgs.linkFarm "resourcepacks" cfg.resourcepacks; + }; + + inherit (lib) mkOption; +in { + options.services.bluemap = { + enable = lib.mkEnableOption "bluemap"; + + eula = mkOption { + type = lib.types.bool; + description = '' + By changing this option to true you confirm that you own a copy of minecraft Java Edition, + and that you agree to minecrafts EULA. + ''; + default = false; + }; + + defaultWorld = mkOption { + type = lib.types.path; + description = '' + The world used by the default map ruleset. + If you configure your own maps you do not need to set this. + ''; + example = lib.literalExpression "\${config.services.minecraft.dataDir}/world"; + }; + + enableRender = mkOption { + type = lib.types.bool; + description = "Enable rendering"; + default = true; + }; + + webRoot = mkOption { + type = lib.types.path; + default = "/var/lib/bluemap/web"; + description = "The directory for saving and serving the webapp and the maps"; + }; + + enableNginx = mkOption { + type = lib.types.bool; + default = true; + description = "Enable configuring a virtualHost for serving the bluemap webapp"; + }; + + host = mkOption { + type = lib.types.str; + default = "bluemap.${config.networking.domain}"; + defaultText = lib.literalExpression "bluemap.\${config.networking.domain}"; + description = "Domain to configure nginx for"; + }; + + onCalendar = mkOption { + type = lib.types.str; + description = '' + How often to trigger rendering the map, + in the format of a systemd timer onCalendar configuration. + See {manpage}`systemd.timer(5)`. + ''; + default = "*-*-* 03:10:00"; + }; + + coreSettings = mkOption { + type = lib.types.submodule { + freeformType = format.type; + options = { + data = mkOption { + type = lib.types.path; + description = "Folder for where bluemap stores its data"; + default = "/var/lib/bluemap"; + }; + metrics = lib.mkEnableOption "Sending usage metrics containing the version of bluemap in use"; + }; + }; + description = "Settings for the core.conf file, [see upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/core.conf)."; + }; + + webappSettings = mkOption { + type = lib.types.submodule { + freeformType = format.type; + }; + default = { + enabled = true; + webroot = cfg.webRoot; + }; + defaultText = lib.literalExpression '' + { + enabled = true; + webroot = config.services.bluemap.webRoot; + } + ''; + description = "Settings for the webapp.conf file, see [upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webapp.conf)."; + }; + + webserverSettings = mkOption { + type = lib.types.submodule { + freeformType = format.type; + options = { + enabled = mkOption { + type = lib.types.bool; + description = '' + Enable bluemap's built-in webserver. + Disabled by default in nixos for use of nginx directly. + ''; + default = false; + }; + }; + }; + default = { }; + description = '' + Settings for the webserver.conf file, usually not required. + [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webserver.conf). + ''; + }; + + maps = mkOption { + type = lib.types.attrsOf (lib.types.submodule { + freeformType = format.type; + options = { + world = lib.mkOption { + type = lib.types.path; + description = "Path to world folder containing the dimension to render"; + }; + }; + }); + default = { + "overworld" = { + world = "${cfg.defaultWorld}"; + ambient-light = 0.1; + cave-detection-ocean-floor = -5; + }; + + "nether" = { + world = "${cfg.defaultWorld}/DIM-1"; + sorting = 100; + sky-color = "#290000"; + void-color = "#150000"; + ambient-light = 0.6; + world-sky-light = 0; + remove-caves-below-y = -10000; + cave-detection-ocean-floor = -5; + cave-detection-uses-block-light = true; + max-y = 90; + }; + + "end" = { + world = "${cfg.defaultWorld}/DIM1"; + sorting = 200; + sky-color = "#080010"; + void-color = "#080010"; + ambient-light = 0.6; + world-sky-light = 0; + remove-caves-below-y = -10000; + cave-detection-ocean-floor = -5; + }; + }; + defaultText = lib.literalExpression '' + { + "overworld" = { + world = "''${cfg.defaultWorld}"; + ambient-light = 0.1; + cave-detection-ocean-floor = -5; + }; + + "nether" = { + world = "''${cfg.defaultWorld}/DIM-1"; + sorting = 100; + sky-color = "#290000"; + void-color = "#150000"; + ambient-light = 0.6; + world-sky-light = 0; + remove-caves-below-y = -10000; + cave-detection-ocean-floor = -5; + cave-detection-uses-block-light = true; + max-y = 90; + }; + + "end" = { + world = "''${cfg.defaultWorld}/DIM1"; + sorting = 200; + sky-color = "#080010"; + void-color = "#080010"; + ambient-light = 0.6; + world-sky-light = 0; + remove-caves-below-y = -10000; + cave-detection-ocean-floor = -5; + }; + }; + ''; + description = '' + Settings for files in `maps/`. + If you define anything here you must define everything yourself. + See the default for an example with good options for the different world types. + For valid values [consult upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf). + ''; + }; + + storage = mkOption { + type = lib.types.attrsOf (lib.types.submodule { + freeformType = format.type; + options = { + storage-type = mkOption { + type = lib.types.enum [ "FILE" "SQL" ]; + description = "Type of storage config"; + default = "FILE"; + }; + }; + }); + description = '' + Where the rendered map will be stored. + Unless you are doing something advanced you should probably leave this alone and configure webRoot instead. + [See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/tree/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages) + ''; + default = { + "file" = { + root = "${cfg.webRoot}/maps"; + }; + }; + defaultText = lib.literalExpression '' + { + "file" = { + root = "''${config.services.bluemap.webRoot}/maps"; + }; + } + ''; + }; + + resourcepacks = mkOption { + type = lib.types.attrsOf lib.types.pathInStore; + default = { }; + description = "A set of resourcepacks to use, loaded in alphabetical order"; + }; + }; + + + config = lib.mkIf cfg.enable { + assertions = + [ { assertion = config.services.bluemap.eula; + message = '' + You have enabled bluemap but have not accepted minecraft's EULA. + You can achieve this through setting `services.bluemap.eula = true` + ''; + } + ]; + + services.bluemap.coreSettings.accept-download = cfg.eula; + + systemd.services."render-bluemap-maps" = lib.mkIf cfg.enableRender { + serviceConfig = { + Type = "oneshot"; + Group = "nginx"; + UMask = "026"; + }; + script = '' + ${lib.getExe pkgs.bluemap} -c ${configFolder} -gs -r + ''; + }; + + systemd.timers."render-bluemap-maps" = lib.mkIf cfg.enableRender { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.onCalendar; + Persistent = true; + Unit = "render-bluemap-maps.service"; + }; + }; + + services.nginx.virtualHosts = lib.mkIf cfg.enableNginx { + "${cfg.host}" = { + root = config.services.bluemap.webRoot; + locations = { + "~* ^/maps/[^/]*/tiles/[^/]*.json$".extraConfig = '' + error_page 404 =200 /assets/emptyTile.json; + gzip_static always; + ''; + "~* ^/maps/[^/]*/tiles/[^/]*.png$".tryFiles = "$uri =204"; + }; + }; + }; + }; + + meta = { + maintainers = with lib.maintainers; [ dandellion h7x4 ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-servers/garage.nix b/nixpkgs/nixos/modules/services/web-servers/garage.nix index d2a5109e266a..7cf71ff6ff06 100644 --- a/nixpkgs/nixos/modules/services/web-servers/garage.nix +++ b/nixpkgs/nixos/modules/services/web-servers/garage.nix @@ -10,7 +10,7 @@ in { meta = { doc = ./garage.md; - maintainers = with pkgs.lib.maintainers; [ raitobezarius ]; + maintainers = [ ]; }; options.services.garage = { @@ -49,8 +49,15 @@ in data_dir = mkOption { default = "/var/lib/garage/data"; - type = types.path; - description = "The main data storage, put this on your large storage (e.g. high capacity HDD)"; + example = [ { + path = "/var/lib/garage/data"; + capacity = "2T"; + } ]; + type = with types; either path (listOf attrs); + description = '' + The directory in which Garage will store the data blocks of objects. This folder can be placed on an HDD. + Since v0.9.0, Garage supports multiple data directories, refer to https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#data_dir for the exact format. + ''; }; }; }; @@ -76,7 +83,7 @@ in # These assertions can be removed in NixOS 24.11, when all users have been # warned once. { - assertion = (cfg.settings ? replication_factor || cfg.settings ? replication_mode) || lib.versionOlder cfg.package "1.0.0"; + assertion = (cfg.settings ? replication_factor || cfg.settings ? replication_mode) || lib.versionOlder cfg.package.version "1.0.0"; message = '' Garage 1.0.0 requires an explicit replication factor to be set. Please set replication_factor to 1 explicitly to preserve the previous behavior. diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/tailscale-auth.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/tailscale-auth.nix index ca272268f572..de1d708cbb42 100644 --- a/nixpkgs/nixos/modules/services/web-servers/nginx/tailscale-auth.nix +++ b/nixpkgs/nixos/modules/services/web-servers/nginx/tailscale-auth.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: let inherit (lib) @@ -22,7 +22,7 @@ in ]; options.services.nginx.tailscaleAuth = { - enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate nginx users via tailscale."; + enable = mkEnableOption "tailscale.nginx-auth, to authenticate nginx users via tailscale"; expectedTailnet = mkOption { default = ""; diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix index 008bc65eb6a4..0e9a06706d4f 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix @@ -269,11 +269,6 @@ in programs.bash.vteIntegration = mkDefault true; programs.zsh.vteIntegration = mkDefault true; - # Use native GTK file chooser on Qt apps. This is because Qt does not know Pantheon. - # https://invent.kde.org/qt/qt/qtbase/-/blob/6.6/src/gui/platform/unix/qgenericunixthemes.cpp#L1312 - # https://github.com/elementary/default-settings/blob/7.0.2/profile.d/qt-qpa-platformtheme.sh - environment.variables.QT_QPA_PLATFORMTHEME = mkDefault "gtk3"; - # Default Fonts fonts.packages = with pkgs; [ inter diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix index e8494b2c017c..12b39f927c01 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/phosh.nix @@ -216,7 +216,7 @@ in security.pam.services.phosh = {}; - hardware.opengl.enable = mkDefault true; + hardware.graphics.enable = mkDefault true; services.gnome.core-shell.enable = true; services.gnome.core-os-services.enable = true; diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix index 7d80b9b2641c..53d3b91bfa17 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -327,7 +327,7 @@ in }; # Enable GTK applications to load SVG icons - services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; + programs.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; fonts.packages = with pkgs; [ cfg.notoPackage hack-font ]; fonts.fontconfig.defaultFonts = { diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix index 85d0d199de3f..727802f3a63e 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -153,7 +153,7 @@ in }]; services.xserver.updateDbusEnvironment = true; - services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; + programs.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; # Enable helpful DBus services. services.udisks2.enable = true; diff --git a/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix b/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix deleted file mode 100644 index 9e89d9f96c4a..000000000000 --- a/nixpkgs/nixos/modules/services/x11/gdk-pixbuf.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.services.xserver.gdk-pixbuf; - - loadersCache = pkgs.gnome._gdkPixbufCacheBuilder_DO_NOT_USE { - extraLoaders = lib.unique (cfg.modulePackages); - }; -in - -{ - options = { - services.xserver.gdk-pixbuf.modulePackages = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = [ ]; - description = "Packages providing GDK-Pixbuf modules, for cache generation."; - }; - }; - - # If there is any package configured in modulePackages, we generate the - # loaders.cache based on that and set the environment variable - # GDK_PIXBUF_MODULE_FILE to point to it. - config = lib.mkIf (cfg.modulePackages != []) { - environment.sessionVariables = { - GDK_PIXBUF_MODULE_FILE = "${loadersCache}"; - }; - }; -} diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix index 700ead836600..4603ca3fb50f 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix @@ -7,6 +7,10 @@ let in { + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "windowManager" "qtile" "backend" ] "The qtile package now provides separate display sessions for both X11 and Wayland.") + ]; + options.services.xserver.windowManager.qtile = { enable = mkEnableOption "qtile"; @@ -22,14 +26,6 @@ in ''; }; - backend = mkOption { - type = types.enum [ "x11" "wayland" ]; - default = "x11"; - description = '' - Backend to use in qtile: `x11` or `wayland`. - ''; - }; - extraPackages = mkOption { type = types.functionTo (types.listOf types.package); default = _: []; @@ -57,25 +53,14 @@ in }; config = mkIf cfg.enable { - services.xserver.windowManager.qtile.finalPackage = pkgs.python3.withPackages (p: - [ (cfg.package.unwrapped or cfg.package) ] ++ (cfg.extraPackages p) - ); - - services.xserver.windowManager.session = [{ - name = "qtile"; - start = '' - ${cfg.finalPackage}/bin/qtile start -b ${cfg.backend} \ - ${optionalString (cfg.configFile != null) - "--config \"${cfg.configFile}\""} & - waitPID=$! - ''; - }]; + services = { + xserver.windowManager.qtile.finalPackage = pkgs.python3.pkgs.qtile.override { extraPackages = cfg.extraPackages pkgs.python3.pkgs; }; + displayManager.sessionPackages = [ cfg.finalPackage ]; + }; - environment.systemPackages = [ - # pkgs.qtile is currently a buildenv of qtile and its dependencies. - # For userland commands, we want the underlying package so that - # packages such as python don't bleed into userland and overwrite intended behavior. - (cfg.package.unwrapped or cfg.package) - ]; + environment = { + etc."xdg/qtile/config.py" = mkIf (cfg.configFile != null) { source = cfg.configFile; }; + systemPackages = [ cfg.finalPackage ]; + }; }; } diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix index 5a86d055c271..57e83399eded 100644 --- a/nixpkgs/nixos/modules/services/x11/xserver.nix +++ b/nixpkgs/nixos/modules/services/x11/xserver.nix @@ -302,7 +302,7 @@ in default = [ "modesetting" "fbdev" ]; example = [ "nvidia" - "amdgpu-pro" + "amdgpu" ]; # TODO(@oxij): think how to easily add the rest, like those nvidia things relatedPackages = concatLists @@ -716,10 +716,7 @@ in restartIfChanged = false; - environment = - optionalAttrs config.hardware.opengl.setLdLibraryPath - { LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; } - // config.services.displayManager.environment; + environment = config.services.displayManager.environment; preStart = '' |