diff options
Diffstat (limited to 'nixos/modules')
43 files changed, 1024 insertions, 212 deletions
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index ca2e911597296..2c19fb8a029d3 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -42,8 +42,8 @@ in strings. The latter is concatenated, interspersed with colon characters. ''; - type = with types; attrsOf (oneOf [ (listOf str) str path ]); - apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else "${v}"); + type = with types; attrsOf (oneOf [ (listOf (oneOf [ float int str ])) float int str path ]); + apply = mapAttrs (n: v: if isList v then concatMapStringsSep ":" toString v else toString v); }; environment.profiles = mkOption { diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix index 562485fcc823d..1846ac51eea6e 100644 --- a/nixos/modules/config/zram.nix +++ b/nixos/modules/config/zram.nix @@ -73,7 +73,7 @@ in algorithm = lib.mkOption { default = "zstd"; example = "lz4"; - type = with lib.types; either (enum [ "lzo" "lz4" "zstd" ]) str; + type = with lib.types; either (enum [ "842" "lzo" "lzo-rle" "lz4" "lz4hc" "zstd" ]) str; description = '' Compression algorithm. `lzo` has good compression, but is slow. `lz4` has bad compression, but is fast. diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 433bcd93213d9..60a6fb57c7a29 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -153,11 +153,10 @@ in ''; type = configType; description = '' - The configuration of the Nix Packages collection. (For - details, see the Nixpkgs documentation.) It allows you to set - package configuration options. + Global configuration for Nixpkgs. + The complete list of [Nixpkgs configuration options](https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference) is in the [Nixpkgs manual section on global configuration](https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig). - Ignored when `nixpkgs.pkgs` is set. + Ignored when {option}`nixpkgs.pkgs` is set. ''; }; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 6c43cd06180ab..2a25561047316 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -179,6 +179,7 @@ ./programs/environment.nix ./programs/evince.nix ./programs/extra-container.nix + ./programs/fcast-receiver.nix ./programs/feedbackd.nix ./programs/file-roller.nix ./programs/firefox.nix @@ -233,6 +234,7 @@ ./programs/neovim.nix ./programs/nethoscope.nix ./programs/nexttrace.nix + ./programs/nh.nix ./programs/nix-index.nix ./programs/nix-ld.nix ./programs/nm-applet.nix @@ -264,6 +266,7 @@ ./programs/skim.nix ./programs/slock.nix ./programs/sniffnet.nix + ./programs/soundmodem.nix ./programs/spacefm.nix ./programs/ssh.nix ./programs/starship.nix @@ -285,8 +288,8 @@ ./programs/virt-manager.nix ./programs/wavemon.nix ./programs/wayland/cardboard.nix - ./programs/wayland/labwc.nix ./programs/wayland/hyprland.nix + ./programs/wayland/labwc.nix ./programs/wayland/river.nix ./programs/wayland/sway.nix ./programs/wayland/waybar.nix @@ -689,6 +692,7 @@ ./services/misc/clipmenu.nix ./services/misc/confd.nix ./services/misc/cpuminer-cryptonight.nix + ./services/misc/db-rest.nix ./services/misc/devmon.nix ./services/misc/dictd.nix ./services/misc/disnix.nix @@ -1162,6 +1166,7 @@ ./services/networking/syncthing-relay.nix ./services/networking/syncthing.nix ./services/networking/tailscale.nix + ./services/networking/tailscale-auth.nix ./services/networking/tayga.nix ./services/networking/tcpcrypt.nix ./services/networking/teamspeak3.nix diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix index 4e8c74deedba3..52ba40902e87a 100644 --- a/nixos/modules/profiles/demo.nix +++ b/nixos/modules/profiles/demo.nix @@ -11,7 +11,7 @@ uid = 1000; }; - services.xserver.displayManager = { + services.displayManager = { autoLogin = { enable = true; user = "demo"; diff --git a/nixos/modules/profiles/graphical.nix b/nixos/modules/profiles/graphical.nix index d80456cede565..8cc31652f8075 100644 --- a/nixos/modules/profiles/graphical.nix +++ b/nixos/modules/profiles/graphical.nix @@ -6,13 +6,12 @@ { services.xserver = { enable = true; - displayManager.sddm.enable = true; - desktopManager.plasma5 = { - enable = true; - }; + desktopManager.plasma5.enable = true; libinput.enable = true; # for touchpad support on many laptops }; + services.displayManager.sddm.enable = true; + # Enable sound in virtualbox appliances. hardware.pulseaudio.enable = true; diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix index 87fb29ed5bbe6..21ef8338d8dd8 100644 --- a/nixos/modules/programs/bash/bash.nix +++ b/nixos/modules/programs/bash/bash.nix @@ -30,7 +30,7 @@ in /* enable = mkOption { default = true; - description = lib.mdDoc '' + description = '' Whenever to configure Bash as an interactive shell. Note that this tries to make Bash the default {option}`users.defaultUserShell`, diff --git a/nixos/modules/programs/fcast-receiver.nix b/nixos/modules/programs/fcast-receiver.nix new file mode 100644 index 0000000000000..8da07a66e2223 --- /dev/null +++ b/nixos/modules/programs/fcast-receiver.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.fcast-receiver; +in +{ + meta = { + maintainers = pkgs.fcast-receiver.meta.maintainers; + }; + + options.programs.fcast-receiver = { + enable = mkEnableOption (lib.mdDoc "FCast Receiver"); + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open ports needed for the functionality of the program. + ''; + }; + package = mkPackageOption pkgs "fcast-receiver" { }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 46899 ]; + }; + }; +} diff --git a/nixos/modules/programs/nh.nix b/nixos/modules/programs/nh.nix new file mode 100644 index 0000000000000..c42fb2fc724a7 --- /dev/null +++ b/nixos/modules/programs/nh.nix @@ -0,0 +1,96 @@ +{ config +, lib +, pkgs +, ... +}: +let + cfg = config.programs.nh; +in +{ + meta.maintainers = [ lib.maintainers.viperML ]; + + options.programs.nh = { + enable = lib.mkEnableOption "nh, yet another Nix CLI helper"; + + package = lib.mkPackageOption pkgs "nh" { }; + + flake = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + The path that will be used for the `FLAKE` environment variable. + + `FLAKE` is used by nh as the default flake for performing actions, like `nh os switch`. + ''; + }; + + clean = { + enable = lib.mkEnableOption "periodic garbage collection with nh clean all"; + + dates = lib.mkOption { + type = lib.types.singleLineStr; + default = "weekly"; + description = '' + How often cleanup is performed. Passed to systemd.time + + The format is described in + {manpage}`systemd.time(7)`. + ''; + }; + + extraArgs = lib.mkOption { + type = lib.types.singleLineStr; + default = ""; + example = "--keep 5 --keep-since 3d"; + description = '' + Options given to nh clean when the service is run automatically. + + See `nh clean all --help` for more information. + ''; + }; + }; + }; + + config = { + warnings = + if (!(cfg.clean.enable -> !config.nix.gc.automatic)) then [ + "programs.nh.clean.enable and nix.gc.automatic are both enabled. Please use one or the other to avoid conflict." + ] else [ ]; + + assertions = [ + # Not strictly required but probably a good assertion to have + { + assertion = cfg.clean.enable -> cfg.enable; + message = "programs.nh.clean.enable requires programs.nh.enable"; + } + + { + assertion = (cfg.flake != null) -> !(lib.hasSuffix ".nix" cfg.flake); + message = "nh.flake must be a directory, not a nix file"; + } + ]; + + environment = lib.mkIf cfg.enable { + systemPackages = [ cfg.package ]; + variables = lib.mkIf (cfg.flake != null) { + FLAKE = cfg.flake; + }; + }; + + systemd = lib.mkIf cfg.clean.enable { + services.nh-clean = { + description = "Nh clean"; + script = "exec ${lib.getExe cfg.package} clean all ${cfg.clean.extraArgs}"; + startAt = cfg.clean.dates; + path = [ config.nix.package ]; + serviceConfig.Type = "oneshot"; + }; + + timers.nh-clean = { + timerConfig = { + Persistent = true; + }; + }; + }; + }; +} diff --git a/nixos/modules/programs/slock.nix b/nixos/modules/programs/slock.nix index ce80fcc5d4a8a..f39b4d5e9280e 100644 --- a/nixos/modules/programs/slock.nix +++ b/nixos/modules/programs/slock.nix @@ -16,16 +16,17 @@ in Whether to install slock screen locker with setuid wrapper. ''; }; + package = mkPackageOption pkgs "slock" {}; }; }; config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.slock ]; + environment.systemPackages = [ cfg.package ]; security.wrappers.slock = { setuid = true; owner = "root"; group = "root"; - source = "${pkgs.slock.out}/bin/slock"; + source = lib.getExe cfg.package; }; }; } diff --git a/nixos/modules/programs/soundmodem.nix b/nixos/modules/programs/soundmodem.nix new file mode 100644 index 0000000000000..ab992c63c6088 --- /dev/null +++ b/nixos/modules/programs/soundmodem.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.soundmodem; +in +{ + options = { + programs.soundmodem = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add Soundmodem to the global environment and configure a + wrapper for 'soundmodemconfig' for users in the 'soundmodem' group. + ''; + }; + package = mkPackageOption pkgs "soundmodem" { }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + users.groups.soundmodem = { }; + + security.wrappers.soundmodemconfig = { + source = "${cfg.package}/bin/soundmodemconfig"; + owner = "root"; + group = "soundmodem"; + permissions = "u+rx,g+x"; + }; + }; +} diff --git a/nixos/modules/programs/tsm-client.nix b/nixos/modules/programs/tsm-client.nix index 5f0d78bfa4a51..82fbc9b26e2d3 100644 --- a/nixos/modules/programs/tsm-client.nix +++ b/nixos/modules/programs/tsm-client.nix @@ -22,7 +22,7 @@ let serverOptions = { name, config, ... }: { freeformType = attrsOf (either scalarType (listOf scalarType)); # Client system-options file directives are explained here: - # https://www.ibm.com/docs/en/storage-protect/8.1.21?topic=commands-processing-options + # https://www.ibm.com/docs/en/storage-protect/8.1.22?topic=commands-processing-options options.servername = mkOption { type = servernameType; default = name; diff --git a/nixos/modules/services/audio/gonic.nix b/nixos/modules/services/audio/gonic.nix index 3dfa24b7fc68a..15a35571acbab 100644 --- a/nixos/modules/services/audio/gonic.nix +++ b/nixos/modules/services/audio/gonic.nix @@ -55,6 +55,9 @@ in RuntimeDirectory = "gonic"; RootDirectory = "/run/gonic"; ReadWritePaths = ""; + BindPaths = [ + cfg.settings.playlists-path + ]; BindReadOnlyPaths = [ # gonic can access scrobbling services "-/etc/resolv.conf" diff --git a/nixos/modules/services/backup/tsm.nix b/nixos/modules/services/backup/tsm.nix index 1a2b02a65811d..dc5d8f09e069b 100644 --- a/nixos/modules/services/backup/tsm.nix +++ b/nixos/modules/services/backup/tsm.nix @@ -90,7 +90,7 @@ in environment.HOME = "/var/lib/tsm-backup"; serviceConfig = { # for exit status description see - # https://www.ibm.com/docs/en/storage-protect/8.1.21?topic=clients-client-return-codes + # https://www.ibm.com/docs/en/storage-protect/8.1.22?topic=clients-client-return-codes SuccessExitStatus = "4 8"; # The `-se` option must come after the command. # The `-optfile` option suppresses a `dsm.opt`-not-found warning. diff --git a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix index c3edba52433f6..30be56f8eeabe 100644 --- a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix +++ b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix @@ -203,6 +203,8 @@ in TOKEN = "${instance.token}"; } // optionalAttrs (wantsPodman) { DOCKER_HOST = "unix:///run/podman/podman.sock"; + } // { + HOME = "/var/lib/gitea-runner/${name}"; }; path = with pkgs; [ coreutils diff --git a/nixos/modules/services/desktop-managers/lomiri.nix b/nixos/modules/services/desktop-managers/lomiri.nix new file mode 100644 index 0000000000000..e11867b691071 --- /dev/null +++ b/nixos/modules/services/desktop-managers/lomiri.nix @@ -0,0 +1,171 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.desktopManager.lomiri; +in { + options.services.desktopManager.lomiri = { + enable = lib.mkEnableOption '' + the Lomiri graphical shell (formerly known as Unity8) + ''; + }; + + config = lib.mkIf cfg.enable { + environment = { + systemPackages = (with pkgs; [ + glib # XDG MIME-related tools identify it as GNOME, add gio for MIME identification to work + libayatana-common + ubports-click + ]) ++ (with pkgs.lomiri; [ + content-hub + hfd-service + history-service + libusermetrics + lomiri + lomiri-download-manager + lomiri-schemas # exposes some required dbus interfaces + lomiri-session # wrappers to properly launch the session + lomiri-sounds + lomiri-system-settings + lomiri-terminal-app + lomiri-thumbnailer + lomiri-url-dispatcher + lomiri-wallpapers + mediascanner2 # TODO possibly needs to be kicked off by graphical-session.target + morph-browser + qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session + suru-icon-theme + telephony-service + ]); + }; + + networking.networkmanager.enable = lib.mkDefault true; + + systemd.packages = with pkgs.lomiri; [ + hfd-service + lomiri-download-manager + ]; + + services.dbus.packages = with pkgs.lomiri; [ + hfd-service + libusermetrics + lomiri-download-manager + ]; + + fonts.packages = with pkgs; [ + # Applications tend to default to Ubuntu font + ubuntu_font_family + ]; + + # Copy-pasted basic stuff + hardware.opengl.enable = lib.mkDefault true; + fonts.enableDefaultPackages = lib.mkDefault true; + programs.dconf.enable = lib.mkDefault true; + + # Xwayland is partly hardcoded in Mir so it can't really be fully turned off, and it must be on PATH for X11 apps *and Lomiri's web browser* to work. + # Until Mir/Lomiri can be properly used without it, force it on so everything behaves as expected. + programs.xwayland.enable = lib.mkForce true; + + services.accounts-daemon.enable = true; + + services.ayatana-indicators = { + enable = true; + packages = (with pkgs; [ + ayatana-indicator-datetime + ayatana-indicator-messages + ayatana-indicator-session + ]) ++ (with pkgs.lomiri; [ + telephony-service + ] ++ lib.optionals config.networking.networkmanager.enable [ + lomiri-indicator-network + ]); + }; + + services.udisks2.enable = true; + services.upower.enable = true; + services.geoclue2.enable = true; + + services.gnome.evolution-data-server = { + enable = true; + plugins = with pkgs; [ + # TODO: lomiri.address-book-service + ]; + }; + + services.telepathy.enable = true; + + services.displayManager = { + defaultSession = lib.mkDefault "lomiri"; + sessionPackages = with pkgs.lomiri; [ lomiri-session ]; + }; + + services.xserver = { + enable = lib.mkDefault true; + displayManager.lightdm = { + enable = lib.mkDefault true; + greeters.lomiri.enable = lib.mkDefault true; + }; + }; + + environment.pathsToLink = [ + # Configs for inter-app data exchange system + "/share/content-hub/peers" + # Configs for inter-app URL requests + "/share/lomiri-url-dispatcher/urls" + # Splash screens & other images for desktop apps launched via lomiri-app-launch + "/share/lomiri-app-launch" + # TODO Try to get maliit stuff working + "/share/maliit/plugins" + # At least the network indicator is still under the unity name, due to leftover Unity-isms + "/share/unity" + # Data + "/share/locale" # TODO LUITK hardcoded default locale path, fix individual apps to not rely on it + "/share/sounds" + "/share/wallpapers" + ]; + + systemd.user.services = { + # Unconditionally run service that collects system-installed URL handlers before LUD + # TODO also run user-installed one? + "lomiri-url-dispatcher-update-system-dir" = { + description = "Lomiri URL dispatcher system directory updater"; + wantedBy = [ "lomiri-url-dispatcher.service" ]; + before = [ "lomiri-url-dispatcher.service" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.lomiri.lomiri-url-dispatcher}/libexec/lomiri-url-dispatcher/lomiri-update-directory /run/current-system/sw/share/lomiri-url-dispatcher/urls/"; + }; + }; + }; + + systemd.services = { + "dbus-com.lomiri.UserMetrics" = { + serviceConfig = { + Type = "dbus"; + BusName = "com.lomiri.UserMetrics"; + User = "usermetrics"; + StandardOutput = "syslog"; + SyslogIdentifier = "com.lomiri.UserMetrics"; + ExecStart = "${pkgs.lomiri.libusermetrics}/libexec/libusermetrics/usermetricsservice"; + } // lib.optionalAttrs (!config.security.apparmor.enable) { + # Due to https://gitlab.com/ubports/development/core/libusermetrics/-/issues/8, auth must be disabled when not using AppArmor, lest the next database usage breaks + Environment = "USERMETRICS_NO_AUTH=1"; + }; + }; + }; + + users.users.usermetrics = { + group = "usermetrics"; + home = "/var/lib/usermetrics"; + createHome = true; + isSystemUser = true; + }; + + users.groups.usermetrics = { }; + + # TODO content-hub cannot pass files between applications without asking AA for permissions. And alot of the Lomiri stack is designed with AA availability in mind. This might be a requirement to be closer to upstream? + # But content-hub currently fails to pass files between applications even with AA enabled, and we can get away without AA in many places. Let's see how this develops before requiring this for good. + # security.apparmor.enable = true; + }; + + meta.maintainers = lib.teams.lomiri.members; +} diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 4f8ca85a74329..3db6616442816 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -401,17 +401,19 @@ in })) ]; - environment.etc = - { - "udev/rules.d".source = udevRulesFor { - name = "udev-rules"; - udevPackages = cfg.packages; - systemd = config.systemd.package; - binPackages = cfg.packages; - inherit udevPath udev; - }; - "udev/hwdb.bin".source = hwdbBin; + environment.etc = { + "udev/rules.d".source = udevRulesFor { + name = "udev-rules"; + udevPackages = cfg.packages; + systemd = config.systemd.package; + binPackages = cfg.packages; + inherit udevPath udev; }; + "udev/hwdb.bin".source = hwdbBin; + } // lib.optionalAttrs config.boot.modprobeConfig.enable { + # We don't place this into `extraModprobeConfig` so that stage-1 ramdisk doesn't bloat. + "modprobe.d/firmware.conf".text = "options firmware_class path=${config.hardware.firmware}/lib/firmware"; + }; system.requiredKernelConfig = with config.lib.kernelConfig; [ (isEnabled "UNIX") @@ -419,21 +421,17 @@ in (isYes "NET") ]; - # We don't place this into `extraModprobeConfig` so that stage-1 ramdisk doesn't bloat. - environment.etc."modprobe.d/firmware.conf".text = "options firmware_class path=${config.hardware.firmware}/lib/firmware"; - - system.activationScripts.udevd = - '' - # The deprecated hotplug uevent helper is not used anymore - if [ -e /proc/sys/kernel/hotplug ]; then - echo "" > /proc/sys/kernel/hotplug - fi + system.activationScripts.udevd = lib.mkIf config.boot.kernel.enable '' + # The deprecated hotplug uevent helper is not used anymore + if [ -e /proc/sys/kernel/hotplug ]; then + echo "" > /proc/sys/kernel/hotplug + fi - # Allow the kernel to find our firmware. - if [ -e /sys/module/firmware_class/parameters/path ]; then - echo -n "${config.hardware.firmware}/lib/firmware" > /sys/module/firmware_class/parameters/path - fi - ''; + # Allow the kernel to find our firmware. + if [ -e /sys/module/firmware_class/parameters/path ]; then + echo -n "${config.hardware.firmware}/lib/firmware" > /sys/module/firmware_class/parameters/path + fi + ''; systemd.services.systemd-udevd = { restartTriggers = cfg.packages; diff --git a/nixos/modules/services/misc/db-rest.nix b/nixos/modules/services/misc/db-rest.nix new file mode 100644 index 0000000000000..fbf8b327af049 --- /dev/null +++ b/nixos/modules/services/misc/db-rest.nix @@ -0,0 +1,182 @@ +{ config, pkgs, lib, ... }: +let + inherit (lib) mkOption types mkIf mkMerge mkDefault mkEnableOption mkPackageOption maintainers; + cfg = config.services.db-rest; +in +{ + options = { + services.db-rest = { + enable = mkEnableOption "db-rest service"; + + user = mkOption { + type = types.str; + default = "db-rest"; + description = "User account under which db-rest runs."; + }; + + group = mkOption { + type = types.str; + default = "db-rest"; + description = "Group under which db-rest runs."; + }; + + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "The host address the db-rest server should listen on."; + }; + + port = mkOption { + type = types.port; + default = 3000; + description = "The port the db-rest server should listen on."; + }; + + redis = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable caching with redis for db-rest."; + }; + + createLocally = mkOption { + type = types.bool; + default = true; + description = "Configure a local redis server for db-rest."; + }; + + host = mkOption { + type = with types; nullOr str; + default = null; + description = "Redis host."; + }; + + port = mkOption { + type = with types; nullOr port; + default = null; + description = "Redis port."; + }; + + user = mkOption { + type = with types; nullOr str; + default = null; + description = "Optional username used for authentication with redis."; + }; + + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/keys/db-rest/pasword-redis-db"; + description = "Path to a file containing the redis password."; + }; + + useSSL = mkOption { + type = types.bool; + default = true; + description = "Use SSL if using a redis network connection."; + }; + }; + + package = mkPackageOption pkgs "db-rest" { }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.host != null && cfg.redis.port != null); + message = '' + {option}`services.db-rest.redis.createLocally` and redis network connection ({option}`services.db-rest.redis.host` or {option}`services.db-rest.redis.port`) enabled. Disable either of them. + ''; + } + { + assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.passwordFile != null); + message = '' + {option}`services.db-rest.redis.createLocally` is disabled, but {option}`services.db-rest.redis.passwordFile` is not set. + ''; + } + ]; + + systemd.services.db-rest = mkMerge [ + { + description = "db-rest service"; + after = [ "network.target" ] + ++ lib.optional cfg.redis.createLocally "redis-db-rest.service"; + requires = lib.optional cfg.redis.createLocally "redis-db-rest.service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = 5; + WorkingDirectory = cfg.package; + User = cfg.user; + Group = cfg.group; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + MemoryDenyWriteExecute = false; + LoadCredential = lib.optional (cfg.redis.enable && cfg.redis.passwordFile != null) "REDIS_PASSWORD:${cfg.redis.passwordFile}"; + ExecStart = mkDefault "${cfg.package}/bin/db-rest"; + + RemoveIPC = true; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectClock = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + PrivateMounts = true; + SystemCallArchitectures = "native"; + ProtectHostname = true; + LockPersonality = true; + ProtectKernelTunables = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RestrictNamespaces = true; + ProtectSystem = "strict"; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectHome = true; + PrivateUsers = true; + PrivateTmp = true; + CapabilityBoundingSet = ""; + }; + environment = { + NODE_ENV = "production"; + NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt"; + HOSTNAME = cfg.host; + PORT = toString cfg.port; + }; + } + (mkIf cfg.redis.enable (if cfg.redis.createLocally then + { environment.REDIS_URL = config.services.redis.servers.db-rest.unixSocket; } + else + { + script = + let + username = lib.optionalString (cfg.redis.user != null) (cfg.redis.user); + host = cfg.redis.host; + port = toString cfg.redis.port; + protocol = if cfg.redis.useSSL then "rediss" else "redis"; + in + '' + export REDIS_URL="${protocol}://${username}:$(${config.systemd.package}/bin/systemd-creds cat REDIS_PASSWORD)@${host}:${port}" + exec ${cfg.package}/bin/db-rest + ''; + })) + ]; + + users.users = lib.mkMerge [ + (lib.mkIf (cfg.user == "db-rest") { + db-rest = { + isSystemUser = true; + group = cfg.group; + }; + }) + (lib.mkIf cfg.redis.createLocally { ${cfg.user}.extraGroups = [ "redis-db-rest" ]; }) + ]; + + users.groups = lib.mkIf (cfg.group == "db-rest") { db-rest = { }; }; + + services.redis.servers.db-rest.enable = cfg.redis.enable && cfg.redis.createLocally; + }; + meta.maintainers = with maintainers; [ marie ]; +} diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix index a2764abd993e6..93bf71ea3ecc1 100644 --- a/nixos/modules/services/misc/docker-registry.nix +++ b/nixos/modules/services/misc/docker-registry.nix @@ -41,8 +41,7 @@ let }; }; - configFile = pkgs.writeText "docker-registry-config.yml" (builtins.toJSON (recursiveUpdate registryConfig cfg.extraConfig)); - + configFile = cfg.configFile; in { options.services.dockerRegistry = { enable = mkEnableOption "Docker Registry"; @@ -106,6 +105,17 @@ in { type = types.attrs; }; + configFile = lib.mkOption { + default = pkgs.writeText "docker-registry-config.yml" (builtins.toJSON (recursiveUpdate registryConfig cfg.extraConfig)); + defaultText = literalExpression ''pkgs.writeText "docker-registry-config.yml" "# my custom docker-registry-config.yml ..."''; + description = '' + Path to CNCF distribution config file. + + Setting this option will override any configuration applied by the extraConfig option. + ''; + type = types.path; + }; + enableGarbageCollect = mkEnableOption "garbage collect"; garbageCollectDates = mkOption { diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix index b2c3de09015b9..948c8f17f9894 100644 --- a/nixos/modules/services/misc/ollama.nix +++ b/nixos/modules/services/misc/ollama.nix @@ -85,7 +85,7 @@ in }; serviceConfig = { ExecStart = "${lib.getExe ollamaPackage} serve"; - WorkingDirectory = "%S/ollama"; + WorkingDirectory = cfg.home; StateDirectory = [ "ollama" ]; DynamicUser = true; }; diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index e691cd64497f0..9d453c5394824 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -682,7 +682,7 @@ in # TODO Add "instrument_queries" option when upgrading to grafana 10.0 # instrument_queries = mkOption { - # description = lib.mdDoc "Set to `true` to add metrics and tracing for database queries."; + # description = "Set to `true` to add metrics and tracing for database queries."; # default = false; # type = types.bool; # }; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index a9eab3e7055cc..2dc12a221bf06 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -31,6 +31,7 @@ let "collectd" "dmarc" "dnsmasq" + "dnssec" "domain" "dovecot" "fastly" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dnssec.nix b/nixos/modules/services/monitoring/prometheus/exporters/dnssec.nix new file mode 100644 index 0000000000000..dda1ad1988a61 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/dnssec.nix @@ -0,0 +1,90 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.prometheus.exporters.dnssec; + configFormat = pkgs.formats.toml { }; + configFile = configFormat.generate "dnssec-checks.toml" cfg.configuration; +in { + port = 9204; + extraOpts = { + configuration = lib.mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; + description = '' + dnssec exporter configuration as nix attribute set. + + See <https://github.com/chrj/prometheus-dnssec-exporter/blob/master/README.md> + for the description of the configuration file format. + ''; + example = lib.literalExpression '' + { + records = [ + { + zone = "ietf.org"; + record = "@"; + type = "SOA"; + } + { + zone = "verisigninc.com"; + record = "@"; + type = "SOA"; + } + ]; + } + ''; + }; + + listenAddress = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Listen address as host IP and port definition. + ''; + example = ":9204"; + }; + + resolvers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + DNSSEC capable resolver to be used for the check. + ''; + example = [ "0.0.0.0:53" ]; + }; + + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + DNS request timeout duration. + ''; + example = "10s"; + }; + + extraFlags = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Extra commandline options when launching Prometheus. + ''; + }; + }; + + serviceOpts = { + serviceConfig = let + startScript = pkgs.writeShellScriptBin "prometheus-dnssec-exporter-start" + "${lib.concatStringsSep " " + ([ "${pkgs.prometheus-dnssec-exporter}/bin/prometheus-dnssec-exporter" ] + ++ lib.optionals (cfg.configuration != null) + [ "-config ${configFile}" ] + ++ lib.optionals (cfg.listenAddress != null) + [ "-listen-address ${lib.escapeShellArg cfg.listenAddress}" ] + ++ lib.optionals (cfg.resolvers != [ ]) [ + "-resolvers ${ + lib.escapeShellArg (lib.concatStringsSep "," cfg.resolvers) + }" + ] ++ lib.optionals (cfg.timeout != null) + [ "-timeout ${lib.escapeShellArg cfg.timeout}" ] ++ cfg.extraFlags)}"; + in { ExecStart = lib.getExe startScript; }; + }; +} + diff --git a/nixos/modules/services/monitoring/prometheus/exporters/redis.nix b/nixos/modules/services/monitoring/prometheus/exporters/redis.nix index 71f94a700efd9..ee7d87e8e6150 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/redis.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/redis.nix @@ -9,6 +9,7 @@ in port = 9121; serviceOpts = { serviceConfig = { + RestrictAddressFamilies = [ "AF_UNIX" ]; ExecStart = '' ${pkgs.prometheus-redis-exporter}/bin/redis_exporter \ -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ diff --git a/nixos/modules/services/networking/deconz.nix b/nixos/modules/services/networking/deconz.nix index 05b7247087771..eaa7759d0407c 100644 --- a/nixos/modules/services/networking/deconz.nix +++ b/nixos/modules/services/networking/deconz.nix @@ -93,6 +93,13 @@ in # be garbage collected. Ensure the file gets "refreshed" on every start. rm -f ${stateDir}/.local/share/dresden-elektronik/deCONZ/zcldb.txt ''; + postStart = '' + # Delay signalling service readiness until it's actually up. + while ! "${lib.getExe pkgs.curl}" -sSfl -o /dev/null "http://${cfg.listenAddress}:${toString cfg.httpPort}"; do + echo "Waiting for TCP port ${toString cfg.httpPort} to be open..." + sleep 1 + done + ''; environment = { HOME = stateDir; XDG_RUNTIME_DIR = "/run/${name}"; diff --git a/nixos/modules/services/networking/dnscrypt-proxy2.nix b/nixos/modules/services/networking/dnscrypt-proxy2.nix index 6272045efe281..980eda117b1eb 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy2.nix +++ b/nixos/modules/services/networking/dnscrypt-proxy2.nix @@ -49,12 +49,12 @@ in passAsFile = [ "json" ]; } '' ${if cfg.upstreamDefaults then '' - ${pkgs.remarshal}/bin/toml2json ${pkgs.dnscrypt-proxy.src}/dnscrypt-proxy/example-dnscrypt-proxy.toml > example.json - ${pkgs.jq}/bin/jq --slurp add example.json $jsonPath > config.json # merges the two + ${pkgs.buildPackages.remarshal}/bin/toml2json ${pkgs.dnscrypt-proxy.src}/dnscrypt-proxy/example-dnscrypt-proxy.toml > example.json + ${pkgs.buildPackages.jq}/bin/jq --slurp add example.json $jsonPath > config.json # merges the two '' else '' cp $jsonPath config.json ''} - ${pkgs.remarshal}/bin/json2toml < config.json > $out + ${pkgs.buildPackages.remarshal}/bin/json2toml < config.json > $out ''; defaultText = literalMD "TOML file generated from {option}`services.dnscrypt-proxy2.settings`"; }; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index a021798e0e4b4..e33bbb2af178f 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -10,49 +10,31 @@ let enableIwd = cfg.wifi.backend == "iwd"; - mkValue = v: - if v == true then "yes" - else if v == false then "no" - else if lib.isInt v then toString v - else v; - - mkSection = name: attrs: '' - [${name}] - ${ - lib.concatStringsSep "\n" - (lib.mapAttrsToList - (k: v: "${k}=${mkValue v}") - (lib.filterAttrs - (k: v: v != null) - attrs)) - } - ''; - - configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [ - (mkSection "main" { + configAttrs = lib.recursiveUpdate { + main = { plugins = "keyfile"; inherit (cfg) dhcp dns; # If resolvconf is disabled that means that resolv.conf is managed by some other module. rc-manager = if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"; - }) - (mkSection "keyfile" { + }; + keyfile = { unmanaged-devices = - if cfg.unmanaged == [ ] then null - else lib.concatStringsSep ";" cfg.unmanaged; - }) - (mkSection "logging" { + if cfg.unmanaged == [ ] then null + else lib.concatStringsSep ";" cfg.unmanaged; + }; + logging = { audit = config.security.audit.enable; level = cfg.logLevel; - }) - (mkSection "connection" cfg.connectionConfig) - (mkSection "device" { - "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress; - "wifi.backend" = cfg.wifi.backend; - }) - cfg.extraConfig - ]); + }; + connection = cfg.connectionConfig; + device = { + "wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress; + "wifi.backend" = cfg.wifi.backend; + }; + } cfg.settings; + configFile = ini.generate "NetworkManager.conf" configAttrs; /* [network-manager] @@ -145,7 +127,7 @@ in { meta = { - maintainers = teams.freedesktop.members; + maintainers = teams.freedesktop.members ++ [ lib.maintainers.janik ]; }; ###### interface @@ -185,11 +167,11 @@ in ''; }; - extraConfig = mkOption { - type = types.lines; - default = ""; + settings = mkOption { + type = ini.type; + default = {}; description = '' - Configuration appended to the generated NetworkManager.conf. + Configuration added to the generated NetworkManager.conf, note that you can overwrite settings with this. Refer to [ https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html @@ -471,8 +453,28 @@ in imports = [ (mkRenamedOptionModule [ "networking" "networkmanager" "packages" ] - [ "networking" "networkmanager" "plugins" ]) - (mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ]) + [ "networking" "networkmanager" "plugins" ] + ) + (mkRenamedOptionModule + [ "networking" "networkmanager" "useDnsmasq" ] + [ "networking" "networkmanager" "dns" ] + ) + (mkRemovedOptionModule [ "networking" "networkmanager" "extraConfig" ] '' + This option was removed in favour of `networking.networkmanager.settings`, + which accepts structured nix-code equivalent to the ini + and allows for overriding settings. + Example patch: + ```patch + networking.networkmanager = { + - extraConfig = ''' + - [main] + - no-auto-default=* + - ''' + + extraConfig.main.no-auto-default = "*"; + }; + ``` + '' + ) (mkRemovedOptionModule [ "networking" "networkmanager" "enableFccUnlock" ] '' This option was removed, because using bundled FCC unlock scripts is risky, might conflict with vendor-provided unlock scripts, and should diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix index 23bb92f09ab9a..b17416c1e3d34 100644 --- a/nixos/modules/services/networking/nsd.nix +++ b/nixos/modules/services/networking/nsd.nix @@ -152,9 +152,7 @@ let copyKeys = concatStrings (mapAttrsToList (keyName: keyOptions: '' secret=$(cat "${keyOptions.keyFile}") dest="${stateDir}/private/${keyName}" - echo " secret: \"$secret\"" > "$dest" - chown ${username}:${username} "$dest" - chmod 0400 "$dest" + install -m 0400 -o "${username}" -g "${username}" <(echo " secret: \"$secret\"") "$dest" '') cfg.keys); @@ -457,9 +455,7 @@ let dnssecTools = pkgs.bind.override { enablePython = true; }; signZones = optionalString dnssec '' - mkdir -p ${stateDir}/dnssec - chown ${username}:${username} ${stateDir}/dnssec - chmod 0600 ${stateDir}/dnssec + install -m 0600 -o "${username}" -g "${username}" -d "${stateDir}/dnssec" ${concatStrings (mapAttrsToList signZone dnssecZones)} ''; @@ -961,9 +957,9 @@ in rm -Rf "${stateDir}/private/" rm -Rf "${stateDir}/tmp/" - mkdir -m 0700 -p "${stateDir}/private" - mkdir -m 0700 -p "${stateDir}/tmp" - mkdir -m 0700 -p "${stateDir}/var" + install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/private" + install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/tmp" + install -dm 0700 -o "${username}" -g "${username}" "${stateDir}/var" cat > "${stateDir}/don't touch anything in here" << EOF Everything in this directory except NSD's state in var and dnssec @@ -971,10 +967,6 @@ in the nsd.service pre-start script. EOF - chown ${username}:${username} -R "${stateDir}/private" - chown ${username}:${username} -R "${stateDir}/tmp" - chown ${username}:${username} -R "${stateDir}/var" - rm -rf "${stateDir}/zones" cp -rL "${nsdEnv}/zones" "${stateDir}/zones" diff --git a/nixos/modules/services/networking/shadowsocks.nix b/nixos/modules/services/networking/shadowsocks.nix index 84d7ece075fef..2f6f40f2b0f60 100644 --- a/nixos/modules/services/networking/shadowsocks.nix +++ b/nixos/modules/services/networking/shadowsocks.nix @@ -136,10 +136,16 @@ in ###### implementation config = mkIf cfg.enable { - assertions = singleton - { assertion = cfg.password == null || cfg.passwordFile == null; - message = "Cannot use both password and passwordFile for shadowsocks-libev"; - }; + assertions = [ + { + # xor, make sure either password or passwordFile be set. + # shadowsocks-libev not support plain/none encryption method + # which indicated that password must set. + assertion = let noPasswd = cfg.password == null; noPasswdFile = cfg.passwordFile == null; + in (noPasswd && !noPasswdFile) || (!noPasswd && noPasswdFile); + message = "Option `password` or `passwordFile` must be set and cannot be set simultaneously"; + } + ]; systemd.services.shadowsocks-libev = { description = "shadowsocks-libev Daemon"; diff --git a/nixos/modules/services/networking/tailscale-auth.nix b/nixos/modules/services/networking/tailscale-auth.nix new file mode 100644 index 0000000000000..c3a515212e782 --- /dev/null +++ b/nixos/modules/services/networking/tailscale-auth.nix @@ -0,0 +1,104 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) + getExe + maintainers + mkEnableOption + mkPackageOption + mkIf + mkOption + types + ; + cfg = config.services.tailscaleAuth; +in +{ + options.services.tailscaleAuth = { + enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate users via tailscale."; + + package = mkPackageOption pkgs "tailscale-nginx-auth" {}; + + user = mkOption { + type = types.str; + default = "tailscale-nginx-auth"; + description = "User which runs tailscale-nginx-auth"; + }; + + group = mkOption { + type = types.str; + default = "tailscale-nginx-auth"; + description = "Group which runs tailscale-nginx-auth"; + }; + + socketPath = mkOption { + default = "/run/tailscale-nginx-auth/tailscale-nginx-auth.sock"; + type = types.path; + description = '' + Path of the socket listening to authorization requests. + ''; + }; + }; + + config = mkIf cfg.enable { + services.tailscale.enable = true; + + users.users.${cfg.user} = { + isSystemUser = true; + inherit (cfg) group; + }; + users.groups.${cfg.group} = { }; + + systemd.sockets.tailscale-nginx-auth = { + description = "Tailscale NGINX Authentication socket"; + partOf = [ "tailscale-nginx-auth.service" ]; + wantedBy = [ "sockets.target" ]; + listenStreams = [ cfg.socketPath ]; + socketConfig = { + SocketMode = "0660"; + SocketUser = cfg.user; + SocketGroup = cfg.group; + }; + }; + + systemd.services.tailscale-nginx-auth = { + description = "Tailscale NGINX Authentication service"; + requires = [ "tailscale-nginx-auth.socket" ]; + + serviceConfig = { + ExecStart = getExe cfg.package; + RuntimeDirectory = "tailscale-nginx-auth"; + User = cfg.user; + Group = cfg.group; + + BindPaths = [ "/run/tailscale/tailscaled.sock" ]; + + CapabilityBoundingSet = ""; + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictNamespaces = true; + RestrictAddressFamilies = [ "AF_UNIX" ]; + RestrictRealtime = true; + RestrictSUIDSGID = true; + + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid" + ]; + }; + }; + }; + + meta.maintainers = with maintainers; [ dan-theriault phaer ]; +} diff --git a/nixos/modules/services/security/oauth2_proxy_nginx.nix b/nixos/modules/services/security/oauth2_proxy_nginx.nix index 1178b70e43438..87ea61276837c 100644 --- a/nixos/modules/services/security/oauth2_proxy_nginx.nix +++ b/nixos/modules/services/security/oauth2_proxy_nginx.nix @@ -28,7 +28,8 @@ in type = types.listOf types.str; default = []; description = '' - A list of nginx virtual hosts to put behind the oauth2 proxy + A list of nginx virtual hosts to put behind the oauth2 proxy. + You can exclude specific locations by setting `auth_request off;` in the locations extraConfig setting. ''; }; }; @@ -50,18 +51,27 @@ in ] ++ optional (cfg.virtualHosts != []) { recommendedProxySettings = true; # needed because duplicate headers } ++ (map (vhost: { - virtualHosts.${vhost}.locations = { - "/oauth2/auth" = { - proxyPass = cfg.proxy; - extraConfig = '' - proxy_set_header X-Scheme $scheme; - # nginx auth_request includes headers but not body - proxy_set_header Content-Length ""; - proxy_pass_request_body off; - ''; + virtualHosts.${vhost} = { + locations = { + "/oauth2/auth" = { + proxyPass = cfg.proxy; + extraConfig = '' + auth_request off; + proxy_set_header X-Scheme $scheme; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + ''; + }; + "@redirectToAuth2ProxyLogin" = { + return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri"; + extraConfig = '' + auth_request off; + ''; + }; }; - "@redirectToAuth2ProxyLogin".return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri"; - "/".extraConfig = '' + + extraConfig = '' auth_request /oauth2/auth; error_page 401 = @redirectToAuth2ProxyLogin; diff --git a/nixos/modules/services/web-apps/akkoma.nix b/nixos/modules/services/web-apps/akkoma.nix index 3eebf340f9b20..eca498549df36 100644 --- a/nixos/modules/services/web-apps/akkoma.nix +++ b/nixos/modules/services/web-apps/akkoma.nix @@ -772,6 +772,11 @@ in { default = if lib.versionOlder config.system.stateVersion "24.05" then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}/media/" else null; + defaultText = literalExpression '' + if lib.versionOlder config.system.stateVersion "24.05" + then "$\{httpConf.scheme}://$\{httpConf.host}:$\{builtins.toString httpConf.port}/media/" + else null; + ''; description = '' Base path which uploads will be stored at. Whilst this can just be set to a subdirectory of the main domain, it is now recommended to use a different subdomain. @@ -804,6 +809,7 @@ in { enabled = mkOption { type = types.bool; default = false; + defaultText = literalExpression "false"; description = '' Whether to enable proxying of remote media through the instance's proxy. ''; @@ -813,6 +819,11 @@ in { default = if lib.versionOlder config.system.stateVersion "24.05" then "${httpConf.scheme}://${httpConf.host}:${builtins.toString httpConf.port}/media/" else null; + defaultText = literalExpression '' + if lib.versionOlder config.system.stateVersion "24.05" + then "$\{httpConf.scheme}://$\{httpConf.host}:$\{builtins.toString httpConf.port}/media/" + else null; + ''; description = '' Base path for the media proxy. Whilst this can just be set to a subdirectory of the main domain, it is now recommended to use a different subdomain. diff --git a/nixos/modules/services/web-apps/coder.nix b/nixos/modules/services/web-apps/coder.nix index 64805fd75248b..318a7c8fc1357 100644 --- a/nixos/modules/services/web-apps/coder.nix +++ b/nixos/modules/services/web-apps/coder.nix @@ -72,6 +72,23 @@ in { example = "*.coder.example.com"; }; + environment = { + extra = mkOption { + type = types.attrs; + description = "Extra environment variables to pass run Coder's server with. See Coder documentation."; + default = {}; + example = { + CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS = true; + CODER_OAUTH2_GITHUB_ALLOWED_ORGS = "your-org"; + }; + }; + file = mkOption { + type = types.nullOr types.path; + description = "Systemd environment file to add to Coder."; + default = null; + }; + }; + database = { createLocally = mkOption { type = types.bool; @@ -152,7 +169,7 @@ in { after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - environment = { + environment = config.environment.extra // { CODER_ACCESS_URL = cfg.accessUrl; CODER_WILDCARD_ACCESS_URL = cfg.wildcardAccessUrl; CODER_PG_CONNECTION_URL = "user=${cfg.database.username} ${optionalString (cfg.database.password != null) "password=${cfg.database.password}"} database=${cfg.database.database} host=${cfg.database.host} ${optionalString (cfg.database.sslmode != null) "sslmode=${cfg.database.sslmode}"}"; @@ -177,6 +194,7 @@ in { ExecStart = "${cfg.package}/bin/coder server"; User = cfg.user; Group = cfg.group; + EnvironmentFile = lib.mkIf (cfg.environment.file != null) cfg.environment.file; }; }; diff --git a/nixos/modules/services/web-apps/freshrss.nix b/nixos/modules/services/web-apps/freshrss.nix index 016c4202e4b3b..77c5ecb246171 100644 --- a/nixos/modules/services/web-apps/freshrss.nix +++ b/nixos/modules/services/web-apps/freshrss.nix @@ -268,11 +268,11 @@ in script = let - userScriptArgs = ''--user ${cfg.defaultUser} --password "$(cat ${cfg.passwordFile})"''; - updateUserScript = optionalString (cfg.authType == "form") '' + userScriptArgs = ''--user ${cfg.defaultUser} ${optionalString (cfg.authType == "form") ''--password "$(cat ${cfg.passwordFile})"''}''; + updateUserScript = optionalString (cfg.authType == "form" || cfg.authType == "none") '' ./cli/update-user.php ${userScriptArgs} ''; - createUserScript = optionalString (cfg.authType == "form") '' + createUserScript = optionalString (cfg.authType == "form" || cfg.authType == "none") '' ./cli/create-user.php ${userScriptArgs} ''; in diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix index a9a0aecb946e6..7246fd93a2314 100644 --- a/nixos/modules/services/web-apps/mediawiki.nix +++ b/nixos/modules/services/web-apps/mediawiki.nix @@ -18,6 +18,9 @@ let cacheDir = "/var/cache/mediawiki"; stateDir = "/var/lib/mediawiki"; + # https://www.mediawiki.org/wiki/Compatibility + php = pkgs.php81; + pkg = pkgs.stdenv.mkDerivation rec { pname = "mediawiki-full"; inherit (src) version; @@ -46,7 +49,7 @@ let } '' mkdir -p $out/bin for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do - makeWrapper ${pkgs.php}/bin/php $out/bin/mediawiki-$(basename $i .php) \ + makeWrapper ${php}/bin/php $out/bin/mediawiki-$(basename $i .php) \ --set MEDIAWIKI_CONFIG ${mediawikiConfig} \ --add-flags ${pkg}/share/mediawiki/maintenance/$i done @@ -485,8 +488,7 @@ in services.phpfpm.pools.mediawiki = { inherit user group; phpEnv.MEDIAWIKI_CONFIG = "${mediawikiConfig}"; - # https://www.mediawiki.org/wiki/Compatibility - phpPackage = pkgs.php81; + phpPackage = php; settings = (if (cfg.webserver == "apache") then { "listen.owner" = config.services.httpd.user; "listen.group" = config.services.httpd.group; @@ -598,8 +600,8 @@ in fi echo "exit( wfGetDB( DB_MASTER )->tableExists( 'user' ) ? 1 : 0 );" | \ - ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \ - ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \ + ${php}/bin/php ${pkg}/share/mediawiki/maintenance/eval.php --conf ${mediawikiConfig} && \ + ${php}/bin/php ${pkg}/share/mediawiki/maintenance/install.php \ --confpath /tmp \ --scriptpath / \ --dbserver ${lib.escapeShellArg dbAddr} \ @@ -613,7 +615,7 @@ in ${lib.escapeShellArg cfg.name} \ admin - ${pkgs.php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick + ${php}/bin/php ${pkg}/share/mediawiki/maintenance/update.php --conf ${mediawikiConfig} --quick ''; serviceConfig = { diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 3d1a00ccde7c9..40470f535bf61 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -164,7 +164,7 @@ let ${commonHttpConfig} ${optionalString (cfg.resolver.addresses != []) '' - resolver ${toString cfg.resolver.addresses} ${optionalString (cfg.resolver.valid != "") "valid=${cfg.resolver.valid}"} ${optionalString (!cfg.resolver.ipv6) "ipv6=off"}; + resolver ${toString cfg.resolver.addresses} ${optionalString (cfg.resolver.valid != "") "valid=${cfg.resolver.valid}"} ${optionalString (!cfg.resolver.ipv4) "ipv4=off"} ${optionalString (!cfg.resolver.ipv6) "ipv6=off"}; ''} ${upstreamConfig} @@ -978,6 +978,15 @@ in An optional valid parameter allows overriding it ''; }; + ipv4 = mkOption { + type = types.bool; + default = true; + description = '' + By default, nginx will look up both IPv4 and IPv6 addresses while resolving. + If looking up of IPv4 addresses is not desired, the ipv4=off parameter can be + specified. + ''; + }; ipv6 = mkOption { type = types.bool; default = true; @@ -1179,6 +1188,13 @@ in to answer to ACME requests. ''; } + + { + assertion = cfg.resolver.ipv4 || cfg.resolver.ipv6; + message = '' + At least one of services.nginx.resolver.ipv4 and services.nginx.resolver.ipv6 must be true. + ''; + } ] ++ map (name: mkCertOwnershipAssertion { inherit (cfg) group user; cert = config.security.acme.certs.${name}; diff --git a/nixos/modules/services/web-servers/nginx/tailscale-auth.nix b/nixos/modules/services/web-servers/nginx/tailscale-auth.nix index 79d02c40de0c3..ca272268f5724 100644 --- a/nixos/modules/services/web-servers/nginx/tailscale-auth.nix +++ b/nixos/modules/services/web-servers/nginx/tailscale-auth.nix @@ -1,28 +1,29 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit (lib) + genAttrs + maintainers + mkAliasOptionModule + mkEnableOption + mkIf + mkOption + types + ; cfg = config.services.nginx.tailscaleAuth; + cfgAuth = config.services.tailscaleAuth; in { + imports = [ + (mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "package" ] [ "services" "tailscaleAuth" "package" ]) + (mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "user" ] [ "services" "tailscaleAuth" "user" ]) + (mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "group" ] [ "services" "tailscaleAuth" "group" ]) + (mkAliasOptionModule [ "services" "nginx" "tailscaleAuth" "socketPath" ] [ "services" "tailscaleAuth" "socketPath" ]) + ]; + options.services.nginx.tailscaleAuth = { enable = mkEnableOption "Enable tailscale.nginx-auth, to authenticate nginx users via tailscale."; - package = lib.mkPackageOptionMD pkgs "tailscale-nginx-auth" {}; - - user = mkOption { - type = types.str; - default = "tailscale-nginx-auth"; - description = "User which runs tailscale-nginx-auth"; - }; - - group = mkOption { - type = types.str; - default = "tailscale-nginx-auth"; - description = "Group which runs tailscale-nginx-auth"; - }; - expectedTailnet = mkOption { default = ""; type = types.nullOr types.str; @@ -33,14 +34,6 @@ in ''; }; - socketPath = mkOption { - default = "/run/tailscale-nginx-auth/tailscale-nginx-auth.sock"; - type = types.path; - description = '' - Path of the socket listening to nginx authorization requests. - ''; - }; - virtualHosts = mkOption { type = types.listOf types.str; default = []; @@ -51,67 +44,14 @@ in }; config = mkIf cfg.enable { - services.tailscale.enable = true; + services.tailscaleAuth.enable = true; services.nginx.enable = true; - users.users.${cfg.user} = { - isSystemUser = true; - inherit (cfg) group; - }; - users.groups.${cfg.group} = { }; - users.users.${config.services.nginx.user}.extraGroups = [ cfg.group ]; - systemd.sockets.tailscale-nginx-auth = { - description = "Tailscale NGINX Authentication socket"; - partOf = [ "tailscale-nginx-auth.service" ]; - wantedBy = [ "sockets.target" ]; - listenStreams = [ cfg.socketPath ]; - socketConfig = { - SocketMode = "0660"; - SocketUser = cfg.user; - SocketGroup = cfg.group; - }; - }; - + users.users.${config.services.nginx.user}.extraGroups = [ cfgAuth.group ]; systemd.services.tailscale-nginx-auth = { - description = "Tailscale NGINX Authentication service"; after = [ "nginx.service" ]; wants = [ "nginx.service" ]; - requires = [ "tailscale-nginx-auth.socket" ]; - - serviceConfig = { - ExecStart = "${lib.getExe cfg.package}"; - RuntimeDirectory = "tailscale-nginx-auth"; - User = cfg.user; - Group = cfg.group; - - BindPaths = [ "/run/tailscale/tailscaled.sock" ]; - - CapabilityBoundingSet = ""; - DeviceAllow = ""; - LockPersonality = true; - MemoryDenyWriteExecute = true; - PrivateDevices = true; - PrivateUsers = true; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - RestrictNamespaces = true; - RestrictAddressFamilies = [ "AF_UNIX" ]; - RestrictRealtime = true; - RestrictSUIDSGID = true; - - SystemCallArchitectures = "native"; - SystemCallErrorNumber = "EPERM"; - SystemCallFilter = [ - "@system-service" - "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid" - ]; - }; }; services.nginx.virtualHosts = genAttrs @@ -121,7 +61,7 @@ in extraConfig = '' internal; - proxy_pass http://unix:${cfg.socketPath}; + proxy_pass http://unix:${cfgAuth.socketPath}; proxy_pass_request_body off; # Upstream uses $http_host here, but we are using gixy to check nginx configurations diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 621a44ad123cc..6fe606f92267b 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -21,7 +21,7 @@ in ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ../../desktop-managers/plasma6.nix ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome.nix ./retroarch.nix ./kodi.nix ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix - ./cinnamon.nix ./budgie.nix ./deepin.nix + ./cinnamon.nix ./budgie.nix ./deepin.nix ../../desktop-managers/lomiri.nix ]; options = { diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix new file mode 100644 index 0000000000000..0cc79178358b2 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: + +let + + dmcfg = config.services.displayManager; + ldmcfg = config.services.xserver.displayManager.lightdm; + cfg = ldmcfg.greeters.lomiri; + +in +{ + meta.maintainers = lib.teams.lomiri.members; + + options = { + services.xserver.displayManager.lightdm.greeters.lomiri = { + enable = lib.mkEnableOption "lomiri's greeter as the lightdm greeter"; + }; + }; + + config = lib.mkIf (ldmcfg.enable && cfg.enable) { + services.xserver.displayManager.lightdm.greeters.gtk.enable = false; + + services.xserver.displayManager.lightdm.greeter = lib.mkDefault { + package = pkgs.lomiri.lomiri.greeter; + name = "lomiri-greeter"; + }; + + # Greeter needs to be run through its wrapper + # Greeter doesn't work with our set-session.py script, need to set default user-session + services.xserver.displayManager.lightdm.extraSeatDefaults = '' + greeter-wrapper = ${lib.getExe' pkgs.lomiri.lomiri "lomiri-greeter-wrapper"} + user-session = ${dmcfg.defaultSession} + ''; + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 63561934be082..25e6c597adcbd 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -81,6 +81,7 @@ in ./lightdm-greeters/mini.nix ./lightdm-greeters/enso-os.nix ./lightdm-greeters/pantheon.nix + ./lightdm-greeters/lomiri.nix ./lightdm-greeters/tiny.nix ./lightdm-greeters/slick.nix ./lightdm-greeters/mobile.nix diff --git a/nixos/modules/services/x11/urserver.nix b/nixos/modules/services/x11/urserver.nix index 0beb62eb766a3..30f8a9805cfbc 100644 --- a/nixos/modules/services/x11/urserver.nix +++ b/nixos/modules/services/x11/urserver.nix @@ -14,7 +14,7 @@ in { allowedUDPPorts = [ 9511 9512 ]; }; - systemd.user.services.urserver = { + systemd.user.services.urserver = { description = '' Server for Unified Remote: The one-and-only remote for your computer. ''; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 0ddb7dcb651de..bb899c8d89994 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -186,6 +186,37 @@ let (assertNetdevMacAddress "MACAddress") ]; + sectionBridge = checkUnitConfig "Bridge" [ + (assertOnlyFields [ + "HelloTimeSec" + "MaxAgeSec" + "ForwardDelaySec" + "AgeingTimeSec" + "Priority" + "GroupForwardMask" + "DefaultPVID" + "MulticastQuerier" + "MulticastSnooping" + "VLANFiltering" + "VLANProtocol" + "STP" + "MulticastIGMPVersion" + ]) + (assertInt "HelloTimeSec") + (assertInt "MaxAgeSec") + (assertInt "ForwardDelaySec") + (assertInt "AgeingTimeSec") + (assertRange "Priority" 0 65535) + (assertRange "GroupForwardMask" 0 65535) + (assertRangeOrOneOf "DefaultPVID" 0 4094 ["none"]) + (assertValueOneOf "MulticastQuerier" boolValues) + (assertValueOneOf "MulticastSnooping" boolValues) + (assertValueOneOf "VLANFiltering" boolValues) + (assertValueOneOf "VLANProtocol" ["802.1q" "802.ad"]) + (assertValueOneOf "STP" boolValues) + (assertValueOneOf "MulticastIGMPVersion" [2 3]) + ]; + sectionVLAN = checkUnitConfig "VLAN" [ (assertOnlyFields [ "Id" @@ -1635,6 +1666,17 @@ let ''; }; + bridgeConfig = mkOption { + default = {}; + example = { STP = true; }; + type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionBridge; + description = '' + Each attribute in this set specifies an option in the + `[Bridge]` section of the unit. See + {manpage}`systemd.netdev(5)` for details. + ''; + }; + vlanConfig = mkOption { default = {}; example = { Id = 4; }; diff --git a/nixos/modules/virtualisation/lxd-virtual-machine.nix b/nixos/modules/virtualisation/lxd-virtual-machine.nix index 92434cb9babf4..2768e7c259662 100644 --- a/nixos/modules/virtualisation/lxd-virtual-machine.nix +++ b/nixos/modules/virtualisation/lxd-virtual-machine.nix @@ -45,6 +45,10 @@ in { boot.kernelParams = ["console=tty1" "console=${serialDevice}"]; + services.udev.extraRules = '' + SUBSYSTEM=="cpu", CONST{arch}=="x86-64", TEST=="online", ATTR{online}=="0", ATTR{online}="1" + ''; + virtualisation.lxd.agent.enable = lib.mkDefault true; }; } |