diff options
Diffstat (limited to 'nixpkgs/nixos/modules')
15 files changed, 673 insertions, 194 deletions
diff --git a/nixpkgs/nixos/modules/config/zram.nix b/nixpkgs/nixos/modules/config/zram.nix index 991387ea9b2b..ec8b4ed6e931 100644 --- a/nixpkgs/nixos/modules/config/zram.nix +++ b/nixpkgs/nixos/modules/config/zram.nix @@ -105,36 +105,25 @@ in } ]; - - system.requiredKernelConfig = with config.lib.kernelConfig; [ - (isModule "ZRAM") - ]; - - # Disabling this for the moment, as it would create and mkswap devices twice, - # once in stage 2 boot, and again when the zram-reloader service starts. - # boot.kernelModules = [ "zram" ]; - - systemd.packages = [ pkgs.zram-generator ]; - systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap - - environment.etc."systemd/zram-generator.conf".source = - (pkgs.formats.ini { }).generate "zram-generator.conf" (lib.listToAttrs - (builtins.map - (dev: { - name = dev; - value = - let - size = "${toString cfg.memoryPercent} / 100 * ram"; - in - { - zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size; - compression-algorithm = cfg.algorithm; - swap-priority = cfg.priority; - } // lib.optionalAttrs (cfg.writebackDevice != null) { - writeback-device = cfg.writebackDevice; - }; - }) - devices)); + services.zram-generator.enable = true; + + services.zram-generator.settings = lib.listToAttrs + (builtins.map + (dev: { + name = dev; + value = + let + size = "${toString cfg.memoryPercent} / 100 * ram"; + in + { + zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size; + compression-algorithm = cfg.algorithm; + swap-priority = cfg.priority; + } // lib.optionalAttrs (cfg.writebackDevice != null) { + writeback-device = cfg.writebackDevice; + }; + }) + devices); }; diff --git a/nixpkgs/nixos/modules/image/amend-repart-definitions.py b/nixpkgs/nixos/modules/image/amend-repart-definitions.py index 52f10303eb5e..fa9b1544ae85 100644 --- a/nixpkgs/nixos/modules/image/amend-repart-definitions.py +++ b/nixpkgs/nixos/modules/image/amend-repart-definitions.py @@ -53,7 +53,7 @@ def add_closure_to_definition( source = Path(line.strip()) target = str(source.relative_to("/nix/store/")) - target = f":{target}" if strip_nix_store_prefix else "" + target = f":/{target}" if strip_nix_store_prefix else "" copy_files_lines.append(f"CopyFiles={source}{target}\n") @@ -102,7 +102,7 @@ def main() -> None: add_contents_to_definition(definition, contents) closure = config.get("closure") - strip_nix_store_prefix = config.get("stripStorePaths") + strip_nix_store_prefix = config.get("stripNixStorePrefix") add_closure_to_definition(definition, closure, strip_nix_store_prefix) print(target_dir.absolute()) diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix index f05e28cd14bc..aec12ab21a75 100644 --- a/nixpkgs/nixos/modules/module-list.nix +++ b/nixpkgs/nixos/modules/module-list.nix @@ -506,6 +506,7 @@ ./services/hardware/fancontrol.nix ./services/hardware/freefall.nix ./services/hardware/fwupd.nix + ./services/hardware/hddfancontrol.nix ./services/hardware/illum.nix ./services/hardware/interception-tools.nix ./services/hardware/irqbalance.nix @@ -1172,6 +1173,7 @@ ./services/system/self-deploy.nix ./services/system/systembus-notify.nix ./services/system/uptimed.nix + ./services/system/zram-generator.nix ./services/torrent/deluge.nix ./services/torrent/flexget.nix ./services/torrent/magnetico.nix diff --git a/nixpkgs/nixos/modules/programs/zsh/zsh.nix b/nixpkgs/nixos/modules/programs/zsh/zsh.nix index 6bb21cb3ef66..cad639f299c8 100644 --- a/nixpkgs/nixos/modules/programs/zsh/zsh.nix +++ b/nixpkgs/nixos/modules/programs/zsh/zsh.nix @@ -159,6 +159,14 @@ in type = types.bool; }; + enableLsColors = mkOption { + default = true; + description = lib.mdDoc '' + Enable extra colors in directory listings (used by `ls` and `tree`). + ''; + type = types.bool; + }; + }; }; @@ -263,6 +271,11 @@ in ${cfg.interactiveShellInit} + ${optionalString cfg.enableLsColors '' + # Extra colors for directory listings. + eval "$(${pkgs.coreutils}/bin/dircolors -b)" + ''} + # Setup aliases. ${zshAliases} diff --git a/nixpkgs/nixos/modules/services/databases/influxdb2.nix b/nixpkgs/nixos/modules/services/databases/influxdb2.nix index 329533b35dc8..3740cd01b5dc 100644 --- a/nixpkgs/nixos/modules/services/databases/influxdb2.nix +++ b/nixpkgs/nixos/modules/services/databases/influxdb2.nix @@ -3,34 +3,291 @@ let inherit (lib) + any + attrNames + attrValues + count escapeShellArg + filterAttrs + flatten + flip + getExe hasAttr + hasInfix + listToAttrs literalExpression + mapAttrsToList + mdDoc mkEnableOption mkIf mkOption + nameValuePair + optional + subtractLists types + unique ; format = pkgs.formats.json { }; cfg = config.services.influxdb2; configFile = format.generate "config.json" cfg.settings; + + validPermissions = [ + "authorizations" + "buckets" + "dashboards" + "orgs" + "tasks" + "telegrafs" + "users" + "variables" + "secrets" + "labels" + "views" + "documents" + "notificationRules" + "notificationEndpoints" + "checks" + "dbrp" + "annotations" + "sources" + "scrapers" + "notebooks" + "remotes" + "replications" + ]; + + # Determines whether at least one active api token is defined + anyAuthDefined = + flip any (attrValues cfg.provision.organizations) + (o: o.present && flip any (attrValues o.auths) + (a: a.present && a.tokenFile != null)); + + provisionState = pkgs.writeText "provision_state.json" (builtins.toJSON { + inherit (cfg.provision) organizations users; + }); + + provisioningScript = pkgs.writeShellScript "post-start-provision" '' + set -euo pipefail + export INFLUX_HOST="http://"${escapeShellArg ( + if ! hasAttr "http-bind-address" cfg.settings + || hasInfix "0.0.0.0" cfg.settings.http-bind-address + then "localhost:8086" + else cfg.settings.http-bind-address + )} + + # Wait for the influxdb server to come online + count=0 + while ! influx ping &>/dev/null; do + if [ "$count" -eq 300 ]; then + echo "Tried for 30 seconds, giving up..." + exit 1 + fi + + if ! kill -0 "$MAINPID"; then + echo "Main server died, giving up..." + exit 1 + fi + + sleep 0.1 + count=$((count++)) + done + + # Do the initial database setup. Pass /dev/null as configs-path to + # avoid saving the token as the active config. + if test -e "$STATE_DIRECTORY/.first_startup"; then + influx setup \ + --configs-path /dev/null \ + --org ${escapeShellArg cfg.provision.initialSetup.organization} \ + --bucket ${escapeShellArg cfg.provision.initialSetup.bucket} \ + --username ${escapeShellArg cfg.provision.initialSetup.username} \ + --password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \ + --token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \ + --retention ${toString cfg.provision.initialSetup.retention}s \ + --force >/dev/null + + rm -f "$STATE_DIRECTORY/.first_startup" + fi + + provision_result=$(${getExe pkgs.influxdb2-provision} ${provisionState} "$INFLUX_HOST" "$(< "$CREDENTIALS_DIRECTORY/admin-token")") + if [[ "$(jq '[.auths[] | select(.action == "created")] | length' <<< "$provision_result")" -gt 0 ]]; then + echo "Created at least one new token, queueing service restart so we can manipulate secrets" + touch "$STATE_DIRECTORY/.needs_restart" + fi + ''; + + restarterScript = pkgs.writeShellScript "post-start-restarter" '' + set -euo pipefail + if test -e "$STATE_DIRECTORY/.needs_restart"; then + rm -f "$STATE_DIRECTORY/.needs_restart" + /run/current-system/systemd/bin/systemctl restart influxdb2 + fi + ''; + + organizationSubmodule = types.submodule (organizationSubmod: let + org = organizationSubmod.config._module.args.name; + in { + options = { + present = mkOption { + description = mdDoc "Whether to ensure that this organization is present or absent."; + type = types.bool; + default = true; + }; + + description = mkOption { + description = mdDoc "Optional description for the organization."; + default = null; + type = types.nullOr types.str; + }; + + buckets = mkOption { + description = mdDoc "Buckets to provision in this organization."; + default = {}; + type = types.attrsOf (types.submodule (bucketSubmod: let + bucket = bucketSubmod.config._module.args.name; + in { + options = { + present = mkOption { + description = mdDoc "Whether to ensure that this bucket is present or absent."; + type = types.bool; + default = true; + }; + + description = mkOption { + description = mdDoc "Optional description for the bucket."; + default = null; + type = types.nullOr types.str; + }; + + retention = mkOption { + type = types.ints.unsigned; + default = 0; + description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite)."; + }; + }; + })); + }; + + auths = mkOption { + description = mdDoc "API tokens to provision for the user in this organization."; + default = {}; + type = types.attrsOf (types.submodule (authSubmod: let + auth = authSubmod.config._module.args.name; + in { + options = { + id = mkOption { + description = mdDoc "A unique identifier for this authentication token. Since influx doesn't store names for tokens, this will be hashed and appended to the description to identify the token."; + readOnly = true; + default = builtins.substring 0 32 (builtins.hashString "sha256" "${org}:${auth}"); + defaultText = "<a hash derived from org and name>"; + type = types.str; + }; + + present = mkOption { + description = mdDoc "Whether to ensure that this user is present or absent."; + type = types.bool; + default = true; + }; + + description = mkOption { + description = '' + Optional description for the API token. + Note that the actual token will always be created with a descriptionregardless + of whether this is given or not. The name is always added plus a unique suffix + to later identify the token to track whether it has already been created. + ''; + default = null; + type = types.nullOr types.str; + }; + + tokenFile = mkOption { + type = types.nullOr types.path; + default = null; + description = mdDoc "The token value. If not given, influx will automatically generate one."; + }; + + operator = mkOption { + description = mdDoc "Grants all permissions in all organizations."; + default = false; + type = types.bool; + }; + + allAccess = mkOption { + description = mdDoc "Grants all permissions in the associated organization."; + default = false; + type = types.bool; + }; + + readPermissions = mkOption { + description = mdDoc '' + The read permissions to include for this token. Access is usually granted only + for resources in the associated organization. + + Available permissions are `authorizations`, `buckets`, `dashboards`, + `orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`, + `documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`, + `annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`. + + Refer to `influx auth create --help` for a full list with descriptions. + + `buckets` grants read access to all associated buckets. Use `readBuckets` to define + more granular access permissions. + ''; + default = []; + type = types.listOf (types.enum validPermissions); + }; + + writePermissions = mkOption { + description = mdDoc '' + The read permissions to include for this token. Access is usually granted only + for resources in the associated organization. + + Available permissions are `authorizations`, `buckets`, `dashboards`, + `orgs`, `tasks`, `telegrafs`, `users`, `variables`, `secrets`, `labels`, `views`, + `documents`, `notificationRules`, `notificationEndpoints`, `checks`, `dbrp`, + `annotations`, `sources`, `scrapers`, `notebooks`, `remotes`, `replications`. + + Refer to `influx auth create --help` for a full list with descriptions. + + `buckets` grants write access to all associated buckets. Use `writeBuckets` to define + more granular access permissions. + ''; + default = []; + type = types.listOf (types.enum validPermissions); + }; + + readBuckets = mkOption { + description = mdDoc "The organization's buckets which should be allowed to be read"; + default = []; + type = types.listOf types.str; + }; + + writeBuckets = mkOption { + description = mdDoc "The organization's buckets which should be allowed to be written"; + default = []; + type = types.listOf types.str; + }; + }; + })); + }; + }; + }); in { options = { services.influxdb2 = { - enable = mkEnableOption (lib.mdDoc "the influxdb2 server"); + enable = mkEnableOption (mdDoc "the influxdb2 server"); package = mkOption { default = pkgs.influxdb2-server; defaultText = literalExpression "pkgs.influxdb2"; - description = lib.mdDoc "influxdb2 derivation to use."; + description = mdDoc "influxdb2 derivation to use."; type = types.package; }; settings = mkOption { default = { }; - description = lib.mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.''; + description = mdDoc ''configuration options for influxdb2, see <https://docs.influxdata.com/influxdb/v2.0/reference/config-options> for details.''; type = format.type; }; @@ -41,52 +298,135 @@ in organization = mkOption { type = types.str; example = "main"; - description = "Primary organization name"; + description = mdDoc "Primary organization name"; }; bucket = mkOption { type = types.str; example = "example"; - description = "Primary bucket name"; + description = mdDoc "Primary bucket name"; }; username = mkOption { type = types.str; default = "admin"; - description = "Primary username"; + description = mdDoc "Primary username"; }; retention = mkOption { - type = types.str; - default = "0"; - description = '' - The duration for which the bucket will retain data (0 is infinite). - Accepted units are `ns` (nanoseconds), `us` or `µs` (microseconds), `ms` (milliseconds), - `s` (seconds), `m` (minutes), `h` (hours), `d` (days) and `w` (weeks). - ''; + type = types.ints.unsigned; + default = 0; + description = mdDoc "The duration in seconds for which the bucket will retain data (0 is infinite)."; }; passwordFile = mkOption { type = types.path; - description = "Password for primary user. Don't use a file from the nix store!"; + description = mdDoc "Password for primary user. Don't use a file from the nix store!"; }; tokenFile = mkOption { type = types.path; - description = "API Token to set for the admin user. Don't use a file from the nix store!"; + description = mdDoc "API Token to set for the admin user. Don't use a file from the nix store!"; }; }; + + organizations = mkOption { + description = mdDoc "Organizations to provision."; + example = literalExpression '' + { + myorg = { + description = "My organization"; + buckets.mybucket = { + description = "My bucket"; + retention = 31536000; # 1 year + }; + auths.mytoken = { + readBuckets = ["mybucket"]; + tokenFile = "/run/secrets/mytoken"; + }; + }; + } + ''; + default = {}; + type = types.attrsOf organizationSubmodule; + }; + + users = mkOption { + description = mdDoc "Users to provision."; + default = {}; + example = literalExpression '' + { + # admin = {}; /* The initialSetup.username will automatically be added. */ + myuser.passwordFile = "/run/secrets/myuser_password"; + } + ''; + type = types.attrsOf (types.submodule (userSubmod: let + user = userSubmod.config._module.args.name; + org = userSubmod.config.org; + in { + options = { + present = mkOption { + description = mdDoc "Whether to ensure that this user is present or absent."; + type = types.bool; + default = true; + }; + + passwordFile = mkOption { + description = mdDoc "Password for the user. If unset, the user will not be able to log in until a password is set by an operator! Don't use a file from the nix store!"; + default = null; + type = types.nullOr types.path; + }; + }; + })); + }; }; }; }; config = mkIf cfg.enable { - assertions = [ - { - assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings); - message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; - } - ]; + assertions = + [ + { + assertion = !(hasAttr "bolt-path" cfg.settings) && !(hasAttr "engine-path" cfg.settings); + message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; + } + ] + ++ flatten (flip mapAttrsToList cfg.provision.organizations (orgName: org: + flip mapAttrsToList org.auths (authName: auth: + [ + { + assertion = 1 == count (x: x) [ + auth.operator + auth.allAccess + (auth.readPermissions != [] + || auth.writePermissions != [] + || auth.readBuckets != [] + || auth.writeBuckets != []) + ]; + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: The `operator` and `allAccess` options are mutually exclusive with each other and the granular permission settings."; + } + (let unknownBuckets = subtractLists (attrNames org.buckets) auth.readBuckets; in { + assertion = unknownBuckets == []; + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in readBuckets: ${toString unknownBuckets}"; + }) + (let unknownBuckets = subtractLists (attrNames org.buckets) auth.writeBuckets; in { + assertion = unknownBuckets == []; + message = "influxdb2: provision.organizations.${orgName}.auths.${authName}: Refers to invalid buckets in writeBuckets: ${toString unknownBuckets}"; + }) + ] + ) + )); + + services.influxdb2.provision = mkIf cfg.provision.enable { + organizations.${cfg.provision.initialSetup.organization} = { + buckets.${cfg.provision.initialSetup.bucket} = { + inherit (cfg.provision.initialSetup) retention; + }; + }; + users.${cfg.provision.initialSetup.username} = { + inherit (cfg.provision.initialSetup) passwordFile; + }; + }; systemd.services.influxdb2 = { description = "InfluxDB is an open-source, distributed, time series database"; @@ -111,58 +451,38 @@ in "admin-password:${cfg.provision.initialSetup.passwordFile}" "admin-token:${cfg.provision.initialSetup.tokenFile}" ]; + + ExecStartPost = mkIf cfg.provision.enable ( + [provisioningScript] ++ + # Only the restarter runs with elevated privileges + optional anyAuthDefined "+${restarterScript}" + ); }; - path = [pkgs.influxdb2-cli]; + path = [ + pkgs.influxdb2-cli + pkgs.jq + ]; - # Mark if this is the first startup so postStart can do the initial setup - preStart = mkIf cfg.provision.enable '' + # Mark if this is the first startup so postStart can do the initial setup. + # Also extract any token secret mappings and apply them if this isn't the first start. + preStart = let + tokenPaths = listToAttrs (flatten + # For all organizations + (flip mapAttrsToList cfg.provision.organizations + # For each contained token that has a token file + (_: org: flip mapAttrsToList (filterAttrs (_: x: x.tokenFile != null) org.auths) + # Collect id -> tokenFile for the mapping + (_: auth: nameValuePair auth.id auth.tokenFile)))); + tokenMappings = pkgs.writeText "token_mappings.json" (builtins.toJSON tokenPaths); + in mkIf cfg.provision.enable '' if ! test -e "$STATE_DIRECTORY/influxd.bolt"; then touch "$STATE_DIRECTORY/.first_startup" + else + # Manipulate provisioned api tokens if necessary + ${getExe pkgs.influxdb2-token-manipulator} "$STATE_DIRECTORY/influxd.bolt" ${tokenMappings} fi ''; - - postStart = let - initCfg = cfg.provision.initialSetup; - in mkIf cfg.provision.enable ( - '' - set -euo pipefail - export INFLUX_HOST="http://"${escapeShellArg (cfg.settings.http-bind-address or "localhost:8086")} - - # Wait for the influxdb server to come online - count=0 - while ! influx ping &>/dev/null; do - if [ "$count" -eq 300 ]; then - echo "Tried for 30 seconds, giving up..." - exit 1 - fi - - if ! kill -0 "$MAINPID"; then - echo "Main server died, giving up..." - exit 1 - fi - - sleep 0.1 - count=$((count++)) - done - - # Do the initial database setup. Pass /dev/null as configs-path to - # avoid saving the token as the active config. - if test -e "$STATE_DIRECTORY/.first_startup"; then - influx setup \ - --configs-path /dev/null \ - --org ${escapeShellArg initCfg.organization} \ - --bucket ${escapeShellArg initCfg.bucket} \ - --username ${escapeShellArg initCfg.username} \ - --password "$(< "$CREDENTIALS_DIRECTORY/admin-password")" \ - --token "$(< "$CREDENTIALS_DIRECTORY/admin-token")" \ - --retention ${escapeShellArg initCfg.retention} \ - --force >/dev/null - - rm -f "$STATE_DIRECTORY/.first_startup" - fi - '' - ); }; users.extraUsers.influxdb2 = { diff --git a/nixpkgs/nixos/modules/services/editors/emacs.nix b/nixpkgs/nixos/modules/services/editors/emacs.nix index fe3a10159794..fad4f39ff210 100644 --- a/nixpkgs/nixos/modules/services/editors/emacs.nix +++ b/nixpkgs/nixos/modules/services/editors/emacs.nix @@ -80,6 +80,15 @@ in using the EDITOR environment variable. ''; }; + + startWithGraphical = mkOption { + type = types.bool; + default = config.services.xserver.enable; + defaultText = literalExpression "config.services.xserver.enable"; + description = lib.mdDoc '' + Start emacs with the graphical session instead of any session. Without this, emacs clients will not be able to create frames in the graphical session. + ''; + }; }; config = mkIf (cfg.enable || cfg.install) { @@ -92,7 +101,13 @@ in ExecStop = "${cfg.package}/bin/emacsclient --eval (kill-emacs)"; Restart = "always"; }; - } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; }; + + unitConfig = optionalAttrs cfg.startWithGraphical { + After = "graphical-session.target"; + }; + } // optionalAttrs cfg.enable { + wantedBy = if cfg.startWithGraphical then [ "graphical-session.target" ] else [ "default.target" ]; + }; environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ]; diff --git a/nixpkgs/nixos/modules/services/hardware/hddfancontrol.nix b/nixpkgs/nixos/modules/services/hardware/hddfancontrol.nix new file mode 100644 index 000000000000..f472b5774cbf --- /dev/null +++ b/nixpkgs/nixos/modules/services/hardware/hddfancontrol.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.hddfancontrol; + types = lib.types; +in + +{ + options = { + + services.hddfancontrol.enable = lib.mkEnableOption (lib.mdDoc "hddfancontrol daemon"); + + services.hddfancontrol.disks = lib.mkOption { + type = with types; listOf path; + default = []; + description = lib.mdDoc '' + Drive(s) to get temperature from + ''; + example = ["/dev/sda"]; + }; + + services.hddfancontrol.pwmPaths = lib.mkOption { + type = with types; listOf path; + default = []; + description = lib.mdDoc '' + PWM filepath(s) to control fan speed (under /sys) + ''; + example = ["/sys/class/hwmon/hwmon2/pwm1"]; + }; + + services.hddfancontrol.smartctl = lib.mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Probe temperature using smartctl instead of hddtemp or hdparm + ''; + }; + + services.hddfancontrol.extraArgs = lib.mkOption { + type = with types; listOf str; + default = []; + description = lib.mdDoc '' + Extra commandline arguments for hddfancontrol + ''; + example = ["--pwm-start-value=32" + "--pwm-stop-value=0" + "--spin-down-time=900"]; + }; + }; + + config = lib.mkIf cfg.enable ( + let args = lib.concatLists [ + ["-d"] cfg.disks + ["-p"] cfg.pwmPaths + (lib.optional cfg.smartctl "--smartctl") + cfg.extraArgs + ]; in { + systemd.packages = [pkgs.hddfancontrol]; + + systemd.services.hddfancontrol = { + wantedBy = [ "multi-user.target" ]; + environment.HDDFANCONTROL_ARGS = lib.escapeShellArgs args; + }; + } + ); +} diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix index f308b7e33114..8b35cc8d6669 100644 --- a/nixpkgs/nixos/modules/services/networking/tailscale.nix +++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix @@ -6,7 +6,7 @@ let cfg = config.services.tailscale; isNetworkd = config.networking.useNetworkd; in { - meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 ]; + meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 mfrw ]; options.services.tailscale = { enable = mkEnableOption (lib.mdDoc "Tailscale client daemon"); diff --git a/nixpkgs/nixos/modules/services/networking/twingate.nix b/nixpkgs/nixos/modules/services/networking/twingate.nix index 170d392bf213..03c68fc874f0 100644 --- a/nixpkgs/nixos/modules/services/networking/twingate.nix +++ b/nixpkgs/nixos/modules/services/networking/twingate.nix @@ -17,7 +17,7 @@ in }; networking.firewall.checkReversePath = lib.mkDefault "loose"; - services.resolved.enable = !(config.networking.networkmanager.enable); + services.resolved.enable = lib.mkIf (!config.networking.networkmanager.enable) true; environment.systemPackages = [ cfg.package ]; # For the CLI. }; diff --git a/nixpkgs/nixos/modules/services/system/zram-generator.nix b/nixpkgs/nixos/modules/services/system/zram-generator.nix new file mode 100644 index 000000000000..5902eda55696 --- /dev/null +++ b/nixpkgs/nixos/modules/services/system/zram-generator.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.zram-generator; + settingsFormat = pkgs.formats.ini { }; +in +{ + meta = { + maintainers = with lib.maintainers; [ nickcao ]; + }; + + options.services.zram-generator = { + enable = lib.mkEnableOption (lib.mdDoc "Systemd unit generator for zram devices"); + + package = lib.mkPackageOptionMD pkgs "zram-generator" { }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + }; + default = { }; + description = lib.mdDoc '' + Configuration for zram-generator, + see https://github.com/systemd/zram-generator for documentation. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + system.requiredKernelConfig = with config.lib.kernelConfig; [ + (isModule "ZRAM") + ]; + + systemd.packages = [ cfg.package ]; + systemd.services."systemd-zram-setup@".path = [ pkgs.util-linux ]; # for mkswap + + environment.etc."systemd/zram-generator.conf".source = settingsFormat.generate "zram-generator.conf" cfg.settings; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix index e2ef350ba4e5..5f42f42a9af9 100644 --- a/nixpkgs/nixos/modules/services/web-apps/netbox.nix +++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix @@ -1,7 +1,5 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.services.netbox; pythonFmt = pkgs.formats.pythonVars {}; @@ -17,7 +15,7 @@ let pkg = (cfg.package.overrideAttrs (old: { installPhase = old.installPhase + '' ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py - '' + optionalString cfg.enableLdap '' + '' + lib.optionalString cfg.enableLdap '' ln -s ${cfg.ldapConfigPath} $out/opt/netbox/netbox/netbox/ldap_config.py ''; })).override { @@ -31,7 +29,7 @@ let in { options.services.netbox = { - enable = mkOption { + enable = lib.mkOption { type = lib.types.bool; default = false; description = lib.mdDoc '' @@ -66,18 +64,18 @@ in { }; }; - listenAddress = mkOption { - type = types.str; + listenAddress = lib.mkOption { + type = lib.types.str; default = "[::1]"; description = lib.mdDoc '' Address the server will listen on. ''; }; - package = mkOption { - type = types.package; - default = if versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3; - defaultText = literalExpression '' + package = lib.mkOption { + type = lib.types.package; + default = if lib.versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3; + defaultText = lib.literalExpression '' if versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3; ''; description = lib.mdDoc '' @@ -85,18 +83,18 @@ in { ''; }; - port = mkOption { - type = types.port; + port = lib.mkOption { + type = lib.types.port; default = 8001; description = lib.mdDoc '' Port the server will listen on. ''; }; - plugins = mkOption { - type = types.functionTo (types.listOf types.package); + plugins = lib.mkOption { + type = with lib.types; functionTo (listOf package); default = _: []; - defaultText = literalExpression '' + defaultText = lib.literalExpression '' python3Packages: with python3Packages; []; ''; description = lib.mdDoc '' @@ -104,23 +102,23 @@ in { ''; }; - dataDir = mkOption { - type = types.str; + dataDir = lib.mkOption { + type = lib.types.str; default = "/var/lib/netbox"; description = lib.mdDoc '' Storage path of netbox. ''; }; - secretKeyFile = mkOption { - type = types.path; + secretKeyFile = lib.mkOption { + type = lib.types.path; description = lib.mdDoc '' Path to a file containing the secret key. ''; }; - extraConfig = mkOption { - type = types.lines; + extraConfig = lib.mkOption { + type = lib.types.lines; default = ""; description = lib.mdDoc '' Additional lines of configuration appended to the `configuration.py`. @@ -128,8 +126,8 @@ in { ''; }; - enableLdap = mkOption { - type = types.bool; + enableLdap = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Enable LDAP-Authentication for Netbox. @@ -138,8 +136,8 @@ in { ''; }; - ldapConfigPath = mkOption { - type = types.path; + ldapConfigPath = lib.mkOption { + type = lib.types.path; default = ""; description = lib.mdDoc '' Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`. @@ -173,15 +171,17 @@ in { }; }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { services.netbox = { - plugins = mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]); + plugins = lib.mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]); settings = { STATIC_ROOT = staticDir; MEDIA_ROOT = "${cfg.dataDir}/media"; REPORTS_ROOT = "${cfg.dataDir}/reports"; SCRIPTS_ROOT = "${cfg.dataDir}/scripts"; + GIT_PATH = "${pkgs.gitMinimal}/bin/git"; + DATABASE = { NAME = "netbox"; USER = "netbox"; @@ -264,40 +264,40 @@ in { RestartSec = 30; }; in { - netbox-migration = { - description = "NetBox migrations"; - wantedBy = [ "netbox.target" ]; - - environment = { - PYTHONPATH = pkg.pythonPath; - }; - - serviceConfig = defaultServiceConfig // { - Type = "oneshot"; - ExecStart = '' - ${pkg}/bin/netbox migrate - ''; - PrivateTmp = true; - }; - }; - netbox = { description = "NetBox WSGI Service"; documentation = [ "https://docs.netbox.dev/" ]; wantedBy = [ "netbox.target" ]; - after = [ "network-online.target" "netbox-migration.service" ]; + after = [ "network-online.target" ]; wants = [ "network-online.target" ]; + environment.PYTHONPATH = pkg.pythonPath; + preStart = '' + # On the first run, or on upgrade / downgrade, run migrations and related. + # This mostly correspond to upstream NetBox's 'upgrade.sh' script. + versionFile="${cfg.dataDir}/version" + + if [[ -e "$versionFile" && "$(cat "$versionFile")" == "${cfg.package.version}" ]]; then + exit 0 + fi + + ${pkg}/bin/netbox migrate ${pkg}/bin/netbox trace_paths --no-input ${pkg}/bin/netbox collectstatic --no-input ${pkg}/bin/netbox remove_stale_contenttypes --no-input + # TODO: remove the condition when we remove netbox_3_3 + ${lib.optionalString + (lib.versionAtLeast cfg.package.version "3.5.0") + "${pkg}/bin/netbox reindex --lazy"} + ${pkg}/bin/netbox clearsessions + ${pkg}/bin/netbox clearcache + + echo "${cfg.package.version}" > "$versionFile" ''; - environment.PYTHONPATH = pkg.pythonPath; - serviceConfig = defaultServiceConfig // { ExecStart = '' ${pkgs.python3Packages.gunicorn}/bin/gunicorn netbox.wsgi \ @@ -331,7 +331,7 @@ in { wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; + after = [ "network-online.target" "netbox.service" ]; wants = [ "network-online.target" ]; environment.PYTHONPATH = pkg.pythonPath; @@ -351,7 +351,7 @@ in { wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; + after = [ "network-online.target" "netbox.service" ]; wants = [ "network-online.target" ]; timerConfig = { diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix index 5cc9ef6dd6d9..cec0b379f67a 100644 --- a/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix +++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix @@ -24,21 +24,26 @@ let } ''; - configFile = - let - Caddyfile = pkgs.writeTextDir "Caddyfile" '' - { - ${cfg.globalConfig} - } - ${cfg.extraConfig} - ''; + settingsFormat = pkgs.formats.json { }; - Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" { nativeBuildInputs = [ cfg.package ]; } '' - mkdir -p $out - cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile - caddy fmt --overwrite $out/Caddyfile - ''; - in + configFile = + if cfg.settings != { } then + settingsFormat.generate "caddy.json" cfg.settings + else + let + Caddyfile = pkgs.writeTextDir "Caddyfile" '' + { + ${cfg.globalConfig} + } + ${cfg.extraConfig} + ''; + + Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" { nativeBuildInputs = [ cfg.package ]; } '' + mkdir -p $out + cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile + caddy fmt --overwrite $out/Caddyfile + ''; + in "${if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile}/Caddyfile"; etcConfigFile = "caddy/caddy_config"; @@ -299,6 +304,27 @@ in which could delay the reload essentially indefinitely. ''; }; + + settings = mkOption { + type = settingsFormat.type; + default = {}; + description = lib.mdDoc '' + Structured configuration for Caddy to generate a Caddy JSON configuration file. + See <https://caddyserver.com/docs/json/> for available options. + + ::: {.warning} + Using a [Caddyfile](https://caddyserver.com/docs/caddyfile) instead of a JSON config is highly recommended by upstream. + There are only very few exception to this. + + Please use a Caddyfile via {option}`services.caddy.configFile`, {option}`services.caddy.virtualHosts` or + {option}`services.caddy.extraConfig` with {option}`services.caddy.globalConfig` instead. + ::: + + ::: {.note} + Takes presence over most `services.caddy.*` options, such as {option}`services.caddy.configFile` and {option}`services.caddy.virtualHosts`, if specified. + ::: + ''; + }; }; # implementation diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/dwm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/dwm.nix index e114f2e26b17..82900fd30540 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/dwm.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/dwm.nix @@ -45,6 +45,7 @@ in { name = "dwm"; start = '' + export _JAVA_AWT_WM_NONREPARENTING=1 dwm & waitPID=$! ''; diff --git a/nixpkgs/nixos/modules/system/boot/binfmt.nix b/nixpkgs/nixos/modules/system/boot/binfmt.nix index bf1688feb19e..5172371d0afb 100644 --- a/nixpkgs/nixos/modules/system/boot/binfmt.nix +++ b/nixpkgs/nixos/modules/system/boot/binfmt.nix @@ -137,14 +137,8 @@ let magicOrExtension = ''\x00asm''; mask = ''\xff\xff\xff\xff''; }; - x86_64-windows = { - magicOrExtension = "exe"; - recognitionType = "extension"; - }; - i686-windows = { - magicOrExtension = "exe"; - recognitionType = "extension"; - }; + x86_64-windows.magicOrExtension = "MZ"; + i686-windows.magicOrExtension = "MZ"; }; in { diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl index a84e374624d1..d1e7a0cb8178 100644 --- a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -516,38 +516,53 @@ sub addEntry { $conf .= "}\n\n"; } +sub addGeneration { + my ($name, $nameSuffix, $path, $options, $current) = @_; -# Add default entries. -$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS; + # Do not search for grand children + my @links = sort (glob "$path/specialisation/*"); -addEntry("@distroName@ - Default", $defaultConfig, $entryOptions, 1); + if ($current != 1 && scalar(@links) != 0) { + $conf .= "submenu \"> $name$nameSuffix\" --class submenu {\n"; + } -$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS; + addEntry("$name" . (scalar(@links) == 0 ? "" : " - Default") . $nameSuffix, $path, $options, $current); -# Find all the children of the current default configuration -# Do not search for grand children -my @links = sort (glob "$defaultConfig/specialisation/*"); -foreach my $link (@links) { + # Find all the children of the current default configuration + # Do not search for grand children + foreach my $link (@links) { - my $entryName = ""; + my $entryName = ""; - my $cfgName = readFile("$link/configuration-name"); + my $cfgName = readFile("$link/configuration-name"); - my $date = strftime("%F", localtime(lstat($link)->mtime)); - my $version = - -e "$link/nixos-version" - ? readFile("$link/nixos-version") - : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); + my $date = strftime("%F", localtime(lstat($link)->mtime)); + my $version = + -e "$link/nixos-version" + ? readFile("$link/nixos-version") + : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); - if ($cfgName) { - $entryName = $cfgName; - } else { - my $linkname = basename($link); - $entryName = "($linkname - $date - $version)"; + if ($cfgName) { + $entryName = $cfgName; + } else { + my $linkname = basename($link); + $entryName = "($linkname - $date - $version)"; + } + addEntry("$name - $entryName", $link, "", 1); + } + + if ($current != 1 && scalar(@links) != 0) { + $conf .= "}\n"; } - addEntry("@distroName@ - $entryName", $link, "", 1); } +# Add default entries. +$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS; + +addGeneration("@distroName@", "", $defaultConfig, $entryOptions, 1); + +$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS; + my $grubBootPath = $grubBoot->path; # extraEntries could refer to @bootRoot@, which we have to substitute $conf =~ s/\@bootRoot\@/$grubBootPath/g; @@ -577,7 +592,7 @@ sub addProfile { -e "$link/nixos-version" ? readFile("$link/nixos-version") : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); - addEntry("@distroName@ - Configuration " . nrFromGen($link) . " ($date - $version)", $link, $subEntryOptions, 0); + addGeneration("@distroName@ - Configuration " . nrFromGen($link), " ($date - $version)", $link, $subEntryOptions, 0); } $conf .= "}\n"; |