diff options
author | Alyssa Ross <hi@alyssa.is> | 2021-09-08 17:57:14 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2021-09-13 11:31:47 +0000 |
commit | ee7984efa14902a2ddd820c937457667a4f40c6a (patch) | |
tree | c9c1d046733cefe5e21fdd8a52104175d47b2443 /nixpkgs/nixos/modules/services | |
parent | ffc9d4ba381da62fd08b361bacd1e71e2a3d934d (diff) | |
parent | b3c692172e5b5241b028a98e1977f9fb12eeaf42 (diff) | |
download | nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.gz nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.bz2 nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.lz nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.xz nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.zst nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.zip |
Merge commit 'b3c692172e5b5241b028a98e1977f9fb12eeaf42'
Diffstat (limited to 'nixpkgs/nixos/modules/services')
141 files changed, 4773 insertions, 1449 deletions
diff --git a/nixpkgs/nixos/modules/services/admin/meshcentral.nix b/nixpkgs/nixos/modules/services/admin/meshcentral.nix new file mode 100644 index 000000000000..ae7b6edda7d5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/admin/meshcentral.nix @@ -0,0 +1,53 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.services.meshcentral; + configFormat = pkgs.formats.json {}; + configFile = configFormat.generate "meshcentral-config.json" cfg.settings; +in with lib; { + options.services.meshcentral = with types; { + enable = mkEnableOption "MeshCentral computer management server"; + package = mkOption { + description = "MeshCentral package to use. Replacing this may be necessary to add dependencies for extra functionality."; + type = types.package; + default = pkgs.meshcentral; + defaultText = "pkgs.meshcentral"; + }; + settings = mkOption { + description = '' + Settings for MeshCentral. Refer to upstream documentation for details: + + <itemizedlist> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json">JSON Schema definition</link></para></listitem> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config.json">simple sample configuration</link></para></listitem> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config-advanced.json">complex sample configuration</link></para></listitem> + <listitem><para><link xlink:href="https://www.meshcommander.com/meshcentral2">Old homepage) with documentation link</link></para></listitem> + </itemizedlist> + ''; + type = types.submodule { + freeformType = configFormat.type; + }; + example = { + settings = { + WANonly = true; + Cert = "meshcentral.example.com"; + TlsOffload = "10.0.0.2,fd42::2"; + Port = 4430; + }; + domains."".certUrl = "https://meshcentral.example.com/"; + }; + }; + }; + config = mkIf cfg.enable { + services.meshcentral.settings.settings.autoBackup.backupPath = lib.mkDefault "/var/lib/meshcentral/backups"; + systemd.services.meshcentral = { + wantedBy = ["multi-user.target"]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/meshcentral --datapath /var/lib/meshcentral --configfile ${configFile}"; + DynamicUser = true; + StateDirectory = "meshcentral"; + CacheDirectory = "meshcentral"; + }; + }; + }; + meta.maintainers = [ maintainers.lheckemann ]; +} diff --git a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix index fc8a1bc3c23c..8fdfda9a66d8 100644 --- a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix +++ b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix @@ -7,12 +7,13 @@ let inherit (builtins) concatStringsSep; - config_file_content = lib.generators.toKeyValue {} cfg.configItems; + config_file_content = lib.generators.toKeyValue { } cfg.configItems; config_file = pkgs.writeText "rabbitmq.conf" config_file_content; advanced_config_file = pkgs.writeText "advanced.config" cfg.config; -in { +in +{ ###### interface options = { services.rabbitmq = { @@ -79,7 +80,7 @@ in { }; configItems = mkOption { - default = {}; + default = { }; type = types.attrsOf types.str; example = literalExample '' { @@ -123,16 +124,38 @@ in { }; plugins = mkOption { - default = []; + default = [ ]; type = types.listOf types.str; description = "The names of plugins to enable"; }; pluginDirs = mkOption { - default = []; + default = [ ]; type = types.listOf types.path; description = "The list of directories containing external plugins"; }; + + managementPlugin = mkOption { + description = "The options to run the management plugin"; + type = types.submodule { + options = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the management plugin + ''; + }; + port = mkOption { + default = 15672; + type = types.port; + description = '' + On which port to run the management plugin + ''; + }; + }; + }; + }; }; }; @@ -157,8 +180,13 @@ in { services.rabbitmq.configItems = { "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}"; + } // optionalAttrs cfg.managementPlugin.enable { + "management.tcp.port" = toString cfg.managementPlugin.port; + "management.tcp.ip" = cfg.listenAddress; }; + services.rabbitmq.plugins = optional cfg.managementPlugin.enable "rabbitmq_management"; + systemd.services.rabbitmq = { description = "RabbitMQ Server"; @@ -180,7 +208,7 @@ in { RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" '' [ ${concatStringsSep "," cfg.plugins} ]. ''; - } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; }; + } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; }; serviceConfig = { ExecStart = "${cfg.package}/sbin/rabbitmq-server"; diff --git a/nixpkgs/nixos/modules/services/audio/hqplayerd.nix b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix new file mode 100644 index 000000000000..d549ac77e0e5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix @@ -0,0 +1,142 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.hqplayerd; + pkg = pkgs.hqplayerd; + # XXX: This is hard-coded in the distributed binary, don't try to change it. + stateDir = "/var/lib/hqplayer"; + configDir = "/etc/hqplayer"; +in +{ + options = { + services.hqplayerd = { + enable = mkEnableOption "HQPlayer Embedded"; + + auth = { + username = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Username used for HQPlayer's WebUI. + + Without this you will need to manually create the credentials after + first start by going to http://your.ip/8088/auth + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password used for HQPlayer's WebUI. + + Without this you will need to manually create the credentials after + first start by going to http://your.ip/8088/auth + ''; + }; + }; + + licenseFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to the HQPlayer license key file. + + Without this, the service will run in trial mode and restart every 30 + minutes. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Opens ports needed for the WebUI and controller API. + ''; + }; + + config = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + HQplayer daemon configuration, written to /etc/hqplayer/hqplayerd.xml. + + Refer to ${pkg}/share/doc/hqplayerd/readme.txt for possible values. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = (cfg.auth.username != null -> cfg.auth.password != null) + && (cfg.auth.password != null -> cfg.auth.username != null); + message = "You must set either both services.hqplayer.auth.username and password, or neither."; + } + ]; + + environment = { + etc = { + "hqplayer/hqplayerd.xml" = mkIf (cfg.config != null) { source = pkgs.writeText "hqplayerd.xml" cfg.config; }; + "hqplayer/hqplayerd4-key.xml" = mkIf (cfg.licenseFile != null) { source = cfg.licenseFile; }; + "modules-load.d/taudio2.conf".source = "${pkg}/etc/modules-load.d/taudio2.conf"; + }; + systemPackages = [ pkg ]; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 8088 4321 ]; + }; + + services.udev.packages = [ pkg ]; + + systemd = { + tmpfiles.rules = [ + "d ${configDir} 0755 hqplayer hqplayer - -" + "d ${stateDir} 0755 hqplayer hqplayer - -" + "d ${stateDir}/home 0755 hqplayer hqplayer - -" + ]; + + packages = [ pkg ]; + + services.hqplayerd = { + wantedBy = [ "multi-user.target" ]; + after = [ "systemd-tmpfiles-setup.service" ]; + + environment.HOME = "${stateDir}/home"; + + unitConfig.ConditionPathExists = [ configDir stateDir ]; + + restartTriggers = optionals (cfg.config != null) [ config.environment.etc."hqplayer/hqplayerd.xml".source ]; + + preStart = '' + cp -r "${pkg}/var/lib/hqplayer/web" "${stateDir}" + chmod -R u+wX "${stateDir}/web" + + if [ ! -f "${configDir}/hqplayerd.xml" ]; then + echo "creating initial config file" + install -m 0644 "${pkg}/etc/hqplayer/hqplayerd.xml" "${configDir}/hqplayerd.xml" + fi + '' + optionalString (cfg.auth.username != null && cfg.auth.password != null) '' + ${pkg}/bin/hqplayerd -s ${cfg.auth.username} ${cfg.auth.password} + ''; + }; + }; + + users.groups = { + hqplayer.gid = config.ids.gids.hqplayer; + }; + + users.users = { + hqplayer = { + description = "hqplayer daemon user"; + extraGroups = [ "audio" ]; + group = "hqplayer"; + uid = config.ids.uids.hqplayer; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/audio/navidrome.nix b/nixpkgs/nixos/modules/services/audio/navidrome.nix new file mode 100644 index 000000000000..c2fe429f9844 --- /dev/null +++ b/nixpkgs/nixos/modules/services/audio/navidrome.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.navidrome; + settingsFormat = pkgs.formats.json {}; +in { + options = { + services.navidrome = { + + enable = mkEnableOption pkgs.navidrome.meta.description; + + settings = mkOption rec { + type = settingsFormat.type; + apply = recursiveUpdate default; + default = { + Address = "127.0.0.1"; + Port = 4533; + }; + example = { + MusicFolder = "/mnt/music"; + }; + description = '' + Configuration for Navidrome, see <link xlink:href="https://www.navidrome.org/docs/usage/configuration-options/"/> for supported values. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.navidrome = { + description = "Navidrome Media Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = '' + ${pkgs.navidrome}/bin/navidrome --configfile ${settingsFormat.generate "navidrome.json" cfg.settings} + ''; + DynamicUser = true; + StateDirectory = "navidrome"; + WorkingDirectory = "/var/lib/navidrome"; + RuntimeDirectory = "navidrome"; + RootDirectory = "/run/navidrome"; + ReadWritePaths = ""; + BindReadOnlyPaths = [ + builtins.storeDir + ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder; + CapabilityBoundingSet = ""; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + UMask = "0066"; + ProtectHostname = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/audio/networkaudiod.nix b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix new file mode 100644 index 000000000000..265a4e1d95d6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + name = "networkaudiod"; + cfg = config.services.networkaudiod; +in { + options = { + services.networkaudiod = { + enable = mkEnableOption "Networkaudiod (NAA)"; + }; + }; + + config = mkIf cfg.enable { + systemd.packages = [ pkgs.networkaudiod ]; + systemd.services.networkaudiod.wantedBy = [ "multi-user.target" ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix index 85273a2039c3..e08f8a4f9e7a 100644 --- a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix +++ b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix @@ -14,9 +14,6 @@ in { default = false; description = '' Open ports in the firewall for the bridge. - - UDP: 9003 - TCP: 9100 - 9200 ''; }; user = mkOption { @@ -54,10 +51,15 @@ in { }; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPortRanges = [ - { from = 9100; to = 9200; } - ]; + allowedTCPPortRanges = [{ from = 9100; to = 9200; }]; allowedUDPPorts = [ 9003 ]; + extraCommands = '' + iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT + iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT + iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT + iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT + iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT + ''; }; diff --git a/nixpkgs/nixos/modules/services/audio/roon-server.nix b/nixpkgs/nixos/modules/services/audio/roon-server.nix index eceb65044c5b..42da5a100170 100644 --- a/nixpkgs/nixos/modules/services/audio/roon-server.nix +++ b/nixpkgs/nixos/modules/services/audio/roon-server.nix @@ -14,9 +14,6 @@ in { default = false; description = '' Open ports in the firewall for the server. - - UDP: 9003 - TCP: 9100 - 9200 ''; }; user = mkOption { @@ -54,10 +51,15 @@ in { }; networking.firewall = mkIf cfg.openFirewall { - allowedTCPPortRanges = [ - { from = 9100; to = 9200; } - ]; + allowedTCPPortRanges = [{ from = 9100; to = 9200; }]; allowedUDPPorts = [ 9003 ]; + extraCommands = '' + iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT + iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT + iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT + iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT + iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT + ''; }; diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix index 18fb29fd72a5..ccbc7726392d 100644 --- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix +++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix @@ -102,7 +102,7 @@ let mkWrapperDrv = { original, name, set ? {} }: - pkgs.runCommandNoCC "${name}-wrapper" { + pkgs.runCommand "${name}-wrapper" { buildInputs = [ pkgs.makeWrapper ]; } (with lib; '' makeWrapper "${original}" "$out/bin/${name}" \ diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix index f658eb756f7b..bcc135005e16 100644 --- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix +++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix @@ -7,28 +7,49 @@ let cfg = config.services.postgresqlBackup; postgresqlBackupService = db: dumpCmd: - { + let + compressSuffixes = { + "none" = ""; + "gzip" = ".gz"; + "zstd" = ".zstd"; + }; + compressSuffix = getAttr cfg.compression compressSuffixes; + + compressCmd = getAttr cfg.compression { + "none" = "cat"; + "gzip" = "${pkgs.gzip}/bin/gzip -c"; + "zstd" = "${pkgs.zstd}/bin/zstd -c"; + }; + + mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}"; + curFile = mkSqlPath "" compressSuffix; + prevFile = mkSqlPath ".prev" compressSuffix; + prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes); + inProgressFile = mkSqlPath ".in-progress" compressSuffix; + in { enable = true; description = "Backup of ${db} database(s)"; requires = [ "postgresql.service" ]; - path = [ pkgs.coreutils pkgs.gzip config.services.postgresql.package ]; + path = [ pkgs.coreutils config.services.postgresql.package ]; script = '' set -e -o pipefail umask 0077 # ensure backup is only readable by postgres user - if [ -e ${cfg.location}/${db}.sql.gz ]; then - mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz + if [ -e ${curFile} ]; then + rm -f ${toString prevFiles} + mv ${curFile} ${prevFile} fi - ${dumpCmd} | \ - gzip -c > ${cfg.location}/${db}.in-progress.sql.gz + ${dumpCmd} \ + | ${compressCmd} \ + > ${inProgressFile} - mv ${cfg.location}/${db}.in-progress.sql.gz ${cfg.location}/${db}.sql.gz + mv ${inProgressFile} ${curFile} ''; serviceConfig = { @@ -87,7 +108,7 @@ in { default = "/var/backup/postgresql"; type = types.path; description = '' - Location to put the gzipped PostgreSQL database dumps. + Path of directory where the PostgreSQL database dumps will be placed. ''; }; @@ -101,6 +122,14 @@ in { when no databases where specified. ''; }; + + compression = mkOption { + type = types.enum ["none" "gzip" "zstd"]; + default = "gzip"; + description = '' + The type of compression to use on the generated database dump. + ''; + }; }; }; diff --git a/nixpkgs/nixos/modules/services/backup/sanoid.nix b/nixpkgs/nixos/modules/services/backup/sanoid.nix index abc4def1c61f..41d0e2e1df68 100644 --- a/nixpkgs/nixos/modules/services/backup/sanoid.nix +++ b/nixpkgs/nixos/modules/services/backup/sanoid.nix @@ -52,7 +52,7 @@ let use_template = mkOption { description = "Names of the templates to use for this dataset."; type = types.listOf (types.enum (attrNames cfg.templates)); - default = []; + default = [ ]; }; useTemplate = use_template; @@ -70,116 +70,127 @@ let processChildrenOnly = process_children_only; }; - # Extract pool names from configured datasets - pools = unique (map (d: head (builtins.match "([^/]+).*" d)) (attrNames cfg.datasets)); - - configFile = let - mkValueString = v: - if builtins.isList v then concatStringsSep "," v - else generators.mkValueStringDefault {} v; - - mkKeyValue = k: v: if v == null then "" - else if k == "processChildrenOnly" then "" - else if k == "useTemplate" then "" - else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v; - in generators.toINI { inherit mkKeyValue; } cfg.settings; - -in { - - # Interface - - options.services.sanoid = { - enable = mkEnableOption "Sanoid ZFS snapshotting service"; - - interval = mkOption { - type = types.str; - default = "hourly"; - example = "daily"; - description = '' - Run sanoid at this interval. The default is to run hourly. - - The format is described in - <citerefentry><refentrytitle>systemd.time</refentrytitle> - <manvolnum>7</manvolnum></citerefentry>. - ''; - }; + # Extract unique dataset names + datasets = unique (attrNames cfg.datasets); + + # Function to build "zfs allow" and "zfs unallow" commands for the + # filesystems we've delegated permissions to. + buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [ + # Here we explicitly use the booted system to guarantee the stable API needed by ZFS + "-+/run/booted-system/sw/bin/zfs" + zfsAction + "sanoid" + (concatStringsSep "," permissions) + dataset + ]; + + configFile = + let + mkValueString = v: + if builtins.isList v then concatStringsSep "," v + else generators.mkValueStringDefault { } v; + + mkKeyValue = k: v: + if v == null then "" + else if k == "processChildrenOnly" then "" + else if k == "useTemplate" then "" + else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v; + in + generators.toINI { inherit mkKeyValue; } cfg.settings; + +in +{ + + # Interface + + options.services.sanoid = { + enable = mkEnableOption "Sanoid ZFS snapshotting service"; + + interval = mkOption { + type = types.str; + default = "hourly"; + example = "daily"; + description = '' + Run sanoid at this interval. The default is to run hourly. + + The format is described in + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; + }; - datasets = mkOption { - type = types.attrsOf (types.submodule ({config, options, ...}: { - freeformType = datasetSettingsType; - options = commonOptions // datasetOptions; - config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {}); - config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {}); - })); - default = {}; - description = "Datasets to snapshot."; - }; + datasets = mkOption { + type = types.attrsOf (types.submodule ({ config, options, ... }: { + freeformType = datasetSettingsType; + options = commonOptions // datasetOptions; + config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or { }); + config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or { }); + })); + default = { }; + description = "Datasets to snapshot."; + }; - templates = mkOption { - type = types.attrsOf (types.submodule { - freeformType = datasetSettingsType; - options = commonOptions; - }); - default = {}; - description = "Templates for datasets."; - }; + templates = mkOption { + type = types.attrsOf (types.submodule { + freeformType = datasetSettingsType; + options = commonOptions; + }); + default = { }; + description = "Templates for datasets."; + }; - settings = mkOption { - type = types.attrsOf datasetSettingsType; - description = '' - Free-form settings written directly to the config file. See - <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/> - for allowed values. - ''; - }; + settings = mkOption { + type = types.attrsOf datasetSettingsType; + description = '' + Free-form settings written directly to the config file. See + <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/> + for allowed values. + ''; + }; - extraArgs = mkOption { - type = types.listOf types.str; - default = []; - example = [ "--verbose" "--readonly" "--debug" ]; - description = '' - Extra arguments to pass to sanoid. See - <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/> - for allowed options. - ''; - }; + extraArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "--verbose" "--readonly" "--debug" ]; + description = '' + Extra arguments to pass to sanoid. See + <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/> + for allowed options. + ''; }; + }; - # Implementation - - config = mkIf cfg.enable { - services.sanoid.settings = mkMerge [ - (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates) - (mapAttrs (d: v: v) cfg.datasets) - ]; - - systemd.services.sanoid = { - description = "Sanoid snapshot service"; - serviceConfig = { - ExecStartPre = map (pool: lib.escapeShellArgs [ - "+/run/booted-system/sw/bin/zfs" "allow" - "sanoid" "snapshot,mount,destroy" pool - ]) pools; - ExecStart = lib.escapeShellArgs ([ - "${pkgs.sanoid}/bin/sanoid" - "--cron" - "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile) - ] ++ cfg.extraArgs); - ExecStopPost = map (pool: lib.escapeShellArgs [ - "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool - ]) pools; - User = "sanoid"; - Group = "sanoid"; - DynamicUser = true; - RuntimeDirectory = "sanoid"; - CacheDirectory = "sanoid"; - }; - # Prevents missing snapshots during DST changes - environment.TZ = "UTC"; - after = [ "zfs.target" ]; - startAt = cfg.interval; + # Implementation + + config = mkIf cfg.enable { + services.sanoid.settings = mkMerge [ + (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates) + (mapAttrs (d: v: v) cfg.datasets) + ]; + + systemd.services.sanoid = { + description = "Sanoid snapshot service"; + serviceConfig = { + ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets); + ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets); + ExecStart = lib.escapeShellArgs ([ + "${pkgs.sanoid}/bin/sanoid" + "--cron" + "--configdir" + (pkgs.writeTextDir "sanoid.conf" configFile) + ] ++ cfg.extraArgs); + User = "sanoid"; + Group = "sanoid"; + DynamicUser = true; + RuntimeDirectory = "sanoid"; + CacheDirectory = "sanoid"; }; + # Prevents missing snapshots during DST changes + environment.TZ = "UTC"; + after = [ "zfs.target" ]; + startAt = cfg.interval; }; + }; - meta.maintainers = with maintainers; [ lopsided98 ]; - } + meta.maintainers = with maintainers; [ lopsided98 ]; +} diff --git a/nixpkgs/nixos/modules/services/backup/syncoid.nix b/nixpkgs/nixos/modules/services/backup/syncoid.nix index b764db1f14e4..3ad8d279a36d 100644 --- a/nixpkgs/nixos/modules/services/backup/syncoid.nix +++ b/nixpkgs/nixos/modules/services/backup/syncoid.nix @@ -5,212 +5,366 @@ with lib; let cfg = config.services.syncoid; - # Extract pool names of local datasets (ones that don't contain "@") that - # have the specified type (either "source" or "target") - getPools = type: unique (map (d: head (builtins.match "([^/]+).*" d)) ( - # Filter local datasets - filter (d: !hasInfix "@" d) - # Get datasets of the specified type - (catAttrs type (attrValues cfg.commands)) - )); -in { - - # Interface - - options.services.syncoid = { - enable = mkEnableOption "Syncoid ZFS synchronization service"; - - interval = mkOption { - type = types.str; - default = "hourly"; - example = "*-*-* *:15:00"; - description = '' - Run syncoid at this interval. The default is to run hourly. - - The format is described in - <citerefentry><refentrytitle>systemd.time</refentrytitle> - <manvolnum>7</manvolnum></citerefentry>. - ''; - }; + # Extract local dasaset names (so no datasets containing "@") + localDatasetName = d: optionals (d != null) ( + let m = builtins.match "([^/@]+[^@]*)" d; in + optionals (m != null) m + ); - user = mkOption { - type = types.str; - default = "syncoid"; - example = "backup"; - description = '' - The user for the service. ZFS privilege delegation will be - automatically configured for any local pools used by syncoid if this - option is set to a user other than root. The user will be given the - "hold" and "send" privileges on any pool that has datasets being sent - and the "create", "mount", "receive", and "rollback" privileges on - any pool that has datasets being received. - ''; - }; + # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html + escapeUnitName = name: + lib.concatMapStrings (s: if lib.isList s then "-" else s) + (builtins.split "[^a-zA-Z0-9_.\\-]+" name); - group = mkOption { - type = types.str; - default = "syncoid"; - example = "backup"; - description = "The group for the service."; - }; + # Function to build "zfs allow" and "zfs unallow" commands for the + # filesystems we've delegated permissions to. + buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [ + # Here we explicitly use the booted system to guarantee the stable API needed by ZFS + "-+/run/booted-system/sw/bin/zfs" + zfsAction + cfg.user + (concatStringsSep "," permissions) + dataset + ]; +in +{ - sshKey = mkOption { - type = types.nullOr types.path; - # Prevent key from being copied to store - apply = mapNullable toString; - default = null; - description = '' - SSH private key file to use to login to the remote system. Can be - overridden in individual commands. - ''; - }; + # Interface - commonArgs = mkOption { - type = types.listOf types.str; - default = []; - example = [ "--no-sync-snap" ]; - description = '' - Arguments to add to every syncoid command, unless disabled for that - command. See - <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/> - for available options. - ''; - }; + options.services.syncoid = { + enable = mkEnableOption "Syncoid ZFS synchronization service"; - commands = mkOption { - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - source = mkOption { - type = types.str; - example = "pool/dataset"; - description = '' - Source ZFS dataset. Can be either local or remote. Defaults to - the attribute name. - ''; - }; + interval = mkOption { + type = types.str; + default = "hourly"; + example = "*-*-* *:15:00"; + description = '' + Run syncoid at this interval. The default is to run hourly. - target = mkOption { - type = types.str; - example = "user@server:pool/dataset"; - description = '' - Target ZFS dataset. Can be either local - (<replaceable>pool/dataset</replaceable>) or remote - (<replaceable>user@server:pool/dataset</replaceable>). - ''; - }; + The format is described in + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; + }; - recursive = mkOption { - type = types.bool; - default = false; - description = '' - Whether to also transfer child datasets. - ''; - }; + user = mkOption { + type = types.str; + default = "syncoid"; + example = "backup"; + description = '' + The user for the service. ZFS privilege delegation will be + automatically configured for any local pools used by syncoid if this + option is set to a user other than root. The user will be given the + "hold" and "send" privileges on any pool that has datasets being sent + and the "create", "mount", "receive", and "rollback" privileges on + any pool that has datasets being received. + ''; + }; - sshKey = mkOption { - type = types.nullOr types.path; - # Prevent key from being copied to store - apply = mapNullable toString; - description = '' - SSH private key file to use to login to the remote system. - Defaults to <option>services.syncoid.sshKey</option> option. - ''; - }; + group = mkOption { + type = types.str; + default = "syncoid"; + example = "backup"; + description = "The group for the service."; + }; - sendOptions = mkOption { - type = types.separatedString " "; - default = ""; - example = "Lc e"; - description = '' - Advanced options to pass to zfs send. Options are specified - without their leading dashes and separated by spaces. - ''; - }; + sshKey = mkOption { + type = types.nullOr types.path; + # Prevent key from being copied to store + apply = mapNullable toString; + default = null; + description = '' + SSH private key file to use to login to the remote system. Can be + overridden in individual commands. + ''; + }; - recvOptions = mkOption { - type = types.separatedString " "; - default = ""; - example = "ux recordsize o compression=lz4"; - description = '' - Advanced options to pass to zfs recv. Options are specified - without their leading dashes and separated by spaces. - ''; - }; + localSourceAllow = mkOption { + type = types.listOf types.str; + # Permissions snapshot and destroy are in case --no-sync-snap is not used + default = [ "bookmark" "hold" "send" "snapshot" "destroy" ]; + description = '' + Permissions granted for the <option>services.syncoid.user</option> user + for local source datasets. See + <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/> + for available permissions. + ''; + }; - useCommonArgs = mkOption { - type = types.bool; - default = true; - description = '' - Whether to add the configured common arguments to this command. - ''; - }; + localTargetAllow = mkOption { + type = types.listOf types.str; + default = [ "change-key" "compression" "create" "mount" "mountpoint" "receive" "rollback" ]; + example = [ "create" "mount" "receive" "rollback" ]; + description = '' + Permissions granted for the <option>services.syncoid.user</option> user + for local target datasets. See + <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/> + for available permissions. + Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets, + the <literal>compression</literal> permission if you send raw compressed datasets, and so on. + For remote target datasets you'll have to set your remote user permissions by yourself. + ''; + }; - extraArgs = mkOption { - type = types.listOf types.str; - default = []; - example = [ "--sshport 2222" ]; - description = "Extra syncoid arguments for this command."; - }; + commonArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "--no-sync-snap" ]; + description = '' + Arguments to add to every syncoid command, unless disabled for that + command. See + <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/> + for available options. + ''; + }; + + service = mkOption { + type = types.attrs; + default = { }; + description = '' + Systemd configuration common to all syncoid services. + ''; + }; + + commands = mkOption { + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + source = mkOption { + type = types.str; + example = "pool/dataset"; + description = '' + Source ZFS dataset. Can be either local or remote. Defaults to + the attribute name. + ''; }; - config = { - source = mkDefault name; - sshKey = mkDefault cfg.sshKey; + + target = mkOption { + type = types.str; + example = "user@server:pool/dataset"; + description = '' + Target ZFS dataset. Can be either local + (<replaceable>pool/dataset</replaceable>) or remote + (<replaceable>user@server:pool/dataset</replaceable>). + ''; }; - })); - default = {}; - example = literalExample '' - { - "pool/test".target = "root@target:pool/test"; - } - ''; - description = "Syncoid commands to run."; - }; - }; - # Implementation + recursive = mkEnableOption ''the transfer of child datasets''; + + sshKey = mkOption { + type = types.nullOr types.path; + # Prevent key from being copied to store + apply = mapNullable toString; + description = '' + SSH private key file to use to login to the remote system. + Defaults to <option>services.syncoid.sshKey</option> option. + ''; + }; - config = mkIf cfg.enable { - users = { - users = mkIf (cfg.user == "syncoid") { - syncoid = { - group = cfg.group; - isSystemUser = true; + localSourceAllow = mkOption { + type = types.listOf types.str; + description = '' + Permissions granted for the <option>services.syncoid.user</option> user + for local source datasets. See + <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/> + for available permissions. + Defaults to <option>services.syncoid.localSourceAllow</option> option. + ''; + }; + + localTargetAllow = mkOption { + type = types.listOf types.str; + description = '' + Permissions granted for the <option>services.syncoid.user</option> user + for local target datasets. See + <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/> + for available permissions. + Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets, + the <literal>compression</literal> permission if you send raw compressed datasets, and so on. + For remote target datasets you'll have to set your remote user permissions by yourself. + ''; + }; + + sendOptions = mkOption { + type = types.separatedString " "; + default = ""; + example = "Lc e"; + description = '' + Advanced options to pass to zfs send. Options are specified + without their leading dashes and separated by spaces. + ''; + }; + + recvOptions = mkOption { + type = types.separatedString " "; + default = ""; + example = "ux recordsize o compression=lz4"; + description = '' + Advanced options to pass to zfs recv. Options are specified + without their leading dashes and separated by spaces. + ''; + }; + + useCommonArgs = mkOption { + type = types.bool; + default = true; + description = '' + Whether to add the configured common arguments to this command. + ''; + }; + + service = mkOption { + type = types.attrs; + default = { }; + description = '' + Systemd configuration specific to this syncoid service. + ''; + }; + + extraArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "--sshport 2222" ]; + description = "Extra syncoid arguments for this command."; }; }; - groups = mkIf (cfg.group == "syncoid") { - syncoid = {}; + config = { + source = mkDefault name; + sshKey = mkDefault cfg.sshKey; + localSourceAllow = mkDefault cfg.localSourceAllow; + localTargetAllow = mkDefault cfg.localTargetAllow; }; - }; + })); + default = { }; + example = literalExample '' + { + "pool/test".target = "root@target:pool/test"; + } + ''; + description = "Syncoid commands to run."; + }; + }; + + # Implementation - systemd.services.syncoid = { - description = "Syncoid ZFS synchronization service"; - script = concatMapStringsSep "\n" (c: lib.escapeShellArgs - ([ "${pkgs.sanoid}/bin/syncoid" ] - ++ (optionals c.useCommonArgs cfg.commonArgs) - ++ (optional c.recursive "-r") - ++ (optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]) - ++ c.extraArgs - ++ [ "--sendoptions" c.sendOptions - "--recvoptions" c.recvOptions - "--no-privilege-elevation" - c.source c.target - ])) (attrValues cfg.commands); - after = [ "zfs.target" ]; - serviceConfig = { - ExecStartPre = let - allowCmd = permissions: pool: lib.escapeShellArgs [ - "+/run/booted-system/sw/bin/zfs" "allow" - cfg.user (concatStringsSep "," permissions) pool - ]; - in - (map (allowCmd [ "hold" "send" "snapshot" "destroy" ]) (getPools "source")) ++ - (map (allowCmd [ "create" "mount" "receive" "rollback" ]) (getPools "target")); - User = cfg.user; - Group = cfg.group; + config = mkIf cfg.enable { + users = { + users = mkIf (cfg.user == "syncoid") { + syncoid = { + group = cfg.group; + isSystemUser = true; + # For syncoid to be able to create /var/lib/syncoid/.ssh/ + # and to use custom ssh_config or known_hosts. + home = "/var/lib/syncoid"; + createHome = false; }; - startAt = cfg.interval; + }; + groups = mkIf (cfg.group == "syncoid") { + syncoid = { }; }; }; - meta.maintainers = with maintainers; [ lopsided98 ]; - } + systemd.services = mapAttrs' + (name: c: + nameValuePair "syncoid-${escapeUnitName name}" (mkMerge [ + { + description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}"; + after = [ "zfs.target" ]; + startAt = cfg.interval; + # syncoid may need zpool to get feature@extensible_dataset + path = [ "/run/booted-system/sw/bin/" ]; + serviceConfig = { + ExecStartPre = + (map (buildAllowCommand "allow" c.localSourceAllow) (localDatasetName c.source)) ++ + (map (buildAllowCommand "allow" c.localTargetAllow) (localDatasetName c.target)); + ExecStopPost = + (map (buildAllowCommand "unallow" c.localSourceAllow) (localDatasetName c.source)) ++ + (map (buildAllowCommand "unallow" c.localTargetAllow) (localDatasetName c.target)); + ExecStart = lib.escapeShellArgs ([ "${pkgs.sanoid}/bin/syncoid" ] + ++ optionals c.useCommonArgs cfg.commonArgs + ++ optional c.recursive "-r" + ++ optionals (c.sshKey != null) [ "--sshkey" c.sshKey ] + ++ c.extraArgs + ++ [ + "--sendoptions" + c.sendOptions + "--recvoptions" + c.recvOptions + "--no-privilege-elevation" + c.source + c.target + ]); + User = cfg.user; + Group = cfg.group; + StateDirectory = [ "syncoid" ]; + StateDirectoryMode = "700"; + # Prevent SSH control sockets of different syncoid services from interfering + PrivateTmp = true; + # Permissive access to /proc because syncoid + # calls ps(1) to detect ongoing `zfs receive`. + ProcSubset = "all"; + ProtectProc = "default"; + + # The following options are only for optimizing: + # systemd-analyze security | grep syncoid-'*' + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DeviceAllow = [ "/dev/zfs" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateNetwork = mkDefault false; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/syncoid/${escapeUnitName name}"; + RootDirectoryStartOnly = true; + BindPaths = [ "/dev/zfs" ]; + BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ]; + # Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace. + InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ]; + MountAPIVFS = true; + # Create RootDirectory= in the host's mount namespace. + RuntimeDirectory = [ "syncoid/${escapeUnitName name}" ]; + RuntimeDirectoryMode = "700"; + SystemCallFilter = [ + "@system-service" + # Groups in @system-service which do not contain a syscall listed by: + # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' syncoid … + # awk >perf.syscalls -F "," '$1 > 0 {sub("syscalls:sys_enter_","",$3); print $3}' perf.log + # systemd-analyze syscall-filter | grep -v -e '#' | sed -e ':loop; /^[^ ]/N; s/\n //; t loop' | grep $(printf ' -e \\<%s\\>' $(cat perf.syscalls)) | cut -f 1 -d ' ' + "~@aio" + "~@chown" + "~@keyring" + "~@memlock" + "~@privileged" + "~@resources" + "~@setuid" + "~@timer" + ]; + SystemCallArchitectures = "native"; + # This is for BindPaths= and BindReadOnlyPaths= + # to allow traversal of directories they create in RootDirectory=. + UMask = "0066"; + }; + } + cfg.service + c.service + ])) + cfg.commands; + }; + + meta.maintainers = with maintainers; [ julm lopsided98 ]; +} diff --git a/nixpkgs/nixos/modules/services/backup/znapzend.nix b/nixpkgs/nixos/modules/services/backup/znapzend.nix index 0ca71b413cee..debb2a397050 100644 --- a/nixpkgs/nixos/modules/services/backup/znapzend.nix +++ b/nixpkgs/nixos/modules/services/backup/znapzend.nix @@ -279,7 +279,7 @@ let src_plan = plan; tsformat = timestampFormat; zend_delay = toString sendDelay; - } // fold (a: b: a // b) {} ( + } // foldr (a: b: a // b) {} ( map mkDestAttrs (builtins.attrValues destinations) ); diff --git a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix index be3f40f6bd86..6c2df95886e7 100644 --- a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix +++ b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix @@ -83,8 +83,8 @@ let }; syncmode = mkOption { - type = types.enum [ "fast" "full" "light" ]; - default = "fast"; + type = types.enum [ "snap" "fast" "full" "light" ]; + default = "snap"; description = "Blockchain sync mode."; }; diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix index 1378b5ccfb7a..821f1aa54604 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix @@ -84,6 +84,9 @@ in Restart = "on-failure"; RestartSec = 10; }; + unitConfig = { + StartLimitIntervalSec = 0; + }; }; services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix index 24d86628b211..8f937a13231b 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix @@ -60,6 +60,45 @@ in { sha256 = "02r440xcdsgi137k5lmmvp0z5w5fmk8g9mysq5pnysq1wl8sj6mw"; }; }; + + corefile = mkOption { + description = '' + Custom coredns corefile configuration. + + See: <link xlink:href="https://coredns.io/manual/toc/#configuration"/>. + ''; + type = types.str; + default = '' + .:${toString ports.dns} { + errors + health :${toString ports.health} + kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :${toString ports.metrics} + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + }''; + defaultText = '' + .:${toString ports.dns} { + errors + health :${toString ports.health} + kubernetes ''${config.services.kubernetes.addons.dns.clusterDomain} in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :${toString ports.metrics} + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + }''; + }; }; config = mkIf cfg.enable { @@ -151,20 +190,7 @@ in { namespace = "kube-system"; }; data = { - Corefile = ".:${toString ports.dns} { - errors - health :${toString ports.health} - kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa { - pods insecure - fallthrough in-addr.arpa ip6.arpa - } - prometheus :${toString ports.metrics} - forward . /etc/resolv.conf - cache 30 - loop - reload - loadbalance - }"; + Corefile = cfg.corefile; }; }; diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix index f1531caa7544..2c89310beb5a 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix @@ -190,12 +190,6 @@ in type = nullOr path; }; - kubeletHttps = mkOption { - description = "Whether to use https for connections to kubelet."; - default = true; - type = bool; - }; - preferredAddressTypes = mkOption { description = "List of the preferred NodeAddressTypes to use for kubelet connections."; type = nullOr str; @@ -365,7 +359,6 @@ in "--feature-gates=${concatMapStringsSep "," (feature: "${feature}=true") cfg.featureGates}"} \ ${optionalString (cfg.basicAuthFile != null) "--basic-auth-file=${cfg.basicAuthFile}"} \ - --kubelet-https=${boolToString cfg.kubeletHttps} \ ${optionalString (cfg.kubeletClientCaFile != null) "--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"} \ ${optionalString (cfg.kubeletClientCertFile != null) @@ -405,6 +398,10 @@ in Restart = "on-failure"; RestartSec = 5; }; + + unitConfig = { + StartLimitIntervalSec = 0; + }; }; services.etcd = { diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix index 0c81fa9ae492..7128b5f70b1a 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix @@ -146,6 +146,9 @@ in User = "kubernetes"; Group = "kubernetes"; }; + unitConfig = { + StartLimitIntervalSec = 0; + }; path = top.path; }; diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix index 3f55719027f0..fecea7a15f3d 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix @@ -58,7 +58,7 @@ in services.kubernetes.addonManager.bootstrapAddons = mkIf ((storageBackend == "kubernetes") && (elem "RBAC" top.apiserver.authorizationMode)) { flannel-cr = { - apiVersion = "rbac.authorization.k8s.io/v1beta1"; + apiVersion = "rbac.authorization.k8s.io/v1"; kind = "ClusterRole"; metadata = { name = "flannel"; }; rules = [{ @@ -79,7 +79,7 @@ in }; flannel-crb = { - apiVersion = "rbac.authorization.k8s.io/v1beta1"; + apiVersion = "rbac.authorization.k8s.io/v1"; kind = "ClusterRoleBinding"; metadata = { name = "flannel"; }; roleRef = { diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix index fcfcc8435477..08f5cdfdf334 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix @@ -337,6 +337,9 @@ in ''; WorkingDirectory = top.dataDir; }; + unitConfig = { + StartLimitIntervalSec = 0; + }; }; # Allways include cni plugins diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix index d9311d3e3a04..faf951d81574 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix @@ -189,7 +189,7 @@ in # manually paste it in place. Just symlink. # otherwise, create the target file, ready for users to insert the token - mkdir -p $(dirname ${certmgrAPITokenPath}) + mkdir -p "$(dirname "${certmgrAPITokenPath}")" if [ -f "${cfsslAPITokenPath}" ]; then ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}" else diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix index 42729f54643b..a92043d52597 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix @@ -77,6 +77,9 @@ in Restart = "on-failure"; RestartSec = 5; }; + unitConfig = { + StartLimitIntervalSec = 0; + }; }; services.kubernetes.proxy.hostname = with config.networking; mkDefault hostName; diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix index 454c689759df..1b0c22a11426 100644 --- a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix +++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix @@ -79,6 +79,9 @@ in Restart = "on-failure"; RestartSec = 5; }; + unitConfig = { + StartLimitIntervalSec = 0; + }; }; services.kubernetes.pki.certs = { diff --git a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix index 9627b723f8f1..f951c1553235 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix @@ -98,6 +98,14 @@ in ''; default = [ ]; }; + + package = mkOption { + type = types.package; + description = '' + Which github-runner derivation to use. + ''; + default = pkgs.github-runner; + }; }; config = mkIf cfg.enable { @@ -131,7 +139,7 @@ in ] ++ cfg.extraPackages; serviceConfig = rec { - ExecStart = "${pkgs.github-runner}/bin/runsvc.sh"; + ExecStart = "${cfg.package}/bin/runsvc.sh"; # Does the following, sequentially: # - Copy the current and the previous `tokenFile` to the $RUNTIME_DIRECTORY @@ -208,7 +216,7 @@ in if [[ -z "$empty" ]]; then echo "Configuring GitHub Actions Runner" token=$(< "$RUNTIME_DIRECTORY"/${newConfigTokenFilename}) - RUNNER_ROOT="$STATE_DIRECTORY" ${pkgs.github-runner}/bin/config.sh \ + RUNNER_ROOT="$STATE_DIRECTORY" ${cfg.package}/bin/config.sh \ --unattended \ --work "$RUNTIME_DIRECTORY" \ --url ${escapeShellArg cfg.url} \ diff --git a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix index 2c6d9530a6b8..15c37c2bc76d 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix @@ -339,6 +339,9 @@ in <literal>CI_SERVER_URL=<CI server URL></literal> <literal>REGISTRATION_TOKEN=<registration secret></literal> + + WARNING: make sure to use quoted absolute path, + or it is going to be copied to Nix Store. ''; }; registrationFlags = mkOption { @@ -523,7 +526,10 @@ in }; }; config = mkIf cfg.enable { - warnings = optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`."; + warnings = (mapAttrsToList + (n: v: "services.gitlab-runner.services.${n}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.") + (filterAttrs (n: v: isStorePath v.registrationConfigFile) cfg.services)) + ++ optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`."; environment.systemPackages = [ cfg.package ]; systemd.services.gitlab-runner = { description = "Gitlab Runner"; diff --git a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix index 889688a26853..98ef1e2c691b 100644 --- a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix +++ b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix @@ -61,7 +61,7 @@ in { port = mkOption { default = 8080; - type = types.int; + type = types.port; description = '' Specifies port number on which the jenkins HTTP interface listens. The default is 8080. diff --git a/nixpkgs/nixos/modules/services/databases/influxdb2.nix b/nixpkgs/nixos/modules/services/databases/influxdb2.nix new file mode 100644 index 000000000000..df7bac4261b5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/databases/influxdb2.nix @@ -0,0 +1,53 @@ +{ config, lib, pkgs, ... }: +with lib; +let + format = pkgs.formats.json { }; + cfg = config.services.influxdb2; + configFile = format.generate "config.json" cfg.settings; +in +{ + options = { + services.influxdb2 = { + enable = mkEnableOption "the influxdb2 server"; + package = mkOption { + default = pkgs.influxdb2; + defaultText = "pkgs.influxdb2"; + description = "influxdb2 derivation to use."; + type = types.package; + }; + settings = mkOption { + default = { }; + description = "configuration options for influxdb2, see https://docs.influxdata.com/influxdb/v2.0/reference/config-options for details."; + type = format.type; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [{ + assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings); + message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd"; + }]; + systemd.services.influxdb2 = { + description = "InfluxDB is an open-source, distributed, time series database"; + documentation = [ "https://docs.influxdata.com/influxdb/" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + INFLUXD_CONFIG_PATH = "${configFile}"; + }; + serviceConfig = { + ExecStart = "${cfg.package}/bin/influxd --bolt-path \${STATE_DIRECTORY}/influxd.bolt --engine-path \${STATE_DIRECTORY}/engine"; + StateDirectory = "influxdb2"; + DynamicUser = true; + CapabilityBoundingSet = ""; + SystemCallFilter = "@system-service"; + LimitNOFILE = 65536; + KillMode = "control-group"; + Restart = "on-failure"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ nickcao ]; +} diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix index effc9182472e..fd4a195787f3 100644 --- a/nixpkgs/nixos/modules/services/databases/postgresql.nix +++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix @@ -293,7 +293,8 @@ in # Note: when changing the default, make it conditional on # ‘system.stateVersion’ to maintain compatibility with existing # systems! - mkDefault (if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11 + mkDefault (if versionAtLeast config.system.stateVersion "21.11" then pkgs.postgresql_13 + else if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11 else if versionAtLeast config.system.stateVersion "17.09" then pkgs.postgresql_9_6 else throw "postgresql_9_5 was removed, please upgrade your postgresql version."); diff --git a/nixpkgs/nixos/modules/services/databases/redis.nix b/nixpkgs/nixos/modules/services/databases/redis.nix index 9c0740f28c9b..8873f6d00e0b 100644 --- a/nixpkgs/nixos/modules/services/databases/redis.nix +++ b/nixpkgs/nixos/modules/services/databases/redis.nix @@ -272,7 +272,7 @@ in { } (mkIf (cfg.bind != null) { bind = cfg.bind; }) (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; }) - (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${cfg.slaveOf.port}"; }) + (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; }) (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; }) (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; }) ]; diff --git a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix index 5b09115bb2fb..9e2c79e61a39 100644 --- a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix +++ b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix @@ -53,6 +53,14 @@ let cfg = config.services.victoriametrics; in -retentionPeriod ${toString cfg.retentionPeriod} \ ${lib.escapeShellArgs cfg.extraOptions} ''; + # victoriametrics 1.59 with ~7GB of data seems to eventually panic when merging files and then + # begins restart-looping forever. Set LimitNOFILE= to a large number to work around this issue. + # + # panic: FATAL: unrecoverable error when merging small parts in the partition "/var/lib/victoriametrics/data/small/2021_08": + # cannot open source part for merging: cannot open values file in stream mode: + # cannot open file "/var/lib/victoriametrics/data/small/2021_08/[...]/values.bin": + # open /var/lib/victoriametrics/data/small/2021_08/[...]/values.bin: too many open files + LimitNOFILE = 1048576; }; wantedBy = [ "multi-user.target" ]; diff --git a/nixpkgs/nixos/modules/services/desktops/bamf.nix b/nixpkgs/nixos/modules/services/desktops/bamf.nix index 37121c219a37..13de3a44328f 100644 --- a/nixpkgs/nixos/modules/services/desktops/bamf.nix +++ b/nixpkgs/nixos/modules/services/desktops/bamf.nix @@ -5,8 +5,8 @@ with lib; { - meta = { - maintainers = with maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; ###### interface diff --git a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix index e9ec787e5ada..cb5c948ecf78 100644 --- a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix +++ b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix @@ -266,5 +266,7 @@ in } // mapAttrs' appConfigToINICompatible cfg.appConfig); }; - meta.maintainers = with lib.maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; + }; } diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json index 7c527b292158..46697ece4483 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json @@ -28,6 +28,12 @@ ] }, { + "name": "BAA 100", + "no-features": [ + "hw-volume" + ] + }, + { "name": "JBL Endurance RUN BT", "no-features": [ "msbc-alt1", @@ -191,6 +197,46 @@ ] }, { + "sysname": "Linux", + "release": "~^5\\.10\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50)($|[^0-9])" + }, + { + "sysname": "Linux", + "release": "~^5\\.10\\.", + "no-features": [ + "msbc-alt1" + ] + }, + { + "sysname": "Linux", + "release": "~^5\\.12\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)($|[^0-9])" + }, + { + "sysname": "Linux", + "release": "~^5\\.12\\.", + "no-features": [ + "msbc-alt1" + ] + }, + { + "sysname": "Linux", + "release": "~^5\\.13\\.(1|2)($|[^0-9])" + }, + { + "sysname": "Linux", + "release": "~^5\\.13\\.", + "no-features": [ + "msbc-alt1" + ] + }, + { + "sysname": "Linux", + "release": "~^5\\.14\\.", + "no-features": [ + "msbc-alt1" + ] + }, + { "no-features": [] } ] diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json index e36e04fffcf2..128178bfa027 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json @@ -24,5 +24,15 @@ "name": "libpipewire-module-metadata" } ], - "jack.properties": {} + "jack.properties": {}, + "jack.rules": [ + { + "matches": [ + {} + ], + "actions": { + "update-props": {} + } + } + ] } diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json index 24906e767d6d..4b4e302af387 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json @@ -59,6 +59,7 @@ "with-pulseaudio": [ "with-audio", "bluez5", + "bluez5-autoswitch", "logind", "restore-stream", "streams-follow-default" diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json index 17bbbdef1179..3ed994f11145 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json @@ -37,5 +37,6 @@ } } ], + "context.exec": [], "stream.properties": {} } diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix index dbd6c5d87e1a..bc75aa2717a9 100644 --- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -194,7 +194,7 @@ in { }; environment.sessionVariables.LD_LIBRARY_PATH = - lib.optional cfg.jack.enable "/run/current-system/sw/lib/pipewire"; + lib.optional cfg.jack.enable "${cfg.package.jack}/lib"; # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554 systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1"; diff --git a/nixpkgs/nixos/modules/services/desktops/tumbler.nix b/nixpkgs/nixos/modules/services/desktops/tumbler.nix index 8d9248cb9839..f5341df2f7a4 100644 --- a/nixpkgs/nixos/modules/services/desktops/tumbler.nix +++ b/nixpkgs/nixos/modules/services/desktops/tumbler.nix @@ -18,8 +18,8 @@ in "") ]; - meta = { - maintainers = with maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; ###### interface diff --git a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix index fb0218da3045..297fd1d3ff2f 100644 --- a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix +++ b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix @@ -6,8 +6,8 @@ with lib; { - meta = { - maintainers = with maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; ###### interface diff --git a/nixpkgs/nixos/modules/services/development/distccd.nix b/nixpkgs/nixos/modules/services/development/distccd.nix new file mode 100644 index 000000000000..8790ea08d0c1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/development/distccd.nix @@ -0,0 +1,155 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.distccd; +in +{ + options = { + services.distccd = { + enable = mkEnableOption "distccd"; + + allowedClients = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" ]; + example = [ "127.0.0.1" "192.168.0.0/24" "10.0.0.0/24" ]; + description = '' + Client IPs which are allowed to connect to distccd in CIDR notation. + + Anyone who can connect to the distccd server can run arbitrary + commands on that system as the distcc user, therefore you should use + this judiciously. + ''; + }; + + jobTimeout = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Maximum duration, in seconds, of a single compilation request. + ''; + }; + + logLevel = mkOption { + type = types.nullOr (types.enum [ "critical" "error" "warning" "notice" "info" "debug" ]); + default = "warning"; + description = '' + Set the minimum severity of error that will be included in the log + file. Useful if you only want to see error messages rather than an + entry for each connection. + ''; + }; + + maxJobs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Maximum number of tasks distccd should execute at any time. + ''; + }; + + + nice = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Niceness of the compilation tasks. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Opens the specified TCP port for distcc. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.distcc; + example = "pkgs.distcc"; + description = '' + The distcc package to use. + ''; + }; + + port = mkOption { + type = types.port; + default = 3632; + description = '' + The TCP port which distccd will listen on. + ''; + }; + + stats = { + enable = mkEnableOption "statistics reporting via HTTP server"; + port = mkOption { + type = types.port; + default = 3633; + description = '' + The TCP port which the distccd statistics HTTP server will listen + on. + ''; + }; + }; + + zeroconf = mkOption { + type = types.bool; + default = false; + description = '' + Whether to register via mDNS/DNS-SD + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ] + ++ optionals cfg.stats.enable [ cfg.stats.port ]; + }; + + systemd.services.distccd = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + description = "Distributed C, C++ and Objective-C compiler"; + documentation = [ "man:distccd(1)" ]; + + serviceConfig = { + User = "distcc"; + Group = "distcc"; + # FIXME: I'd love to get rid of `--enable-tcp-insecure` here, but I'm + # not sure how I'm supposed to get distccd to "accept" running a binary + # (the compiler) that's outside of /usr/lib. + ExecStart = pkgs.writeShellScript "start-distccd" '' + export PATH="${pkgs.distccMasquerade}/bin" + ${cfg.package}/bin/distccd \ + --no-detach \ + --daemon \ + --enable-tcp-insecure \ + --port ${toString cfg.port} \ + ${optionalString (cfg.jobTimeout != null) "--job-lifetime ${toString cfg.jobTimeout}"} \ + ${optionalString (cfg.logLevel != null) "--log-level ${cfg.logLevel}"} \ + ${optionalString (cfg.maxJobs != null) "--jobs ${toString cfg.maxJobs}"} \ + ${optionalString (cfg.nice != null) "--nice ${toString cfg.nice}"} \ + ${optionalString cfg.stats.enable "--stats"} \ + ${optionalString cfg.stats.enable "--stats-port ${toString cfg.stats.port}"} \ + ${optionalString cfg.zeroconf "--zeroconf"} \ + ${concatMapStrings (c: "--allow ${c} ") cfg.allowedClients} + ''; + }; + }; + + users = { + groups.distcc.gid = config.ids.gids.distcc; + users.distcc = { + description = "distccd user"; + group = "distcc"; + uid = config.ids.uids.distcc; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/development/hoogle.nix b/nixpkgs/nixos/modules/services/development/hoogle.nix index 6d6c88b9b2aa..a6693013b73c 100644 --- a/nixpkgs/nixos/modules/services/development/hoogle.nix +++ b/nixpkgs/nixos/modules/services/development/hoogle.nix @@ -17,7 +17,7 @@ in { enable = mkEnableOption "Haskell documentation server"; port = mkOption { - type = types.int; + type = types.port; default = 8080; description = '' Port number Hoogle will be listening to. diff --git a/nixpkgs/nixos/modules/services/hardware/sane.nix b/nixpkgs/nixos/modules/services/hardware/sane.nix index 8c1bde7b4158..ccf726bd182b 100644 --- a/nixpkgs/nixos/modules/services/hardware/sane.nix +++ b/nixpkgs/nixos/modules/services/hardware/sane.nix @@ -4,7 +4,10 @@ with lib; let - pkg = pkgs.sane-backends; + pkg = pkgs.sane-backends.override { + scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable; + scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package; + }; sanedConf = pkgs.writeTextFile { name = "saned.conf"; @@ -98,6 +101,28 @@ in ''; }; + hardware.sane.drivers.scanSnap.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether to enable drivers for the Fujitsu ScanSnap scanners. + + The driver files are unfree and extracted from the Windows driver image. + ''; + }; + + hardware.sane.drivers.scanSnap.package = mkOption { + type = types.package; + default = pkgs.sane-drivers.epjitsu; + description = '' + Epjitsu driver package to use. Useful if you want to extract the driver files yourself. + + The process is described in the <literal>/etc/sane.d/epjitsu.conf</literal> file in + the <literal>sane-backends</literal> package. + ''; + }; + services.saned.enable = mkOption { type = types.bool; default = false; diff --git a/nixpkgs/nixos/modules/services/mail/dovecot.nix b/nixpkgs/nixos/modules/services/mail/dovecot.nix index 1ccfb357750b..f3500f46e355 100644 --- a/nixpkgs/nixos/modules/services/mail/dovecot.nix +++ b/nixpkgs/nixos/modules/services/mail/dovecot.nix @@ -429,6 +429,7 @@ in startLimitIntervalSec = 60; # 1 min serviceConfig = { + Type = "notify"; ExecStart = "${dovecotPkg}/sbin/dovecot -F"; ExecReload = "${dovecotPkg}/sbin/doveadm reload"; Restart = "on-failure"; @@ -468,10 +469,6 @@ in assertions = [ { - assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != []; - message = "dovecot needs at least one of the IMAP or POP3 listeners enabled"; - } - { assertion = (cfg.sslServerCert == null) == (cfg.sslServerKey == null) && (cfg.sslCACert != null -> !(cfg.sslServerCert == null || cfg.sslServerKey == null)); message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto"; diff --git a/nixpkgs/nixos/modules/services/mail/nullmailer.nix b/nixpkgs/nixos/modules/services/mail/nullmailer.nix index 09874ca0ed75..f9c345669978 100644 --- a/nixpkgs/nixos/modules/services/mail/nullmailer.nix +++ b/nixpkgs/nixos/modules/services/mail/nullmailer.nix @@ -220,7 +220,7 @@ with lib; after = [ "network.target" ]; preStart = '' - mkdir -p /var/spool/nullmailer/{queue,tmp} + mkdir -p /var/spool/nullmailer/{queue,tmp,failed} rm -f /var/spool/nullmailer/trigger && mkfifo -m 660 /var/spool/nullmailer/trigger ''; diff --git a/nixpkgs/nixos/modules/services/mail/postfix.nix b/nixpkgs/nixos/modules/services/mail/postfix.nix index 35639e1bbc83..9b0a5bba2feb 100644 --- a/nixpkgs/nixos/modules/services/mail/postfix.nix +++ b/nixpkgs/nixos/modules/services/mail/postfix.nix @@ -194,7 +194,7 @@ let # We need to handle the last column specially here, because it's # open-ended (command + args). lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf); - in fold foldLine (genList (const 0) (length labels)) lines; + in foldr foldLine (genList (const 0) (length labels)) lines; # Pad a string with spaces from the right (opposite of fixedWidthString). pad = width: str: let @@ -203,7 +203,7 @@ let in str + optionalString (padWidth > 0) padding; # It's + 2 here, because that's the amount of spacing between columns. - fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths; + fullWidth = foldr (width: acc: acc + width + 2) 0 maxWidths; formatLine = line: concatStringsSep " " (zipListsWith pad maxWidths line); diff --git a/nixpkgs/nixos/modules/services/mail/postfixadmin.nix b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix new file mode 100644 index 000000000000..f5c8efb3076c --- /dev/null +++ b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix @@ -0,0 +1,199 @@ +{ lib, config, pkgs, ... }: + +with lib; + +let + cfg = config.services.postfixadmin; + fpm = config.services.phpfpm.pools.postfixadmin; + localDB = cfg.database.host == "localhost"; + user = if localDB then cfg.database.username else "nginx"; +in +{ + options.services.postfixadmin = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable postfixadmin. + + Also enables nginx virtual host management. + Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.<name></literal>. + See <xref linkend="opt-services.nginx.virtualHosts"/> for further information. + ''; + }; + + hostName = mkOption { + type = types.str; + example = "postfixadmin.example.com"; + description = "Hostname to use for the nginx vhost"; + }; + + adminEmail = mkOption { + type = types.str; + example = "postmaster@example.com"; + description = '' + Defines the Site Admin's email address. + This will be used to send emails from to create mailboxes and + from Send Email / Broadcast message pages. + ''; + }; + + setupPasswordFile = mkOption { + type = types.path; + description = '' + Password file for the admin. + Generate with <literal>php -r "echo password_hash('some password here', PASSWORD_DEFAULT);"</literal> + ''; + }; + + database = { + username = mkOption { + type = types.str; + default = "postfixadmin"; + description = '' + Username for the postgresql connection. + If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well. + ''; + }; + host = mkOption { + type = types.str; + default = "localhost"; + description = '' + Host of the postgresql server. If this is not set to + <literal>localhost</literal>, you have to create the + postgresql user and database yourself, with appropriate + permissions. + ''; + }; + passwordFile = mkOption { + type = types.path; + description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>."; + }; + dbname = mkOption { + type = types.str; + default = "postfixadmin"; + description = "Name of the postgresql database"; + }; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra configuration for the postfixadmin instance, see postfixadmin's config.inc.php for available options."; + }; + }; + + config = mkIf cfg.enable { + environment.etc."postfixadmin/config.local.php".text = '' + <?php + + $CONF['setup_password'] = file_get_contents('${cfg.setupPasswordFile}'); + + $CONF['database_type'] = 'pgsql'; + $CONF['database_host'] = ${if localDB then "null" else "'${cfg.database.host}'"}; + ${optionalString localDB "$CONF['database_user'] = '${cfg.database.username}';"} + $CONF['database_password'] = ${if localDB then "'dummy'" else "file_get_contents('${cfg.database.passwordFile}')"}; + $CONF['database_name'] = '${cfg.database.dbname}'; + $CONF['configured'] = true; + + ${cfg.extraConfig} + ''; + + systemd.tmpfiles.rules = [ "d /var/cache/postfixadmin/templates_c 700 ${user} ${user}" ]; + + services.nginx = { + enable = true; + virtualHosts = { + ${cfg.hostName} = { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."/" = { + root = "${pkgs.postfixadmin}/public"; + index = "index.php"; + extraConfig = '' + location ~* \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${fpm.socket}; + include ${pkgs.nginx}/conf/fastcgi_params; + include ${pkgs.nginx}/conf/fastcgi.conf; + } + ''; + }; + }; + }; + }; + + services.postgresql = mkIf localDB { + enable = true; + ensureUsers = [ { + name = cfg.database.username; + } ]; + }; + # The postgresql module doesn't currently support concepts like + # objects owners and extensions; for now we tack on what's needed + # here. + systemd.services.postfixadmin-postgres = let pgsql = config.services.postgresql; in mkIf localDB { + after = [ "postgresql.service" ]; + bindsTo = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ + pgsql.package + pkgs.util-linux + ]; + script = '' + set -eu + + PSQL() { + psql --port=${toString pgsql.port} "$@" + } + + PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.database.dbname}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.database.dbname}" OWNER "${cfg.database.username}"' + current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.database.dbname}'") + if [[ "$current_owner" != "${cfg.database.username}" ]]; then + PSQL -tAc 'ALTER DATABASE "${cfg.database.dbname}" OWNER TO "${cfg.database.username}"' + if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" ]]; then + echo "Reassigning ownership of database ${cfg.database.dbname} to user ${cfg.database.username} failed on last boot. Failing..." + exit 1 + fi + touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" + PSQL "${cfg.database.dbname}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.database.username}\"" + rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" + fi + ''; + + serviceConfig = { + User = pgsql.superUser; + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + users.users.${user} = mkIf localDB { + group = user; + isSystemUser = true; + createHome = false; + }; + users.groups.${user} = mkIf localDB {}; + + services.phpfpm.pools.postfixadmin = { + user = user; + phpPackage = pkgs.php74; + phpOptions = '' + error_log = 'stderr' + log_errors = on + ''; + settings = mapAttrs (name: mkDefault) { + "listen.owner" = "nginx"; + "listen.group" = "nginx"; + "listen.mode" = "0660"; + "pm" = "dynamic"; + "pm.max_children" = 75; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 1; + "pm.max_spare_servers" = 20; + "pm.max_requests" = 500; + "catch_workers_output" = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/mail/rspamd.nix b/nixpkgs/nixos/modules/services/mail/rspamd.nix index 473ddd52357d..c78f464235aa 100644 --- a/nixpkgs/nixos/modules/services/mail/rspamd.nix +++ b/nixpkgs/nixos/modules/services/mail/rspamd.nix @@ -371,8 +371,9 @@ in }; services.postfix.config = mkIf cfg.postfix.enable cfg.postfix.config; - systemd.services.postfix.serviceConfig.SupplementaryGroups = - mkIf cfg.postfix.enable [ postfixCfg.group ]; + systemd.services.postfix = mkIf cfg.postfix.enable { + serviceConfig.SupplementaryGroups = [ postfixCfg.group ]; + }; # Allow users to run 'rspamc' and 'rspamadm'. environment.systemPackages = [ pkgs.rspamd ]; diff --git a/nixpkgs/nixos/modules/services/misc/airsonic.nix b/nixpkgs/nixos/modules/services/misc/airsonic.nix index a572f1f6d6f5..490f6c5a5c06 100644 --- a/nixpkgs/nixos/modules/services/misc/airsonic.nix +++ b/nixpkgs/nixos/modules/services/misc/airsonic.nix @@ -82,6 +82,25 @@ in { ''; }; + jre = mkOption { + type = types.package; + default = pkgs.jre8; + defaultText = literalExample "pkgs.jre8"; + description = '' + JRE package to use. + + Airsonic only supports Java 8, airsonic-advanced requires at least + Java 11. + ''; + }; + + war = mkOption { + type = types.path; + default = "${pkgs.airsonic}/webapps/airsonic.war"; + defaultText = "\${pkgs.airsonic}/webapps/airsonic.war"; + description = "Airsonic war file to use."; + }; + jvmOptions = mkOption { description = '' Extra command line options for the JVM running AirSonic. @@ -118,7 +137,7 @@ in { ''; serviceConfig = { ExecStart = '' - ${pkgs.jre8}/bin/java -Xmx${toString cfg.maxMemory}m \ + ${cfg.jre}/bin/java -Xmx${toString cfg.maxMemory}m \ -Dairsonic.home=${cfg.home} \ -Dserver.address=${cfg.listenAddress} \ -Dserver.port=${toString cfg.port} \ @@ -128,7 +147,7 @@ in { "-Dserver.use-forward-headers=true"} \ ${toString cfg.jvmOptions} \ -verbose:gc \ - -jar ${pkgs.airsonic}/webapps/airsonic.war + -jar ${cfg.war} ''; Restart = "always"; User = "airsonic"; diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix index b6c1ca3e61a9..8322b7c09022 100644 --- a/nixpkgs/nixos/modules/services/misc/gitea.nix +++ b/nixpkgs/nixos/modules/services/misc/gitea.nix @@ -522,20 +522,16 @@ in (umask 027; gitea_setup) ''} + # run migrations/init the database + ${gitea}/bin/gitea migrate + # update all hooks' binary paths - HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*") - if [ "$HOOKS" ] - then - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS - fi + ${gitea}/bin/gitea admin regenerate hooks # update command option in authorized_keys if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] then - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys + ${gitea}/bin/gitea admin regenerate keys fi ''; diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix index 1514cc0665df..805deeee0c04 100644 --- a/nixpkgs/nixos/modules/services/misc/gitlab.nix +++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix @@ -117,6 +117,7 @@ let shared.path = "${cfg.statePath}/shared"; gitaly.client_path = "${cfg.packages.gitaly}/bin"; backup = { + gitaly_backup_path = "${cfg.packages.gitaly}/bin/gitaly-backup"; path = cfg.backup.path; keep_time = cfg.backup.keepTime; } // (optionalAttrs (cfg.backup.uploadOptions != {}) { @@ -1299,7 +1300,7 @@ in { Restart = "on-failure"; WorkingDirectory = gitlabEnv.HOME; ExecStart = - "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse " + "${cfg.packages.gitlab-workhorse}/bin/workhorse " + "-listenUmask 0 " + "-listenNetwork unix " + "-listenAddr /run/gitlab/gitlab-workhorse.socket " @@ -1352,9 +1353,8 @@ in { procps gnupg ]; - serviceConfig = { - Type = "simple"; + Type = "notify"; User = cfg.user; Group = cfg.group; TimeoutSec = "infinity"; diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix index dcd825bba433..73ec3b9a17a2 100644 --- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix +++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix @@ -78,7 +78,7 @@ in { port = mkOption { default = 8123; - type = types.int; + type = types.port; description = "The port on which to listen."; }; @@ -285,6 +285,7 @@ in { "alarmdecoder" "arduino" "blackbird" + "deconz" "dsmr" "edl21" "elkm1" diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix index 4930648ba8e3..e6b9dd234a9b 100644 --- a/nixpkgs/nixos/modules/services/misc/klipper.nix +++ b/nixpkgs/nixos/modules/services/misc/klipper.nix @@ -2,7 +2,13 @@ with lib; let cfg = config.services.klipper; - format = pkgs.formats.ini { mkKeyValue = generators.mkKeyValueDefault {} ":"; }; + format = pkgs.formats.ini { + # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996 + listToValue = l: + if builtins.length l == 1 then generators.mkValueStringDefault {} (head l) + else lib.concatMapStrings (s: "\n ${generators.mkValueStringDefault {} s}") l; + mkKeyValue = generators.mkKeyValueDefault {} ":"; + }; in { ##### interface @@ -24,8 +30,7 @@ in apiSocket = mkOption { type = types.nullOr types.path; - default = null; - example = "/run/klipper/api"; + default = "/run/klipper/api"; description = "Path of the API socket to create."; }; diff --git a/nixpkgs/nixos/modules/services/misc/libreddit.nix b/nixpkgs/nixos/modules/services/misc/libreddit.nix new file mode 100644 index 000000000000..77b34a856204 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/libreddit.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +with lib; + + let + cfg = config.services.libreddit; + + args = concatStringsSep " " ([ + "--port ${toString cfg.port}" + "--address ${cfg.address}" + ] ++ optional cfg.redirect "--redirect-https"); + +in +{ + options = { + services.libreddit = { + enable = mkEnableOption "Private front-end for Reddit"; + + address = mkOption { + default = "0.0.0.0"; + example = "127.0.0.1"; + type = types.str; + description = "The address to listen on"; + }; + + port = mkOption { + default = 8080; + example = 8000; + type = types.port; + description = "The port to listen on"; + }; + + redirect = mkOption { + type = types.bool; + default = false; + description = "Enable the redirecting to HTTPS"; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for the libreddit web interface"; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.libreddit = { + description = "Private front-end for Reddit"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.libreddit}/bin/libreddit ${args}"; + AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + Restart = "on-failure"; + RestartSec = "2s"; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix index a0a5973d30f2..02627e51c932 100644 --- a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix +++ b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix @@ -10,7 +10,7 @@ let jsonType = (pkgs.formats.json {}).type; - configFile = pkgs.runCommandNoCC "matrix-appservice-irc.yml" { + configFile = pkgs.runCommand "matrix-appservice-irc.yml" { # Because this program will be run at build time, we need `nativeBuildInputs` nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ]; preferLocalBuild = true; diff --git a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix index 3c734a948198..e150a1aaaad1 100644 --- a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix @@ -221,9 +221,10 @@ in { default = config.networking.hostName; description = '' The domain name of the server, with optional explicit port. - This is used by remote servers to connect to this server, - e.g. matrix.org, localhost:8080, etc. + This is used by remote servers to look up the server address. This is also the last part of your UserID. + + The server_name cannot be changed later so it is important to configure this correctly before you start Synapse. ''; }; public_baseurl = mkOption { diff --git a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix index 0ae5797fea04..717cf7936ead 100644 --- a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix +++ b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix @@ -128,7 +128,7 @@ in { # https://github.com/tulir/mautrix-telegram/issues/584 [ -f ${settingsFile} ] && rm -f ${settingsFile} old_umask=$(umask) - umask 0277 + umask 0177 ${pkgs.envsubst}/bin/envsubst \ -o ${settingsFile} \ -i ${settingsFileUnsubstituted} diff --git a/nixpkgs/nixos/modules/services/misc/moonraker.nix b/nixpkgs/nixos/modules/services/misc/moonraker.nix new file mode 100644 index 000000000000..de8668a0c066 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/moonraker.nix @@ -0,0 +1,135 @@ +{ config, lib, pkgs, ... }: +with lib; +let + pkg = pkgs.moonraker; + cfg = config.services.moonraker; + format = pkgs.formats.ini { + # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996 + listToValue = l: + if builtins.length l == 1 then generators.mkValueStringDefault {} (head l) + else lib.concatMapStrings (s: "\n ${generators.mkValueStringDefault {} s}") l; + mkKeyValue = generators.mkKeyValueDefault {} ":"; + }; +in { + options = { + services.moonraker = { + enable = mkEnableOption "Moonraker, an API web server for Klipper"; + + klipperSocket = mkOption { + type = types.path; + default = config.services.klipper.apiSocket; + description = "Path to Klipper's API socket."; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/moonraker"; + description = "The directory containing the Moonraker databases."; + }; + + configDir = mkOption { + type = types.path; + default = cfg.stateDir + "/config"; + description = '' + The directory containing client-writable configuration files. + + Clients will be able to edit files in this directory via the API. This directory must be writable. + ''; + }; + + user = mkOption { + type = types.str; + default = "moonraker"; + description = "User account under which Moonraker runs."; + }; + + group = mkOption { + type = types.str; + default = "moonraker"; + description = "Group account under which Moonraker runs."; + }; + + address = mkOption { + type = types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = "The IP or host to listen on."; + }; + + port = mkOption { + type = types.ints.unsigned; + default = 7125; + description = "The port to listen on."; + }; + + settings = mkOption { + type = format.type; + default = { }; + example = { + authorization = { + trusted_clients = [ "10.0.0.0/24" ]; + cors_domains = [ "https://app.fluidd.xyz" ]; + }; + }; + description = '' + Configuration for Moonraker. See the <link xlink:href="https://moonraker.readthedocs.io/en/latest/configuration/">documentation</link> + for supported values. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + warnings = optional (cfg.settings ? update_manager) + ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.''; + + users.users = optionalAttrs (cfg.user == "moonraker") { + moonraker = { + group = cfg.group; + uid = config.ids.uids.moonraker; + }; + }; + + users.groups = optionalAttrs (cfg.group == "moonraker") { + moonraker.gid = config.ids.gids.moonraker; + }; + + environment.etc."moonraker.cfg".source = let + forcedConfig = { + server = { + host = cfg.address; + port = cfg.port; + klippy_uds_address = cfg.klipperSocket; + config_path = cfg.configDir; + database_path = "${cfg.stateDir}/database"; + }; + }; + fullConfig = recursiveUpdate cfg.settings forcedConfig; + in format.generate "moonraker.cfg" fullConfig; + + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" + "d '${cfg.configDir}' - ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.moonraker = { + description = "Moonraker, an API web server for Klipper"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ] + ++ optional config.services.klipper.enable "klipper.service"; + + # Moonraker really wants its own config to be writable... + script = '' + cp /etc/moonraker.cfg ${cfg.configDir}/moonraker-temp.cfg + chmod u+w ${cfg.configDir}/moonraker-temp.cfg + exec ${pkg}/bin/moonraker -c ${cfg.configDir}/moonraker-temp.cfg + ''; + + serviceConfig = { + WorkingDirectory = cfg.stateDir; + Group = cfg.group; + User = cfg.user; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix new file mode 100644 index 000000000000..11116f7c3489 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix @@ -0,0 +1,120 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + dataDir = "/var/lib/mx-puppet-discord"; + registrationFile = "${dataDir}/discord-registration.yaml"; + cfg = config.services.mx-puppet-discord; + settingsFormat = pkgs.formats.json {}; + settingsFile = settingsFormat.generate "mx-puppet-discord-config.json" cfg.settings; + +in { + options = { + services.mx-puppet-discord = { + enable = mkEnableOption '' + mx-puppet-discord is a discord puppeting bridge for matrix. + It handles bridging private and group DMs, as well as Guilds (servers) + ''; + + settings = mkOption rec { + apply = recursiveUpdate default; + inherit (settingsFormat) type; + default = { + bridge.port = 8434; + presence = { + enabled = true; + interval = 500; + }; + provisioning.whitelist = [ ]; + relay.whitelist = [ ]; + + # variables are preceded by a colon. + namePatterns = { + user = ":name"; + userOverride = ":displayname"; + room = ":name"; + group = ":name"; + }; + + #defaults to sqlite but can be configured to use postgresql with + #connstring + database.filename = "${dataDir}/mx-puppet-discord/database.db"; + logging = { + console = "info"; + lineDateFormat = "MMM-D HH:mm:ss.SSS"; + }; + }; + example = literalExample '' + { + bridge = { + bindAddress = "localhost"; + domain = "example.com"; + homeserverUrl = "https://example.com"; + }; + + provisioning.whitelist = [ "@admin:example.com" ]; + relay.whitelist = [ "@.*:example.com" ]; + } + ''; + description = '' + <filename>config.yaml</filename> configuration as a Nix attribute set. + Configuration options should match those described in + <link xlink:href="https://github.com/matrix-discord/mx-puppet-discord/blob/master/sample.config.yaml"> + sample.config.yaml</link>. + ''; + }; + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable "matrix-synapse.service"; + description = '' + List of Systemd services to require and wait for when starting the application service. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.mx-puppet-discord = { + description = '' + mx-puppet-discord is a discord puppeting bridge for matrix. + It handles bridging private and group DMs, as well as Guilds (servers). + ''; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + + preStart = '' + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -r -c ${settingsFile} \ + -f ${registrationFile} + fi + ''; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + DynamicUser = true; + PrivateTmp = true; + WorkingDirectory = pkgs.mx-puppet-discord; + StateDirectory = baseNameOf dataDir; + UMask = 0027; + + ExecStart = '' + ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -c ${settingsFile} + ''; + }; + }; + }; + + meta.maintainers = with maintainers; [ govanify ]; +} diff --git a/nixpkgs/nixos/modules/services/misc/nitter.nix b/nixpkgs/nixos/modules/services/misc/nitter.nix new file mode 100644 index 000000000000..301af76c336a --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/nitter.nix @@ -0,0 +1,351 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.nitter; + configFile = pkgs.writeText "nitter.conf" '' + ${generators.toINI { + # String values need to be quoted + mkKeyValue = generators.mkKeyValueDefault { + mkValueString = v: + if isString v then "\"" + (strings.escape ["\""] (toString v)) + "\"" + else generators.mkValueStringDefault {} v; + } " = "; + } (lib.recursiveUpdate { + Server = cfg.server; + Cache = cfg.cache; + Config = cfg.config // { hmacKey = "@hmac@"; }; + Preferences = cfg.preferences; + } cfg.settings)} + ''; + # `hmac` is a secret used for cryptographic signing of video URLs. + # Generate it on first launch, then copy configuration and replace + # `@hmac@` with this value. + # We are not using sed as it would leak the value in the command line. + preStart = pkgs.writers.writePython3 "nitter-prestart" {} '' + import os + import secrets + + state_dir = os.environ.get("STATE_DIRECTORY") + if not os.path.isfile(f"{state_dir}/hmac"): + # Generate hmac on first launch + hmac = secrets.token_hex(32) + with open(f"{state_dir}/hmac", "w") as f: + f.write(hmac) + else: + # Load previously generated hmac + with open(f"{state_dir}/hmac", "r") as f: + hmac = f.read() + + configFile = "${configFile}" + with open(configFile, "r") as f_in: + with open(f"{state_dir}/nitter.conf", "w") as f_out: + f_out.write(f_in.read().replace("@hmac@", hmac)) + ''; +in +{ + options = { + services.nitter = { + enable = mkEnableOption "If enabled, start Nitter."; + + server = { + address = mkOption { + type = types.str; + default = "0.0.0.0"; + example = "127.0.0.1"; + description = "The address to listen on."; + }; + + port = mkOption { + type = types.port; + default = 8080; + example = 8000; + description = "The port to listen on."; + }; + + https = mkOption { + type = types.bool; + default = false; + description = "Set secure attribute on cookies. Keep it disabled to enable cookies when not using HTTPS."; + }; + + httpMaxConnections = mkOption { + type = types.int; + default = 100; + description = "Maximum number of HTTP connections."; + }; + + staticDir = mkOption { + type = types.path; + default = "${pkgs.nitter}/share/nitter/public"; + defaultText = "\${pkgs.nitter}/share/nitter/public"; + description = "Path to the static files directory."; + }; + + title = mkOption { + type = types.str; + default = "nitter"; + description = "Title of the instance."; + }; + + hostname = mkOption { + type = types.str; + default = "localhost"; + example = "nitter.net"; + description = "Hostname of the instance."; + }; + }; + + cache = { + listMinutes = mkOption { + type = types.int; + default = 240; + description = "How long to cache list info (not the tweets, so keep it high)."; + }; + + rssMinutes = mkOption { + type = types.int; + default = 10; + description = "How long to cache RSS queries."; + }; + + redisHost = mkOption { + type = types.str; + default = "localhost"; + description = "Redis host."; + }; + + redisPort = mkOption { + type = types.port; + default = 6379; + description = "Redis port."; + }; + + redisConnections = mkOption { + type = types.int; + default = 20; + description = "Redis connection pool size."; + }; + + redisMaxConnections = mkOption { + type = types.int; + default = 30; + description = '' + Maximum number of connections to Redis. + + New connections are opened when none are available, but if the + pool size goes above this, they are closed when released, do not + worry about this unless you receive tons of requests per second. + ''; + }; + }; + + config = { + base64Media = mkOption { + type = types.bool; + default = false; + description = "Use base64 encoding for proxied media URLs."; + }; + + tokenCount = mkOption { + type = types.int; + default = 10; + description = '' + Minimum amount of usable tokens. + + Tokens are used to authorize API requests, but they expire after + ~1 hour, and have a limit of 187 requests. The limit gets reset + every 15 minutes, and the pool is filled up so there is always at + least tokenCount usable tokens. Only increase this if you receive + major bursts all the time. + ''; + }; + }; + + preferences = { + replaceTwitter = mkOption { + type = types.str; + default = ""; + example = "nitter.net"; + description = "Replace Twitter links with links to this instance (blank to disable)."; + }; + + replaceYouTube = mkOption { + type = types.str; + default = ""; + example = "piped.kavin.rocks"; + description = "Replace YouTube links with links to this instance (blank to disable)."; + }; + + replaceInstagram = mkOption { + type = types.str; + default = ""; + description = "Replace Instagram links with links to this instance (blank to disable)."; + }; + + mp4Playback = mkOption { + type = types.bool; + default = true; + description = "Enable MP4 video playback."; + }; + + hlsPlayback = mkOption { + type = types.bool; + default = false; + description = "Enable HLS video streaming (requires JavaScript)."; + }; + + proxyVideos = mkOption { + type = types.bool; + default = true; + description = "Proxy video streaming through the server (might be slow)."; + }; + + muteVideos = mkOption { + type = types.bool; + default = false; + description = "Mute videos by default."; + }; + + autoplayGifs = mkOption { + type = types.bool; + default = true; + description = "Autoplay GIFs."; + }; + + theme = mkOption { + type = types.str; + default = "Nitter"; + description = "Instance theme."; + }; + + infiniteScroll = mkOption { + type = types.bool; + default = false; + description = "Infinite scrolling (requires JavaScript, experimental!)."; + }; + + stickyProfile = mkOption { + type = types.bool; + default = true; + description = "Make profile sidebar stick to top."; + }; + + bidiSupport = mkOption { + type = types.bool; + default = false; + description = "Support bidirectional text (makes clicking on tweets harder)."; + }; + + hideTweetStats = mkOption { + type = types.bool; + default = false; + description = "Hide tweet stats (replies, retweets, likes)."; + }; + + hideBanner = mkOption { + type = types.bool; + default = false; + description = "Hide profile banner."; + }; + + hidePins = mkOption { + type = types.bool; + default = false; + description = "Hide pinned tweets."; + }; + + hideReplies = mkOption { + type = types.bool; + default = false; + description = "Hide tweet replies."; + }; + }; + + settings = mkOption { + type = types.attrs; + default = {}; + description = '' + Add settings here to override NixOS module generated settings. + + Check the official repository for the available settings: + https://github.com/zedeus/nitter/blob/master/nitter.conf + ''; + }; + + redisCreateLocally = mkOption { + type = types.bool; + default = true; + description = "Configure local Redis server for Nitter."; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for Nitter web interface."; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !cfg.redisCreateLocally || (cfg.cache.redisHost == "localhost" && cfg.cache.redisPort == 6379); + message = "When services.nitter.redisCreateLocally is enabled, you need to use localhost:6379 as a cache server."; + } + ]; + + systemd.services.nitter = { + description = "Nitter (An alternative Twitter front-end)"; + wantedBy = [ "multi-user.target" ]; + after = [ "syslog.target" "network.target" ]; + serviceConfig = { + DynamicUser = true; + StateDirectory = "nitter"; + Environment = [ "NITTER_CONF_FILE=/var/lib/nitter/nitter.conf" ]; + # Some parts of Nitter expect `public` folder in working directory, + # see https://github.com/zedeus/nitter/issues/414 + WorkingDirectory = "${pkgs.nitter}/share/nitter"; + ExecStart = "${pkgs.nitter}/bin/nitter"; + ExecStartPre = "${preStart}"; + AmbientCapabilities = lib.mkIf (cfg.server.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + Restart = "on-failure"; + RestartSec = "5s"; + # Hardening + CapabilityBoundingSet = if (cfg.server.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + # A private user cannot have process capabilities on the host's user + # namespace and thus CAP_NET_BIND_SERVICE has no effect. + PrivateUsers = (cfg.server.port >= 1024); + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0077"; + }; + }; + + services.redis = lib.mkIf (cfg.redisCreateLocally) { + enable = true; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.server.port ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix index 133e96da0ec8..70b27b7d3d09 100644 --- a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix +++ b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix @@ -458,7 +458,7 @@ in description = "The flake reference to which <option>from></option> is to be rewritten."; }; flake = mkOption { - type = types.unspecified; + type = types.nullOr types.attrs; default = null; example = literalExample "nixpkgs"; description = '' diff --git a/nixpkgs/nixos/modules/services/misc/nzbget.nix b/nixpkgs/nixos/modules/services/misc/nzbget.nix index 715ec891cd68..27c5f2e395f6 100644 --- a/nixpkgs/nixos/modules/services/misc/nzbget.nix +++ b/nixpkgs/nixos/modules/services/misc/nzbget.nix @@ -7,24 +7,12 @@ let pkg = pkgs.nzbget; stateDir = "/var/lib/nzbget"; configFile = "${stateDir}/nzbget.conf"; - configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${value}") nixosOpts); - - nixosOpts = { - # allows nzbget to run as a "simple" service - OutputMode = "loggable"; - # use journald for logging - WriteLog = "none"; - ErrorTarget = "screen"; - WarningTarget = "screen"; - InfoTarget = "screen"; - DetailTarget = "screen"; - # required paths - ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf"; - WebDir = "${pkg}/share/nzbget/webui"; - # nixos handles package updates - UpdateCheck = "none"; - }; - + configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${escapeShellArg (toStr value)}") cfg.settings); + toStr = v: + if v == true then "yes" + else if v == false then "no" + else if isInt v then toString v + else v; in { imports = [ @@ -50,12 +38,41 @@ in default = "nzbget"; description = "Group under which NZBGet runs"; }; + + settings = mkOption { + type = with types; attrsOf (oneOf [ bool int str ]); + default = {}; + description = '' + NZBGet configuration, passed via command line using switch -o. Refer to + <link xlink:href="https://github.com/nzbget/nzbget/blob/master/nzbget.conf"/> + for details on supported values. + ''; + example = { + MainDir = "/data"; + }; + }; }; }; # implementation config = mkIf cfg.enable { + services.nzbget.settings = { + # allows nzbget to run as a "simple" service + OutputMode = "loggable"; + # use journald for logging + WriteLog = "none"; + ErrorTarget = "screen"; + WarningTarget = "screen"; + InfoTarget = "screen"; + DetailTarget = "screen"; + # required paths + ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf"; + WebDir = "${pkg}/share/nzbget/webui"; + # nixos handles package updates + UpdateCheck = "none"; + }; + systemd.services.nzbget = { description = "NZBGet Daemon"; after = [ "network.target" ]; @@ -64,6 +81,7 @@ in unrar p7zip ]; + preStart = '' if [ ! -f ${configFile} ]; then ${pkgs.coreutils}/bin/install -m 0700 ${pkg}/share/nzbget/nzbget.conf ${configFile} diff --git a/nixpkgs/nixos/modules/services/misc/octoprint.nix b/nixpkgs/nixos/modules/services/misc/octoprint.nix index c926d889b37a..7129ac69527f 100644 --- a/nixpkgs/nixos/modules/services/misc/octoprint.nix +++ b/nixpkgs/nixos/modules/services/misc/octoprint.nix @@ -122,6 +122,9 @@ in ExecStart = "${pluginsEnv}/bin/octoprint serve -b ${cfg.stateDir}"; User = cfg.user; Group = cfg.group; + SupplementaryGroups = [ + "dialout" + ]; }; }; diff --git a/nixpkgs/nixos/modules/services/misc/paperless-ng.nix b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix new file mode 100644 index 000000000000..4b7087e17f96 --- /dev/null +++ b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix @@ -0,0 +1,308 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.services.paperless-ng; + + defaultUser = "paperless"; + + env = { + PAPERLESS_DATA_DIR = cfg.dataDir; + PAPERLESS_MEDIA_ROOT = cfg.mediaDir; + PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir; + GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}"; + } // lib.mapAttrs (_: toString) cfg.extraConfig; + + manage = let + setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env); + in pkgs.writeShellScript "manage" '' + ${setupEnv} + exec ${cfg.package}/bin/paperless-ng "$@" + ''; + + # Secure the services + defaultServiceConfig = { + TemporaryFileSystem = "/:ro"; + BindReadOnlyPaths = [ + "/nix/store" + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/hosts" + "-/etc/localtime" + "-/run/postgresql" + ]; + BindPaths = [ + cfg.consumptionDir + cfg.dataDir + cfg.mediaDir + ]; + CapabilityBoundingSet = ""; + # ProtectClock adds DeviceAllow=char-rtc r + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + # Needs to connect to redis + # PrivateNetwork = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + # Breaks if the home dir of the user is in /home + # Also does not add much value in combination with the TemporaryFileSystem. + # ProtectHome = true; + ProtectHostname = true; + # Would re-mount paths ignored by temporary root + #ProtectSystem = "strict"; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ]; + # Does not work well with the temporary root + #UMask = "0066"; + }; +in +{ + meta.maintainers = with maintainers; [ earvstedt Flakebi ]; + + imports = [ + (mkRemovedOptionModule [ "services" "paperless"] '' + The paperless module has been removed as the upstream project died. + Users should migrate to the paperless-ng module (services.paperless-ng). + More information can be found in the NixOS 21.11 release notes. + '') + ]; + + options.services.paperless-ng = { + enable = mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable Paperless-ng. + + When started, the Paperless database is automatically created if it doesn't + exist and updated if the Paperless package has changed. + Both tasks are achieved by running a Django migration. + + A script to manage the Paperless instance (by wrapping Django's manage.py) is linked to + <literal>''${dataDir}/paperless-ng-manage</literal>. + ''; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/paperless"; + description = "Directory to store the Paperless data."; + }; + + mediaDir = mkOption { + type = types.str; + default = "${cfg.dataDir}/media"; + defaultText = "\${dataDir}/consume"; + description = "Directory to store the Paperless documents."; + }; + + consumptionDir = mkOption { + type = types.str; + default = "${cfg.dataDir}/consume"; + defaultText = "\${dataDir}/consume"; + description = "Directory from which new documents are imported."; + }; + + consumptionDirIsPublic = mkOption { + type = types.bool; + default = false; + description = "Whether all users can write to the consumption dir."; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/paperless-ng-password"; + description = '' + A file containing the superuser password. + + A superuser is required to access the web interface. + If unset, you can create a superuser manually by running + <literal>''${dataDir}/paperless-ng-manage createsuperuser</literal>. + + The default superuser name is <literal>admin</literal>. To change it, set + option <option>extraConfig.PAPERLESS_ADMIN_USER</option>. + WARNING: When changing the superuser name after the initial setup, the old superuser + will continue to exist. + + To disable login for the web interface, set the following: + <literal>extraConfig.PAPERLESS_AUTO_LOGIN_USERNAME = "admin";</literal>. + WARNING: Only use this on a trusted system without internet access to Paperless. + ''; + }; + + address = mkOption { + type = types.str; + default = "localhost"; + description = "Web interface address."; + }; + + port = mkOption { + type = types.port; + default = 28981; + description = "Web interface port."; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + description = '' + Extra paperless-ng config options. + + See <link xlink:href="https://paperless-ng.readthedocs.io/en/latest/configuration.html">the documentation</link> + for available options. + ''; + example = literalExample '' + { + PAPERLESS_OCR_LANGUAGE = "deu+eng"; + } + ''; + }; + + user = mkOption { + type = types.str; + default = defaultUser; + description = "User under which Paperless runs."; + }; + + package = mkOption { + type = types.package; + default = pkgs.paperless-ng; + defaultText = "pkgs.paperless-ng"; + description = "The Paperless package to use."; + }; + }; + + config = mkIf cfg.enable { + # Enable redis if no special url is set + services.redis.enable = mkIf (!hasAttr "PAPERLESS_REDIS" env) true; + + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -" + "d '${cfg.mediaDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -" + (if cfg.consumptionDirIsPublic then + "d '${cfg.consumptionDir}' 777 - - - -" + else + "d '${cfg.consumptionDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -" + ) + ]; + + systemd.services.paperless-ng-server = { + description = "Paperless document server"; + serviceConfig = defaultServiceConfig // { + User = cfg.user; + ExecStart = "${cfg.package}/bin/paperless-ng qcluster"; + Restart = "on-failure"; + }; + environment = env; + wantedBy = [ "multi-user.target" ]; + wants = [ "paperless-ng-consumer.service" "paperless-ng-web.service" ]; + + preStart = '' + ln -sf ${manage} ${cfg.dataDir}/paperless-ng-manage + + # Auto-migrate on first run or if the package has changed + versionFile="${cfg.dataDir}/src-version" + if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then + ${cfg.package}/bin/paperless-ng migrate + echo ${cfg.package} > "$versionFile" + fi + '' + + optionalString (cfg.passwordFile != null) '' + export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}" + export PAPERLESS_ADMIN_PASSWORD=$(cat "${cfg.dataDir}/superuser-password") + superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD" + superuserStateFile="${cfg.dataDir}/superuser-state" + + if [[ $(cat "$superuserStateFile" 2>/dev/null) != $superuserState ]]; then + ${cfg.package}/bin/paperless-ng manage_superuser + echo "$superuserState" > "$superuserStateFile" + fi + ''; + }; + + # Password copying can't be implemented as a privileged preStart script + # in 'paperless-ng-server' because 'defaultServiceConfig' limits the filesystem + # paths accessible by the service. + systemd.services.paperless-ng-copy-password = mkIf (cfg.passwordFile != null) { + requiredBy = [ "paperless-ng-server.service" ]; + before = [ "paperless-ng-server.service" ]; + serviceConfig = { + ExecStart = '' + ${pkgs.coreutils}/bin/install --mode 600 --owner '${cfg.user}' --compare \ + '${cfg.passwordFile}' '${cfg.dataDir}/superuser-password' + ''; + Type = "oneshot"; + }; + }; + + systemd.services.paperless-ng-consumer = { + description = "Paperless document consumer"; + serviceConfig = defaultServiceConfig // { + User = cfg.user; + ExecStart = "${cfg.package}/bin/paperless-ng document_consumer"; + Restart = "on-failure"; + }; + environment = env; + # Bind to `paperless-ng-server` so that the consumer never runs + # during migrations + bindsTo = [ "paperless-ng-server.service" ]; + after = [ "paperless-ng-server.service" ]; + }; + + systemd.services.paperless-ng-web = { + description = "Paperless web server"; + serviceConfig = defaultServiceConfig // { + User = cfg.user; + ExecStart = '' + ${pkgs.python3Packages.gunicorn}/bin/gunicorn \ + -c ${cfg.package}/lib/paperless-ng/gunicorn.conf.py paperless.asgi:application + ''; + Restart = "on-failure"; + + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + # gunicorn needs setuid + SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid" ]; + }; + environment = env // { + PATH = mkForce cfg.package.path; + PYTHONPATH = "${cfg.package.pythonPath}:${cfg.package}/lib/paperless-ng/src"; + }; + # Allow the web interface to access the private /tmp directory of the server. + # This is required to support uploading files via the web interface. + unitConfig.JoinsNamespaceOf = "paperless-ng-server.service"; + # Bind to `paperless-ng-server` so that the web server never runs + # during migrations + bindsTo = [ "paperless-ng-server.service" ]; + after = [ "paperless-ng-server.service" ]; + }; + + users = optionalAttrs (cfg.user == defaultUser) { + users.${defaultUser} = { + group = defaultUser; + uid = config.ids.uids.paperless; + home = cfg.dataDir; + }; + + groups.${defaultUser} = { + gid = config.ids.gids.paperless; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix deleted file mode 100644 index 43730b80eb2c..000000000000 --- a/nixpkgs/nixos/modules/services/misc/paperless.nix +++ /dev/null @@ -1,183 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; -let - cfg = config.services.paperless; - - defaultUser = "paperless"; - - manage = cfg.package.withConfig { - config = { - PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir; - PAPERLESS_INLINE_DOC = "true"; - PAPERLESS_DISABLE_LOGIN = "true"; - } // cfg.extraConfig; - inherit (cfg) dataDir ocrLanguages; - paperlessPkg = cfg.package; - }; -in -{ - options.services.paperless = { - enable = mkOption { - type = lib.types.bool; - default = false; - description = '' - Enable Paperless. - - When started, the Paperless database is automatically created if it doesn't - exist and updated if the Paperless package has changed. - Both tasks are achieved by running a Django migration. - ''; - }; - - dataDir = mkOption { - type = types.str; - default = "/var/lib/paperless"; - description = "Directory to store the Paperless data."; - }; - - consumptionDir = mkOption { - type = types.str; - default = "${cfg.dataDir}/consume"; - defaultText = "\${dataDir}/consume"; - description = "Directory from which new documents are imported."; - }; - - consumptionDirIsPublic = mkOption { - type = types.bool; - default = false; - description = "Whether all users can write to the consumption dir."; - }; - - ocrLanguages = mkOption { - type = with types; nullOr (listOf str); - default = null; - description = '' - Languages available for OCR via Tesseract, specified as - <literal>ISO 639-2/T</literal> language codes. - If unset, defaults to all available languages. - ''; - example = [ "eng" "spa" "jpn" ]; - }; - - address = mkOption { - type = types.str; - default = "localhost"; - description = "Server listening address."; - }; - - port = mkOption { - type = types.port; - default = 28981; - description = "Server port to listen on."; - }; - - extraConfig = mkOption { - type = types.attrs; - default = {}; - description = '' - Extra paperless config options. - - The config values are evaluated as double-quoted Bash string literals. - - See <literal>paperless-src/paperless.conf.example</literal> for available options. - - To enable user authentication, set <literal>PAPERLESS_DISABLE_LOGIN = "false"</literal> - and run the shell command <literal>$dataDir/paperless-manage createsuperuser</literal>. - - To define secret options without storing them in /nix/store, use the following pattern: - <literal>PAPERLESS_PASSPHRASE = "$(< /etc/my_passphrase_file)"</literal> - ''; - example = literalExample '' - { - PAPERLESS_OCR_LANGUAGE = "deu"; - } - ''; - }; - - user = mkOption { - type = types.str; - default = defaultUser; - description = "User under which Paperless runs."; - }; - - package = mkOption { - type = types.package; - default = pkgs.paperless; - defaultText = "pkgs.paperless"; - description = "The Paperless package to use."; - }; - - manage = mkOption { - type = types.package; - readOnly = true; - default = manage; - description = '' - A script to manage the Paperless instance. - It wraps Django's manage.py and is also available at - <literal>$dataDir/manage-paperless</literal> - ''; - }; - }; - - config = mkIf cfg.enable { - - systemd.tmpfiles.rules = [ - "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -" - ] ++ (optional cfg.consumptionDirIsPublic - "d '${cfg.consumptionDir}' 777 - - - -" - # If the consumption dir is not created here, it's automatically created by - # 'manage' with the default permissions. - ); - - systemd.services.paperless-consumer = { - description = "Paperless document consumer"; - serviceConfig = { - User = cfg.user; - ExecStart = "${manage} document_consumer"; - Restart = "always"; - }; - after = [ "systemd-tmpfiles-setup.service" ]; - wantedBy = [ "multi-user.target" ]; - preStart = '' - if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then - ln -sf ${manage} ${cfg.dataDir}/paperless-manage - fi - - ${manage.setupEnv} - # Auto-migrate on first run or if the package has changed - versionFile="$PAPERLESS_DBDIR/src-version" - if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then - python $paperlessSrc/manage.py migrate - echo ${cfg.package} > "$versionFile" - fi - ''; - }; - - systemd.services.paperless-server = { - description = "Paperless document server"; - serviceConfig = { - User = cfg.user; - ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}"; - Restart = "always"; - }; - # Bind to `paperless-consumer` so that the server never runs - # during migrations - bindsTo = [ "paperless-consumer.service" ]; - after = [ "paperless-consumer.service" ]; - wantedBy = [ "multi-user.target" ]; - }; - - users = optionalAttrs (cfg.user == defaultUser) { - users.${defaultUser} = { - group = defaultUser; - uid = config.ids.uids.paperless; - home = cfg.dataDir; - }; - - groups.${defaultUser} = { - gid = config.ids.gids.paperless; - }; - }; - }; -} diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix index a17a1010dbf7..e446f08284f7 100644 --- a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix +++ b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix @@ -84,7 +84,7 @@ in (rev: archs: lib.attrsets.mapAttrsToList (arch: image: - pkgs.runCommandNoCC "buildsrht-images" { } '' + pkgs.runCommand "buildsrht-images" { } '' mkdir -p $out/${distro}/${rev}/${arch} ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2 '') @@ -97,7 +97,7 @@ in "${pkgs.sourcehut.buildsrht}/lib/images" ]; }; - image_dir = pkgs.runCommandNoCC "builds.sr.ht-worker-images" { } '' + image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } '' mkdir -p $out/images cp -Lr ${image_dir_pre}/* $out/images ''; diff --git a/nixpkgs/nixos/modules/services/misc/uhub.nix b/nixpkgs/nixos/modules/services/misc/uhub.nix index d1b388310280..da2613e6db17 100644 --- a/nixpkgs/nixos/modules/services/misc/uhub.nix +++ b/nixpkgs/nixos/modules/services/misc/uhub.nix @@ -3,178 +3,110 @@ with lib; let - - cfg = config.services.uhub; - - uhubPkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; }; - - pluginConfig = "" - + optionalString cfg.plugins.authSqlite.enable '' - plugin ${uhubPkg.mod_auth_sqlite}/mod_auth_sqlite.so "file=${cfg.plugins.authSqlite.file}" - '' - + optionalString cfg.plugins.logging.enable '' - plugin ${uhubPkg.mod_logging}/mod_logging.so ${if cfg.plugins.logging.syslog then "syslog=true" else "file=${cfg.plugins.logging.file}"} - '' - + optionalString cfg.plugins.welcome.enable '' - plugin ${uhubPkg.mod_welcome}/mod_welcome.so "motd=${pkgs.writeText "motd.txt" cfg.plugins.welcome.motd} rules=${pkgs.writeText "rules.txt" cfg.plugins.welcome.rules}" - '' - + optionalString cfg.plugins.history.enable '' - plugin ${uhubPkg.mod_chat_history}/mod_chat_history.so "history_max=${toString cfg.plugins.history.max} history_default=${toString cfg.plugins.history.default} history_connect=${toString cfg.plugins.history.connect}" - ''; - - uhubConfigFile = pkgs.writeText "uhub.conf" '' - file_acl=${pkgs.writeText "users.conf" cfg.aclConfig} - file_plugins=${pkgs.writeText "plugins.conf" pluginConfig} - server_bind_addr=${cfg.address} - server_port=${toString cfg.port} - ${lib.optionalString cfg.enableTLS "tls_enable=yes"} - ${cfg.hubConfig} - ''; - -in - -{ + settingsFormat = { + type = with lib.types; attrsOf (oneOf [ bool int str ]); + generate = name: attrs: + pkgs.writeText name (lib.strings.concatStringsSep "\n" + (lib.attrsets.mapAttrsToList + (key: value: "${key}=${builtins.toJSON value}") attrs)); + }; +in { options = { - services.uhub = { - - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable the uhub ADC hub."; - }; - - port = mkOption { - type = types.int; - default = 1511; - description = "TCP port to bind the hub to."; - }; - - address = mkOption { - type = types.str; - default = "any"; - description = "Address to bind the hub to."; - }; - - enableTLS = mkOption { - type = types.bool; - default = false; - description = "Whether to enable TLS support."; - }; + services.uhub = mkOption { + default = { }; + description = "Uhub ADC hub instances"; + type = types.attrsOf (types.submodule { + options = { - hubConfig = mkOption { - type = types.lines; - default = ""; - description = "Contents of uhub configuration file."; - }; + enable = mkEnableOption "hub instance" // { default = true; }; - aclConfig = mkOption { - type = types.lines; - default = ""; - description = "Contents of user ACL configuration file."; - }; - - plugins = { - - authSqlite = { - enable = mkOption { + enableTLS = mkOption { type = types.bool; default = false; - description = "Whether to enable the Sqlite authentication database plugin"; - }; - file = mkOption { - type = types.path; - example = "/var/db/uhub-users"; - description = "Path to user database. Use the uhub-passwd utility to create the database and add/remove users."; + description = "Whether to enable TLS support."; }; - }; - logging = { - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable the logging plugin."; - }; - file = mkOption { - type = types.str; - default = ""; - description = "Path of log file."; - }; - syslog = mkOption { - type = types.bool; - default = false; - description = "If true then the system log is used instead of writing to file."; - }; - }; - - welcome = { - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable the welcome plugin."; - }; - motd = mkOption { - default = ""; - type = types.lines; + settings = mkOption { + inherit (settingsFormat) type; description = '' - Welcome message displayed to clients after connecting - and with the <literal>!motd</literal> command. + Configuration of uhub. + See https://www.uhub.org/doc/config.php for a list of options. ''; + default = { }; + example = { + server_bind_addr = "any"; + server_port = 1511; + hub_name = "My Public Hub"; + hub_description = "Yet another ADC hub"; + max_users = 150; + }; }; - rules = mkOption { - default = ""; - type = types.lines; - description = '' - Rules message, displayed to clients with the <literal>!rules</literal> command. - ''; - }; - }; - history = { - enable = mkOption { - type = types.bool; - default = false; - description = "Whether to enable the history plugin."; + plugins = mkOption { + description = "Uhub plugin configuration."; + type = with types; + listOf (submodule { + options = { + plugin = mkOption { + type = path; + example = literalExample + "$${pkgs.uhub}/plugins/mod_auth_sqlite.so"; + description = "Path to plugin file."; + }; + settings = mkOption { + description = "Settings specific to this plugin."; + type = with types; attrsOf str; + example = { file = "/etc/uhub/users.db"; }; + }; + }; + }); + default = [ ]; }; - max = mkOption { - type = types.int; - default = 200; - description = "The maximum number of messages to keep in history"; - }; - default = mkOption { - type = types.int; - default = 10; - description = "When !history is provided without arguments, then this default number of messages are returned."; - }; - connect = mkOption { - type = types.int; - default = 5; - description = "The number of chat history messages to send when users connect (0 = do not send any history)."; - }; - }; - }; + }; + }); }; }; - config = mkIf cfg.enable { - - users = { - users.uhub.uid = config.ids.uids.uhub; - groups.uhub.gid = config.ids.gids.uhub; - }; - - systemd.services.uhub = { - description = "high performance peer-to-peer hub for the ADC network"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "notify"; - ExecStart = "${uhubPkg}/bin/uhub -c ${uhubConfigFile} -u uhub -g uhub -L"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + config = let + hubs = lib.attrsets.filterAttrs (_: cfg: cfg.enable) config.services.uhub; + in { + + environment.etc = lib.attrsets.mapAttrs' (name: cfg: + let + settings' = cfg.settings // { + tls_enable = cfg.enableTLS; + file_plugins = pkgs.writeText "uhub-plugins.conf" + (lib.strings.concatStringsSep "\n" (map ({ plugin, settings }: + "plugin ${plugin} ${ + toString + (lib.attrsets.mapAttrsToList (key: value: ''"${key}=${value}"'') + settings) + }") cfg.plugins)); + }; + in { + name = "uhub/${name}.conf"; + value.source = settingsFormat.generate "uhub-${name}.conf" settings'; + }) hubs; + + systemd.services = lib.attrsets.mapAttrs' (name: cfg: { + name = "uhub-${name}"; + value = let pkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; }; + in { + description = "high performance peer-to-peer hub for the ADC network"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + serviceConfig = { + Type = "notify"; + ExecStart = "${pkg}/bin/uhub -c /etc/uhub/${name}.conf -L"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + DynamicUser = true; + }; }; - }; + }) hubs; }; } diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix index e0b2624b6cac..fb67bbfb8420 100644 --- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix +++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix @@ -6,6 +6,8 @@ let cfg = config.services.grafana; opt = options.services.grafana; declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins); + useMysql = cfg.database.type == "mysql"; + usePostgresql = cfg.database.type == "postgres"; envOptions = { PATHS_DATA = cfg.dataDir; @@ -635,7 +637,7 @@ in { systemd.services.grafana = { description = "Grafana Service Daemon"; wantedBy = ["multi-user.target"]; - after = ["networking.target"]; + after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service"; environment = { QT_QPA_PLATFORM = "offscreen"; } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions; diff --git a/nixpkgs/nixos/modules/services/monitoring/nagios.nix b/nixpkgs/nixos/modules/services/monitoring/nagios.nix index 61214508a9c6..0afaefe04e18 100644 --- a/nixpkgs/nixos/modules/services/monitoring/nagios.nix +++ b/nixpkgs/nixos/modules/services/monitoring/nagios.nix @@ -102,8 +102,8 @@ in plugins = mkOption { type = types.listOf types.package; - default = with pkgs; [ nagiosPluginsOfficial ssmtp mailutils ]; - defaultText = "[pkgs.nagiosPluginsOfficial pkgs.ssmtp pkgs.mailutils]"; + default = with pkgs; [ monitoring-plugins ssmtp mailutils ]; + defaultText = "[pkgs.monitoring-plugins pkgs.ssmtp pkgs.mailutils]"; description = " Packages to be added to the Nagios <envar>PATH</envar>. Typically used to add plugins, but can be anything. diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix index 3be247ffb24e..1161d18ab14b 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix @@ -10,7 +10,7 @@ let # a wrapper that verifies that the configuration is valid promtoolCheck = what: name: file: if cfg.checkConfig then - pkgs.runCommandNoCCLocal + pkgs.runCommandLocal "${name}-${replaceStrings [" "] [""] what}-checked" { buildInputs = [ cfg.package ]; } '' ln -s ${file} $out @@ -19,7 +19,7 @@ let # Pretty-print JSON to a file writePrettyJSON = name: x: - pkgs.runCommandNoCCLocal name {} '' + pkgs.runCommandLocal name {} '' echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out ''; diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix index d648de6a4148..9182c2f2ed87 100644 --- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -33,6 +33,7 @@ let "domain" "dovecot" "fritzbox" + "influxdb" "json" "jitsi" "kea" diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix new file mode 100644 index 000000000000..ba45173e946a --- /dev/null +++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.influxdb; +in +{ + port = 9122; + extraOpts = { + sampleExpiry = mkOption { + type = types.str; + default = "5m"; + example = "10m"; + description = "How long a sample is valid for"; + }; + udpBindAddress = mkOption { + type = types.str; + default = ":9122"; + example = "192.0.2.1:9122"; + description = "Address on which to listen for udp packets"; + }; + }; + serviceOpts = { + serviceConfig = { + RuntimeDirectory = "prometheus-influxdb-exporter"; + ExecStart = '' + ${pkgs.prometheus-influxdb-exporter}/bin/influxdb_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --influxdb.sample-expiry ${cfg.sampleExpiry} ${concatStringsSep " " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/monitoring/thanos.nix b/nixpkgs/nixos/modules/services/monitoring/thanos.nix index 474ea4b25054..96addf392bd7 100644 --- a/nixpkgs/nixos/modules/services/monitoring/thanos.nix +++ b/nixpkgs/nixos/modules/services/monitoring/thanos.nix @@ -63,7 +63,7 @@ let }; }; - toYAML = name: attrs: pkgs.runCommandNoCC name { + toYAML = name: attrs: pkgs.runCommand name { preferLocalBuild = true; json = builtins.toFile "${name}.json" (builtins.toJSON attrs); nativeBuildInputs = [ pkgs.remarshal ]; diff --git a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix index 2748571be1f7..57f5f6b006c8 100644 --- a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix +++ b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix @@ -5,36 +5,41 @@ let opt = options.services.ipfs; ipfsFlags = toString ([ - (optionalString cfg.autoMount "--mount") - (optionalString cfg.enableGC "--enable-gc") - (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false") - (optionalString (cfg.defaultMode == "offline") "--offline") + (optionalString cfg.autoMount "--mount") + (optionalString cfg.enableGC "--enable-gc") + (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false") + (optionalString (cfg.defaultMode == "offline") "--offline") (optionalString (cfg.defaultMode == "norouting") "--routing=none") ] ++ cfg.extraFlags); splitMulitaddr = addrRaw: lib.tail (lib.splitString "/" addrRaw); - multiaddrToListenStream = addrRaw: let + multiaddrToListenStream = addrRaw: + let addr = splitMulitaddr addrRaw; s = builtins.elemAt addr; - in if s 0 == "ip4" && s 2 == "tcp" - then "${s 1}:${s 3}" + in + if s 0 == "ip4" && s 2 == "tcp" + then "${s 1}:${s 3}" else if s 0 == "ip6" && s 2 == "tcp" - then "[${s 1}]:${s 3}" + then "[${s 1}]:${s 3}" else if s 0 == "unix" - then "/${lib.concatStringsSep "/" (lib.tail addr)}" + then "/${lib.concatStringsSep "/" (lib.tail addr)}" else null; # not valid for listen stream, skip - multiaddrToListenDatagram = addrRaw: let + multiaddrToListenDatagram = addrRaw: + let addr = splitMulitaddr addrRaw; s = builtins.elemAt addr; - in if s 0 == "ip4" && s 2 == "udp" - then "${s 1}:${s 3}" + in + if s 0 == "ip4" && s 2 == "udp" + then "${s 1}:${s 3}" else if s 0 == "ip6" && s 2 == "udp" - then "[${s 1}]:${s 3}" + then "[${s 1}]:${s 3}" else null; # not valid for listen datagram, skip -in { +in +{ ###### interface @@ -65,9 +70,10 @@ in { dataDir = mkOption { type = types.str; - default = if versionAtLeast config.system.stateVersion "17.09" - then "/var/lib/ipfs" - else "/var/lib/ipfs/.ipfs"; + default = + if versionAtLeast config.system.stateVersion "17.09" + then "/var/lib/ipfs" + else "/var/lib/ipfs/.ipfs"; description = "The data dir for IPFS"; }; @@ -83,6 +89,12 @@ in { description = "Whether IPFS should try to mount /ipfs and /ipns at startup."; }; + autoMigrate = mkOption { + type = types.bool; + default = true; + description = "Whether IPFS should try to run the fs-repo-migration at startup."; + }; + ipfsMountDir = mkOption { type = types.str; default = "/ipfs"; @@ -137,7 +149,7 @@ in { These are applied last, so may override configuration set by other options in this module. Keep in mind that this configuration is stateful; i.e., unsetting anything in here does not reset the value to the default! ''; - default = {}; + default = { }; example = { Datastore.StorageMax = "100GB"; Discovery.MDNS.Enabled = false; @@ -153,7 +165,7 @@ in { extraFlags = mkOption { type = types.listOf types.str; description = "Extra flags passed to the IPFS daemon"; - default = []; + default = [ ]; }; localDiscovery = mkOption { @@ -168,7 +180,7 @@ in { type = types.nullOr types.int; default = null; description = "The fdlimit for the IPFS systemd unit or <literal>null</literal> to have the daemon attempt to manage it"; - example = 64*1024; + example = 64 * 1024; }; startWhenNeeded = mkOption { @@ -186,6 +198,9 @@ in { environment.systemPackages = [ cfg.package ]; environment.variables.IPFS_PATH = cfg.dataDir; + # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size + boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000; + programs.fuse = mkIf cfg.autoMount { userAllowOther = true; }; @@ -226,33 +241,36 @@ in { ${optionalString (! cfg.localDiscovery) "--profile=server"} else ${if cfg.localDiscovery - then "ipfs config profile apply local-discovery" - else "ipfs config profile apply server" + then "ipfs --offline config profile apply local-discovery" + else "ipfs --offline config profile apply server" } fi '' + optionalString cfg.autoMount '' ipfs --offline config Mounts.FuseAllowOther --json true ipfs --offline config Mounts.IPFS ${cfg.ipfsMountDir} ipfs --offline config Mounts.IPNS ${cfg.ipnsMountDir} + '' + optionalString cfg.autoMigrate '' + ${pkgs.ipfs-migrator}/bin/fs-repo-migrations -y '' + concatStringsSep "\n" (collect - isString - (mapAttrsRecursive - (path: value: - # Using heredoc below so that the value is never improperly quoted - '' - read value <<EOF - ${builtins.toJSON value} - EOF - ipfs --offline config --json "${concatStringsSep "." path}" "$value" - '') - ({ Addresses.API = cfg.apiAddress; - Addresses.Gateway = cfg.gatewayAddress; - Addresses.Swarm = cfg.swarmAddress; - } // - cfg.extraConfig)) - ); + isString + (mapAttrsRecursive + (path: value: + # Using heredoc below so that the value is never improperly quoted + '' + read value <<EOF + ${builtins.toJSON value} + EOF + ipfs --offline config --json "${concatStringsSep "." path}" "$value" + '') + ({ + Addresses.API = cfg.apiAddress; + Addresses.Gateway = cfg.gatewayAddress; + Addresses.Swarm = cfg.swarmAddress; + } // + cfg.extraConfig)) + ); serviceConfig = { - ExecStart = ["" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}"]; + ExecStart = [ "" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}" ]; User = cfg.user; Group = cfg.group; } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; }; @@ -263,12 +281,16 @@ in { systemd.sockets.ipfs-gateway = { wantedBy = [ "sockets.target" ]; socketConfig = { - ListenStream = let + ListenStream = + let fromCfg = multiaddrToListenStream cfg.gatewayAddress; - in [ "" ] ++ lib.optional (fromCfg != null) fromCfg; - ListenDatagram = let + in + [ "" ] ++ lib.optional (fromCfg != null) fromCfg; + ListenDatagram = + let fromCfg = multiaddrToListenDatagram cfg.gatewayAddress; - in [ "" ] ++ lib.optional (fromCfg != null) fromCfg; + in + [ "" ] ++ lib.optional (fromCfg != null) fromCfg; }; }; @@ -276,9 +298,11 @@ in { wantedBy = [ "sockets.target" ]; # We also include "%t/ipfs.sock" because there is no way to put the "%t" # in the multiaddr. - socketConfig.ListenStream = let + socketConfig.ListenStream = + let fromCfg = multiaddrToListenStream cfg.apiAddress; - in [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg; + in + [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg; }; }; diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix new file mode 100644 index 000000000000..f1806c5af0a9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.litestream; + settingsFormat = pkgs.formats.yaml {}; +in +{ + options.services.litestream = { + enable = mkEnableOption "litestream"; + + package = mkOption { + description = "Package to use."; + default = pkgs.litestream; + defaultText = "pkgs.litestream"; + type = types.package; + }; + + settings = mkOption { + description = '' + See the <link xlink:href="https://litestream.io/reference/config/">documentation</link>. + ''; + type = settingsFormat.type; + example = { + dbs = [ + { + path = "/var/lib/db1"; + replicas = [ + { + url = "s3://mybkt.litestream.io/db1"; + } + ]; + } + ]; + }; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/secrets/litestream"; + description = '' + Environment file as defined in <citerefentry> + <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>. + + Secrets may be passed to the service without adding them to the + world-readable Nix store, by specifying placeholder variables as + the option value in Nix and setting these variables accordingly in the + environment file. + + By default, Litestream will perform environment variable expansion + within the config file before reading it. Any references to ''$VAR or + ''${VAR} formatted variables will be replaced with their environment + variable values. If no value is set then it will be replaced with an + empty string. + + <programlisting> + # Content of the environment file + LITESTREAM_ACCESS_KEY_ID=AKIAxxxxxxxxxxxxxxxx + LITESTREAM_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxx + </programlisting> + + Note that this file needs to be available on the host on which + this exporter is running. + ''; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + environment.etc = { + "litestream.yml" = { + source = settingsFormat.generate "litestream-config.yaml" cfg.settings; + }; + }; + + systemd.services.litestream = { + description = "Litestream"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" ]; + serviceConfig = { + EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; + ExecStart = "${cfg.package}/bin/litestream replicate"; + Restart = "always"; + User = "litestream"; + Group = "litestream"; + }; + }; + + users.users.litestream = { + description = "Litestream user"; + group = "litestream"; + isSystemUser = true; + }; + users.groups.litestream = {}; + }; + meta.doc = ./litestream.xml; +} diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml new file mode 100644 index 000000000000..598f9be8cf63 --- /dev/null +++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml @@ -0,0 +1,65 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-litestream"> + <title>Litestream</title> + <para> + <link xlink:href="https://litestream.io/">Litestream</link> is a standalone streaming + replication tool for SQLite. + </para> + + <section xml:id="module-services-litestream-configuration"> + <title>Configuration</title> + + <para> + Litestream service is managed by a dedicated user named <literal>litestream</literal> + which needs permission to the database file. Here's an example config which gives + required permissions to access <link linkend="opt-services.grafana.database.path"> + grafana database</link>: +<programlisting> +{ pkgs, ... }: +{ + users.users.litestream.extraGroups = [ "grafana" ]; + + systemd.services.grafana.serviceConfig.ExecStartPost = "+" + pkgs.writeShellScript "grant-grafana-permissions" '' + timeout=10 + + while [ ! -f /var/lib/grafana/data/grafana.db ]; + do + if [ "$timeout" == 0 ]; then + echo "ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db." + exit 1 + fi + + sleep 1 + + ((timeout--)) + done + + find /var/lib/grafana -type d -exec chmod -v 775 {} \; + find /var/lib/grafana -type f -exec chmod -v 660 {} \; + ''; + + services.litestream = { + enable = true; + + environmentFile = "/run/secrets/litestream"; + + settings = { + dbs = [ + { + path = "/var/lib/grafana/data/grafana.db"; + replicas = [{ + url = "s3://mybkt.litestream.io/grafana"; + }]; + } + ]; + }; + }; +} +</programlisting> + </para> + </section> + +</chapter> diff --git a/nixpkgs/nixos/modules/services/networking/autossh.nix b/nixpkgs/nixos/modules/services/networking/autossh.nix index a8d9a027e9fa..245f2bfc2cf3 100644 --- a/nixpkgs/nixos/modules/services/networking/autossh.nix +++ b/nixpkgs/nixos/modules/services/networking/autossh.nix @@ -79,7 +79,7 @@ in systemd.services = - lib.fold ( s : acc : acc // + lib.foldr ( s : acc : acc // { "autossh-${s.name}" = let diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix index 0b7d5575c11f..020a817f2596 100644 --- a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix @@ -240,8 +240,8 @@ in system.nssModules = optional cfg.nssmdns pkgs.nssmdns; system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [ - (mkOrder 900 [ "mdns_minimal [NOTFOUND=return]" ]) # must be before resolve - (mkOrder 1501 [ "mdns" ]) # 1501 to ensure it's after dns + (mkBefore [ "mdns_minimal [NOTFOUND=return]" ]) # before resolve + (mkAfter [ "mdns" ]) # after dns ]); environment.systemPackages = [ pkgs.avahi ]; diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix index 1923afdf83f2..c14adbda3c5a 100644 --- a/nixpkgs/nixos/modules/services/networking/bird.nix +++ b/nixpkgs/nixos/modules/services/networking/bird.nix @@ -10,8 +10,8 @@ let birdBin = if variant == "bird6" then "bird6" else "bird"; birdc = if variant == "bird6" then "birdc6" else "birdc"; descr = - { bird = "1.9.x with IPv4 suport"; - bird6 = "1.9.x with IPv6 suport"; + { bird = "1.6.x with IPv4 support"; + bird6 = "1.6.x with IPv6 support"; bird2 = "2.x"; }.${variant}; in { diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix index f1a504b3e3f4..ca95d00c2ff8 100644 --- a/nixpkgs/nixos/modules/services/networking/cjdns.nix +++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix @@ -39,7 +39,7 @@ let }; # Additional /etc/hosts entries for peers with an associated hostname - cjdnsExtraHosts = pkgs.runCommandNoCC "cjdns-hosts" {} '' + cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} '' exec >$out ${concatStringsSep "\n" (mapAttrsToList (k: v: optionalString (v.hostname != "") diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix index 11f66b05df12..608672c6446c 100644 --- a/nixpkgs/nixos/modules/services/networking/connman.nix +++ b/nixpkgs/nixos/modules/services/networking/connman.nix @@ -150,6 +150,7 @@ in { useDHCP = false; wireless = { enable = mkIf (!enableIwd) true; + dbusControlled = true; iwd = mkIf enableIwd { enable = true; }; diff --git a/nixpkgs/nixos/modules/services/networking/epmd.nix b/nixpkgs/nixos/modules/services/networking/epmd.nix index f7cdc0fe79c0..3899d164f16a 100644 --- a/nixpkgs/nixos/modules/services/networking/epmd.nix +++ b/nixpkgs/nixos/modules/services/networking/epmd.nix @@ -4,9 +4,7 @@ with lib; let cfg = config.services.epmd; - in - { ###### interface options.services.epmd = { @@ -27,16 +25,31 @@ in an Erlang runtime that is already installed for other purposes. ''; }; + listenStream = mkOption + { + type = types.str; + default = "[::]:4369"; + description = '' + the listenStream used by the systemd socket. + see https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream= for more informations. + use this to change the port epmd will run on. + if not defined, epmd will use "[::]:4369" + ''; + }; }; ###### implementation config = mkIf cfg.enable { + assertions = [{ + assertion = cfg.listenStream == "[::]:4369" -> config.networking.enableIPv6; + message = "epmd listens by default on ipv6, enable ipv6 or change config.services.epmd.listenStream"; + }]; systemd.sockets.epmd = rec { description = "Erlang Port Mapper Daemon Activation Socket"; wantedBy = [ "sockets.target" ]; before = wantedBy; socketConfig = { - ListenStream = "4369"; + ListenStream = cfg.listenStream; Accept = "false"; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/firewall.nix b/nixpkgs/nixos/modules/services/networking/firewall.nix index cdc3a172ea70..f982621e2328 100644 --- a/nixpkgs/nixos/modules/services/networking/firewall.nix +++ b/nixpkgs/nixos/modules/services/networking/firewall.nix @@ -339,6 +339,8 @@ in description = '' Whether to log rejected or dropped incoming connections. + Note: The logs are found in the kernel logs, i.e. dmesg + or journalctl -k. ''; }; @@ -350,6 +352,8 @@ in Whether to log all rejected or dropped incoming packets. This tends to give a lot of log messages, so it's mostly useful for debugging. + Note: The logs are found in the kernel logs, i.e. dmesg + or journalctl -k. ''; }; diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix index 93a21fd4c97e..fba0d817006e 100644 --- a/nixpkgs/nixos/modules/services/networking/i2pd.nix +++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix @@ -32,9 +32,9 @@ let description = "Bind address for ${name} endpoint."; }; port = mkOption { - type = types.int; + type = types.port; default = port; - description = "Bind port for ${name} endoint."; + description = "Bind port for ${name} endpoint."; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/iwd.nix b/nixpkgs/nixos/modules/services/networking/iwd.nix index 99e5e78badd2..8835f7f9372d 100644 --- a/nixpkgs/nixos/modules/services/networking/iwd.nix +++ b/nixpkgs/nixos/modules/services/networking/iwd.nix @@ -4,8 +4,31 @@ with lib; let cfg = config.networking.wireless.iwd; + ini = pkgs.formats.ini { }; + configFile = ini.generate "main.conf" cfg.settings; in { - options.networking.wireless.iwd.enable = mkEnableOption "iwd"; + options.networking.wireless.iwd = { + enable = mkEnableOption "iwd"; + + settings = mkOption { + type = ini.type; + default = { }; + + example = { + Settings.AutoConnect = true; + + Network = { + EnableIPv6 = true; + RoutePriorityOffset = 300; + }; + }; + + description = '' + Options passed to iwd. + See <link xlink:href="https://iwd.wiki.kernel.org/networkconfigurationsettings">here</link> for supported options. + ''; + }; + }; config = mkIf cfg.enable { assertions = [{ @@ -15,6 +38,8 @@ in { ''; }]; + environment.etc."iwd/main.conf".source = configFile; + # for iwctl environment.systemPackages = [ pkgs.iwd ]; @@ -27,7 +52,10 @@ in { linkConfig.NamePolicy = "keep kernel"; }; - systemd.services.iwd.wantedBy = [ "multi-user.target" ]; + systemd.services.iwd = { + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ configFile ]; + }; }; meta.maintainers = with lib.maintainers; [ mic92 dtzWill ]; diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix index 72773b83a496..b11402204aec 100644 --- a/nixpkgs/nixos/modules/services/networking/kea.nix +++ b/nixpkgs/nixos/modules/services/networking/kea.nix @@ -238,6 +238,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + ctrlAgentConfig + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; KillMode = "process"; @@ -269,6 +273,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcp4Config + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; # Kea does not request capabilities by itself @@ -307,6 +315,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcp6Config + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}"; # Kea does not request capabilities by itself @@ -343,6 +355,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcpDdnsConfig + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}"; AmbientCapabilites = [ diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix index 8ce71f26b3ee..9ec1ddc929e1 100644 --- a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix +++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix @@ -9,6 +9,7 @@ with lib; default = false; description = '' This option enables Mullvad VPN daemon. + This sets <option>networking.firewall.checkReversePath</option> to "loose", which might be undesirable for security. ''; }; @@ -18,6 +19,9 @@ with lib; # mullvad-daemon writes to /etc/iproute2/rt_tables networking.iproute2.enable = true; + # See https://github.com/NixOS/nixpkgs/issues/113589 + networking.firewall.checkReversePath = "loose"; + systemd.services.mullvad-daemon = { description = "Mullvad VPN daemon"; wantedBy = [ "multi-user.target" ]; @@ -42,5 +46,5 @@ with lib; }; }; - meta.maintainers = [ maintainers.xfix ]; + meta.maintainers = with maintainers; [ ymarkus ]; } diff --git a/nixpkgs/nixos/modules/services/networking/nats.nix b/nixpkgs/nixos/modules/services/networking/nats.nix new file mode 100644 index 000000000000..eb0c65bc6561 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nats.nix @@ -0,0 +1,159 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.nats; + + format = pkgs.formats.json { }; + + configFile = format.generate "nats.conf" cfg.settings; + +in { + + ### Interface + + options = { + services.nats = { + enable = mkEnableOption "NATS messaging system"; + + user = mkOption { + type = types.str; + default = "nats"; + description = "User account under which NATS runs."; + }; + + group = mkOption { + type = types.str; + default = "nats"; + description = "Group under which NATS runs."; + }; + + serverName = mkOption { + default = "nats"; + example = "n1-c3"; + type = types.str; + description = '' + Name of the NATS server, must be unique if clustered. + ''; + }; + + jetstream = mkEnableOption "JetStream"; + + port = mkOption { + default = 4222; + example = 4222; + type = types.port; + description = '' + Port on which to listen. + ''; + }; + + dataDir = mkOption { + default = "/var/lib/nats"; + type = types.path; + description = '' + The NATS data directory. Only used if JetStream is enabled, for + storing stream metadata and messages. + + If left as the default value this directory will automatically be + created before the NATS server starts, otherwise the sysadmin is + responsible for ensuring the directory exists with appropriate + ownership and permissions. + ''; + }; + + settings = mkOption { + default = { }; + type = format.type; + example = literalExample '' + { + jetstream = { + max_mem = "1G"; + max_file = "10G"; + }; + }; + ''; + description = '' + Declarative NATS configuration. See the + <link xlink:href="https://docs.nats.io/nats-server/configuration"> + NATS documentation</link> for a list of options. + ''; + }; + }; + }; + + ### Implementation + + config = mkIf cfg.enable { + services.nats.settings = { + server_name = cfg.serverName; + port = cfg.port; + jetstream = optionalAttrs cfg.jetstream { store_dir = cfg.dataDir; }; + }; + + systemd.services.nats = { + description = "NATS messaging system"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = mkMerge [ + (mkIf (cfg.dataDir == "/var/lib/nats") { + StateDirectory = "nats"; + StateDirectoryMode = "0750"; + }) + { + Type = "simple"; + ExecStart = "${pkgs.nats-server}/bin/nats-server -c ${configFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStop = "${pkgs.coreutils}/bin/kill -SIGINT $MAINPID"; + Restart = "on-failure"; + + User = cfg.user; + Group = cfg.group; + + # Hardening + CapabilityBoundingSet = ""; + LimitNOFILE = 800000; # JetStream requires 2 FDs open per stream. + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ReadOnlyPaths = [ ]; + ReadWritePaths = [ cfg.dataDir ]; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0077"; + } + ]; + }; + + users.users = mkIf (cfg.user == "nats") { + nats = { + description = "NATS daemon user"; + isSystemUser = true; + group = cfg.group; + home = cfg.dataDir; + }; + }; + + users.groups = mkIf (cfg.group == "nats") { nats = { }; }; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix index 064018057cdb..c8861171dd6c 100644 --- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix +++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix @@ -6,7 +6,6 @@ let cfg = config.networking.networkmanager; basePackages = with pkgs; [ - crda modemmanager networkmanager networkmanager-fortisslvpn @@ -49,6 +48,7 @@ let rc-manager = if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"; + firewall-backend = cfg.firewallBackend; }) (mkSection "keyfile" { unmanaged-devices = @@ -244,6 +244,15 @@ in { ''; }; + firewallBackend = mkOption { + type = types.enum [ "iptables" "nftables" "none" ]; + default = "iptables"; + description = '' + Which firewall backend should be used for configuring masquerading with shared mode. + If set to none, NetworkManager doesn't manage the configuration at all. + ''; + }; + logLevel = mkOption { type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ]; default = "WARN"; @@ -404,6 +413,8 @@ in { } ]; + hardware.wirelessRegulatoryDatabase = true; + environment.etc = with pkgs; { "NetworkManager/NetworkManager.conf".source = configFile; diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix index cb75142965ea..72f37c32253e 100644 --- a/nixpkgs/nixos/modules/services/networking/nftables.nix +++ b/nixpkgs/nixos/modules/services/networking/nftables.nix @@ -103,6 +103,7 @@ in }]; boot.blacklistedKernelModules = [ "ip_tables" ]; environment.systemPackages = [ pkgs.nftables ]; + networking.networkmanager.firewallBackend = mkDefault "nftables"; systemd.services.nftables = { description = "nftables firewall"; before = [ "network-pre.target" ]; diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix index 96c6444c23a1..ed61c178c685 100644 --- a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix +++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix @@ -81,17 +81,26 @@ in ''; }; - initstepslew = mkOption { - type = types.attrsOf (types.either types.bool types.int); - default = { - enabled = true; - threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s) + initstepslew = { + enabled = mkOption { + type = types.bool; + default = true; + description = '' + Allow chronyd to make a rapid measurement of the system clock error + at boot time, and to correct the system clock by stepping before + normal operation begins. + ''; + }; + + threshold = mkOption { + type = types.either types.float types.int; + default = 1000; # by default, same threshold as 'ntpd -g' (1000s) + description = '' + The threshold of system clock error (in seconds) above which the + clock will be stepped. If the correction required is less than the + threshold, a slew is used instead. + ''; }; - description = '' - Allow chronyd to make a rapid measurement of the system clock error at - boot time, and to correct the system clock by stepping before normal - operation begins. - ''; }; directory = mkOption { @@ -148,7 +157,7 @@ in wantedBy = [ "multi-user.target" ]; wants = [ "time-sync.target" ]; before = [ "time-sync.target" ]; - after = [ "network.target" ]; + after = [ "network.target" "nss-lookup.target" ]; conflicts = [ "ntpd.service" "systemd-timesyncd.service" ]; path = [ chronyPkg ]; diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix index bfc358cb12fb..a20fa615af80 100644 --- a/nixpkgs/nixos/modules/services/networking/nylon.nix +++ b/nixpkgs/nixos/modules/services/networking/nylon.nix @@ -160,7 +160,7 @@ in users.groups.nylon.gid = config.ids.gids.nylon; - systemd.services = fold (a: b: a // b) {} nylonUnits; + systemd.services = foldr (a: b: a // b) {} nylonUnits; }; } diff --git a/nixpkgs/nixos/modules/services/networking/quicktun.nix b/nixpkgs/nixos/modules/services/networking/quicktun.nix index fb783c836464..438e67d5ebb6 100644 --- a/nixpkgs/nixos/modules/services/networking/quicktun.nix +++ b/nixpkgs/nixos/modules/services/networking/quicktun.nix @@ -87,7 +87,7 @@ with lib; }; config = mkIf (cfg != []) { - systemd.services = fold (a: b: a // b) {} ( + systemd.services = foldr (a: b: a // b) {} ( mapAttrsToList (name: qtcfg: { "quicktun-${name}" = { wantedBy = [ "multi-user.target" ]; diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix index a808a7f39d05..405808491ea4 100644 --- a/nixpkgs/nixos/modules/services/networking/shout.nix +++ b/nixpkgs/nixos/modules/services/networking/shout.nix @@ -41,7 +41,7 @@ in { }; port = mkOption { - type = types.int; + type = types.port; default = 9000; description = "TCP port to listen on for http connections."; }; diff --git a/nixpkgs/nixos/modules/services/networking/soju.nix b/nixpkgs/nixos/modules/services/networking/soju.nix new file mode 100644 index 000000000000..68a33e9dccba --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/soju.nix @@ -0,0 +1,113 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.soju; + stateDir = "/var/lib/soju"; + listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") cfg.listen; + tlsCfg = optionalString (cfg.tlsCertificate != null) + "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}"; + logCfg = optionalString cfg.enableMessageLogging + "log fs ${stateDir}/logs"; + + configFile = pkgs.writeText "soju.conf" '' + ${listenCfg} + hostname ${cfg.hostName} + ${tlsCfg} + db sqlite3 ${stateDir}/soju.db + ${logCfg} + http-origin ${concatStringsSep " " cfg.httpOrigins} + accept-proxy-ip ${concatStringsSep " " cfg.acceptProxyIP} + + ${cfg.extraConfig} + ''; +in +{ + ###### interface + + options.services.soju = { + enable = mkEnableOption "soju"; + + listen = mkOption { + type = types.listOf types.str; + default = [ ":6697" ]; + description = '' + Where soju should listen for incoming connections. See the + <literal>listen</literal> directive in + <citerefentry><refentrytitle>soju</refentrytitle> + <manvolnum>1</manvolnum></citerefentry>. + ''; + }; + + hostName = mkOption { + type = types.str; + default = config.networking.hostName; + description = "Server hostname."; + }; + + tlsCertificate = mkOption { + type = types.nullOr types.path; + example = "/var/host.cert"; + description = "Path to server TLS certificate."; + }; + + tlsCertificateKey = mkOption { + type = types.nullOr types.path; + example = "/var/host.key"; + description = "Path to server TLS certificate key."; + }; + + enableMessageLogging = mkOption { + type = types.bool; + default = true; + description = "Whether to enable message logging."; + }; + + httpOrigins = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of allowed HTTP origins for WebSocket listeners. The parameters are + interpreted as shell patterns, see + <citerefentry><refentrytitle>glob</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; + }; + + acceptProxyIP = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Allow the specified IPs to act as a proxy. Proxys have the ability to + overwrite the remote and local connection addresses (via the X-Forwarded-\* + HTTP header fields). The special name "localhost" accepts the loopback + addresses 127.0.0.0/8 and ::1/128. By default, all IPs are rejected. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Lines added verbatim to the configuration file."; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.soju = { + description = "soju IRC bouncer"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + DynamicUser = true; + Restart = "always"; + ExecStart = "${pkgs.soju}/bin/soju -config ${configFile}"; + StateDirectory = "soju"; + }; + }; + }; + + meta.maintainers = with maintainers; [ malvo ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix index d804c017f5d6..04879eb7d82d 100644 --- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix @@ -531,11 +531,7 @@ in XAuthLocation ${pkgs.xorg.xauth}/bin/xauth ''} - ${if cfg.forwardX11 then '' - X11Forwarding yes - '' else '' - X11Forwarding no - ''} + X11Forwarding ${if cfg.forwardX11 then "yes" else "no"} ${optionalString cfg.allowSFTP '' Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags} @@ -568,11 +564,7 @@ in LogLevel ${cfg.logLevel} - ${if cfg.useDns then '' - UseDNS yes - '' else '' - UseDNS no - ''} + UseDNS ${if cfg.useDns then "yes" else "no"} StrictModes ${if cfg.strictModes then "yes" else "no"} diff --git a/nixpkgs/nixos/modules/services/networking/syncplay.nix b/nixpkgs/nixos/modules/services/networking/syncplay.nix index e3147c10502c..27a16fb2e29f 100644 --- a/nixpkgs/nixos/modules/services/networking/syncplay.nix +++ b/nixpkgs/nixos/modules/services/networking/syncplay.nix @@ -21,7 +21,7 @@ in }; port = mkOption { - type = types.int; + type = types.port; default = 8999; description = '' TCP port to bind to. diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix index 28348c7893a0..ebe4d89a0e7f 100644 --- a/nixpkgs/nixos/modules/services/networking/syncthing.nix +++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix @@ -5,15 +5,16 @@ with lib; let cfg = config.services.syncthing; defaultUser = "syncthing"; + defaultGroup = defaultUser; devices = mapAttrsToList (name: device: { deviceID = device.id; - inherit (device) name addresses introducer; - }) cfg.declarative.devices; + inherit (device) name addresses introducer autoAcceptFolders; + }) cfg.devices; folders = mapAttrsToList ( _: folder: { inherit (folder) path id label type; - devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices; + devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices; rescanIntervalS = folder.rescanInterval; fsWatcherEnabled = folder.watch; fsWatcherDelayS = folder.watchDelay; @@ -23,215 +24,227 @@ let }) (filterAttrs ( _: folder: folder.enable - ) cfg.declarative.folders); - - # get the api key by parsing the config.xml - getApiKey = pkgs.writers.writeDash "getAPIKey" '' - ${pkgs.libxml2}/bin/xmllint \ - --xpath 'string(configuration/gui/apikey)'\ - ${cfg.configDir}/config.xml - ''; + ) cfg.folders); updateConfig = pkgs.writers.writeDash "merge-syncthing-config" '' set -efu - # wait for syncthing port to open - until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do - sleep 1 - done - - API_KEY=$(${getApiKey}) - OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - ${cfg.guiAddress}/rest/system/config) - - # generate the new config by merging with the nixos config options - NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * { - "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}), - "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"}) - }') - - # POST the new config to syncthing - echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - ${cfg.guiAddress}/rest/system/config -d @- - - # restart syncthing after sending the new config - ${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - -X POST \ - ${cfg.guiAddress}/rest/system/restart + + # get the api key by parsing the config.xml + while + ! api_key=$(${pkgs.libxml2}/bin/xmllint \ + --xpath 'string(configuration/gui/apikey)' \ + ${cfg.configDir}/config.xml) + do sleep 1; done + + curl() { + ${pkgs.curl}/bin/curl -sSLk -H "X-API-Key: $api_key" \ + --retry 1000 --retry-delay 1 --retry-all-errors \ + "$@" + } + + # query the old config + old_cfg=$(curl ${cfg.guiAddress}/rest/config) + + # generate the new config by merging with the NixOS config options + new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * { + "devices": (${builtins.toJSON devices}${optionalString (! cfg.overrideDevices) " + .devices"}), + "folders": (${builtins.toJSON folders}${optionalString (! cfg.overrideFolders) " + .folders"}) + } * ${builtins.toJSON cfg.extraOptions}') + + # send the new config + curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config + + # restart Syncthing if required + if curl ${cfg.guiAddress}/rest/config/restart-required | + ${pkgs.jq}/bin/jq -e .requiresRestart > /dev/null; then + curl -X POST ${cfg.guiAddress}/rest/system/restart + fi ''; in { ###### interface options = { services.syncthing = { - enable = mkEnableOption '' - Syncthing - the self-hosted open-source alternative - to Dropbox and Bittorrent Sync. Initial interface will be - available on http://127.0.0.1:8384/. - ''; - - declarative = { - cert = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - Path to users cert.pem file, will be copied into the syncthing's - <literal>configDir</literal> - ''; - }; + enable = mkEnableOption + "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync"; - key = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - Path to users key.pem file, will be copied into the syncthing's - <literal>configDir</literal> - ''; - }; + cert = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's + <link linkend="opt-services.syncthing.configDir">configDir</link>. + ''; + }; - overrideDevices = mkOption { - type = types.bool; - default = true; - description = '' - Whether to delete the devices which are not configured via the - <literal>declarative.devices</literal> option. - If set to false, devices added via the webinterface will - persist but will have to be deleted manually. - ''; + key = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's + <link linkend="opt-services.syncthing.configDir">configDir</link>. + ''; + }; + + overrideDevices = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the devices which are not configured via the + <link linkend="opt-services.syncthing.devices">devices</link> option. + If set to <literal>false</literal>, devices added via the web + interface will persist and will have to be deleted manually. + ''; + }; + + devices = mkOption { + default = {}; + description = '' + Peers/devices which Syncthing should communicate with. + + Note that you can still add devices manually, but those changes + will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link> + is enabled. + ''; + example = { + bigbox = { + id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; + addresses = [ "tcp://192.168.0.10:51820" ]; + }; }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + name = mkOption { + type = types.str; + default = name; + description = '' + The name of the device. + ''; + }; - devices = mkOption { - default = {}; - description = '' - Peers/devices which syncthing should communicate with. - ''; - example = { - bigbox = { - id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; - addresses = [ "tcp://192.168.0.10:51820" ]; + addresses = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The addresses used to connect to the device. + If this is left empty, dynamic configuration is attempted. + ''; + }; + + id = mkOption { + type = types.str; + description = '' + The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>. + ''; + }; + + introducer = mkOption { + type = types.bool; + default = false; + description = '' + Whether the device should act as an introducer and be allowed + to add folders on this computer. + See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>. + ''; }; + + autoAcceptFolders = mkOption { + type = types.bool; + default = false; + description = '' + Automatically create or share folders that this device advertises at the default path. + See <link xlink:href="https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format"/>. + ''; + }; + }; - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - - name = mkOption { - type = types.str; - default = name; - description = '' - Name of the device - ''; - }; - - addresses = mkOption { - type = types.listOf types.str; - default = []; - description = '' - The addresses used to connect to the device. - If this is let empty, dynamic configuration is attempted - ''; - }; - - id = mkOption { - type = types.str; - description = '' - The id of the other peer, this is mandatory. It's documented at - https://docs.syncthing.net/dev/device-ids.html - ''; - }; - - introducer = mkOption { - type = types.bool; - default = false; - description = '' - If the device should act as an introducer and be allowed - to add folders on this computer. - ''; - }; + })); + }; + overrideFolders = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the folders which are not configured via the + <link linkend="opt-services.syncthing.folders">folders</link> option. + If set to <literal>false</literal>, folders added via the web + interface will persist and will have to be deleted manually. + ''; + }; + + folders = mkOption { + default = {}; + description = '' + Folders which should be shared by Syncthing. + + Note that you can still add devices manually, but those changes + will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link> + is enabled. + ''; + example = literalExample '' + { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + } + ''; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to share this folder. + This option is useful when you want to define all folders + in one place, but not every machine should share all folders. + ''; }; - })); - }; - overrideFolders = mkOption { - type = types.bool; - default = true; - description = '' - Whether to delete the folders which are not configured via the - <literal>declarative.folders</literal> option. - If set to false, folders added via the webinterface will persist - but will have to be deleted manually. - ''; - }; + path = mkOption { + type = types.str; + default = name; + description = '' + The path to the folder which should be shared. + ''; + }; - folders = mkOption { - default = {}; - description = '' - folders which should be shared by syncthing. - ''; - example = literalExample '' - { - "/home/user/sync" = { - id = "syncme"; - devices = [ "bigbox" ]; - }; - } - ''; - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - - enable = mkOption { - type = types.bool; - default = true; - description = '' - share this folder. - This option is useful when you want to define all folders - in one place, but not every machine should share all folders. - ''; - }; - - path = mkOption { - type = types.str; - default = name; - description = '' - The path to the folder which should be shared. - ''; - }; - - id = mkOption { - type = types.str; - default = name; - description = '' - The id of the folder. Must be the same on all devices. - ''; - }; - - label = mkOption { - type = types.str; - default = name; - description = '' - The label of the folder. - ''; - }; - - devices = mkOption { - type = types.listOf types.str; - default = []; - description = '' - The devices this folder should be shared with. Must be defined - in the <literal>declarative.devices</literal> attribute. - ''; - }; - - versioning = mkOption { - default = null; - description = '' - How to keep changed/deleted files with syncthing. - There are 4 different types of versioning with different parameters. - See https://docs.syncthing.net/users/versioning.html - ''; - example = [ + id = mkOption { + type = types.str; + default = name; + description = '' + The ID of the folder. Must be the same on all devices. + ''; + }; + + label = mkOption { + type = types.str; + default = name; + description = '' + The label of the folder. + ''; + }; + + devices = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The devices this folder should be shared with. Each device must + be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option. + ''; + }; + + versioning = mkOption { + default = null; + description = '' + How to keep changed/deleted files with Syncthing. + There are 4 different types of versioning with different parameters. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; + example = literalExample '' + [ { versioning = { type = "simple"; @@ -257,87 +270,99 @@ in { { versioning = { type = "external"; - params.versionsPath = pkgs.writers.writeBash "backup" '' + params.versionsPath = pkgs.writers.writeBash "backup" ''' folderpath="$1" filepath="$2" rm -rf "$folderpath/$filepath" - ''; + '''; }; } - ]; - type = with types; nullOr (submodule { - options = { - type = mkOption { - type = enum [ "external" "simple" "staggered" "trashcan" ]; - description = '' - Type of versioning. - See https://docs.syncthing.net/users/versioning.html - ''; - }; - params = mkOption { - type = attrsOf (either str path); - description = '' - Parameters for versioning. Structure depends on versioning.type. - See https://docs.syncthing.net/users/versioning.html - ''; - }; + ] + ''; + type = with types; nullOr (submodule { + options = { + type = mkOption { + type = enum [ "external" "simple" "staggered" "trashcan" ]; + description = '' + The type of versioning. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; + }; + params = mkOption { + type = attrsOf (either str path); + description = '' + The parameters for versioning. Structure depends on + <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; }; - }); - }; - - rescanInterval = mkOption { - type = types.int; - default = 3600; - description = '' - How often the folders should be rescaned for changes. - ''; - }; - - type = mkOption { - type = types.enum [ "sendreceive" "sendonly" "receiveonly" ]; - default = "sendreceive"; - description = '' - Whether to send only changes from this folder, only receive them - or propagate both. - ''; - }; - - watch = mkOption { - type = types.bool; - default = true; - description = '' - Whether the folder should be watched for changes by inotify. - ''; - }; - - watchDelay = mkOption { - type = types.int; - default = 10; - description = '' - The delay after an inotify event is triggered. - ''; - }; - - ignorePerms = mkOption { - type = types.bool; - default = true; - description = '' - Whether to propagate permission changes. - ''; - }; - - ignoreDelete = mkOption { - type = types.bool; - default = false; - description = '' - Whether to delete files in destination. See <link - xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"> - upstream's docs</link>. - ''; - }; + }; + }); + }; + + rescanInterval = mkOption { + type = types.int; + default = 3600; + description = '' + How often the folder should be rescanned for changes. + ''; + }; + type = mkOption { + type = types.enum [ "sendreceive" "sendonly" "receiveonly" ]; + default = "sendreceive"; + description = '' + Whether to only send changes for this folder, only receive them + or both. + ''; }; - })); + + watch = mkOption { + type = types.bool; + default = true; + description = '' + Whether the folder should be watched for changes by inotify. + ''; + }; + + watchDelay = mkOption { + type = types.int; + default = 10; + description = '' + The delay after an inotify event is triggered. + ''; + }; + + ignorePerms = mkOption { + type = types.bool; + default = true; + description = '' + Whether to ignore permission changes. + ''; + }; + + ignoreDelete = mkOption { + type = types.bool; + default = false; + description = '' + Whether to skip deleting files that are deleted by peers. + See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>. + ''; + }; + }; + })); + }; + + extraOptions = mkOption { + type = types.addCheck (pkgs.formats.json {}).type isAttrs; + default = {}; + description = '' + Extra configuration options for Syncthing. + See <link xlink:href="https://docs.syncthing.net/users/config.html"/>. + ''; + example = { + options.localAnnounceEnabled = false; + gui.theme = "black"; }; }; @@ -345,31 +370,35 @@ in { type = types.str; default = "127.0.0.1:8384"; description = '' - Address to serve the GUI. + The address to serve the web interface at. ''; }; systemService = mkOption { type = types.bool; default = true; - description = "Auto launch Syncthing as a system service."; + description = '' + Whether to auto-launch Syncthing as a system service. + ''; }; user = mkOption { type = types.str; default = defaultUser; + example = "yourUser"; description = '' - Syncthing will be run under this user (user will be created if it doesn't exist. - This can be your user name). + The user to run Syncthing as. + By default, a user named <literal>${defaultUser}</literal> will be created. ''; }; group = mkOption { type = types.str; - default = defaultUser; + default = defaultGroup; + example = "yourGroup"; description = '' - Syncthing will be run under this group (group will not be created if it doesn't exist. - This can be your user name). + The group to run Syncthing under. + By default, a group named <literal>${defaultGroup}</literal> will be created. ''; }; @@ -378,63 +407,76 @@ in { default = null; example = "socks5://address.com:1234"; description = '' - Overwrites all_proxy environment variable for the syncthing process to - the given value. This is normaly used to let relay client connect - through SOCKS5 proxy server. + Overwrites the all_proxy environment variable for the Syncthing process to + the given value. This is normally used to let Syncthing connect + through a SOCKS5 proxy server. + See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>. ''; }; dataDir = mkOption { type = types.path; default = "/var/lib/syncthing"; + example = "/home/yourUser"; description = '' - Path where synced directories will exist. + The path where synchronised directories will exist. ''; }; - configDir = mkOption { + configDir = let + cond = versionAtLeast config.system.stateVersion "19.03"; + in mkOption { type = types.path; description = '' - Path where the settings and keys will exist. + The path where the settings and keys will exist. + ''; + default = cfg.dataDir + (optionalString cond "/.config/syncthing"); + defaultText = literalExample "dataDir${optionalString cond " + \"/.config/syncthing\""}"; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + example = [ "--reset-deltas" ]; + description = '' + Extra flags passed to the syncthing command in the service definition. ''; - default = - let - nixos = config.system.stateVersion; - cond = versionAtLeast nixos "19.03"; - in cfg.dataDir + (optionalString cond "/.config/syncthing"); }; openDefaultPorts = mkOption { type = types.bool; default = false; - example = literalExample "true"; + example = true; description = '' - Open the default ports in the firewall: - - TCP 22000 for transfers - - UDP 21027 for discovery - If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled. - Alternatively, if are running only a single instance on this machine using the default ports, enable this. + Whether to open the default ports in the firewall: TCP 22000 for transfers + and UDP 21027 for discovery. + + If multiple users are running Syncthing on this machine, you will need + to manually open a set of ports for each instance and leave this disabled. + Alternatively, if you are running only a single instance on this machine + using the default ports, enable this. ''; }; package = mkOption { type = types.package; default = pkgs.syncthing; - defaultText = "pkgs.syncthing"; - example = literalExample "pkgs.syncthing"; + defaultText = literalExample "pkgs.syncthing"; description = '' - Syncthing package to use. + The Syncthing package to use. ''; }; }; }; imports = [ - (mkRemovedOptionModule ["services" "syncthing" "useInotify"] '' - This option was removed because syncthing now has the inotify functionality included under the name "fswatcher". - It can be enabled on a per-folder basis through the webinterface. + (mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] '' + This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher". + It can be enabled on a per-folder basis through the web interface. '') - ]; + ] ++ map (o: + mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ] + ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"]; ###### implementation @@ -457,8 +499,8 @@ in { }; }; - users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) { - ${defaultUser}.gid = + users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) { + ${defaultGroup}.gid = config.ids.gids.syncthing; }; @@ -478,14 +520,14 @@ in { RestartForceExitStatus="3 4"; User = cfg.user; Group = cfg.group; - ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null) + ExecStartPre = mkIf (cfg.cert != null || cfg.key != null) "+${pkgs.writers.writeBash "syncthing-copy-keys" '' install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir} - ${optionalString (cfg.declarative.cert != null) '' - install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem + ${optionalString (cfg.cert != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem ''} - ${optionalString (cfg.declarative.key != null) '' - install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem + ${optionalString (cfg.key != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem ''} ''}" ; @@ -493,7 +535,7 @@ in { ${cfg.package}/bin/syncthing \ -no-browser \ -gui-address=${cfg.guiAddress} \ - -home=${cfg.configDir} + -home=${cfg.configDir} ${escapeShellArgs cfg.extraFlags} ''; MemoryDenyWriteExecute = true; NoNewPrivileges = true; @@ -516,8 +558,10 @@ in { }; }; syncthing-init = mkIf ( - cfg.declarative.devices != {} || cfg.declarative.folders != {} + cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {} ) { + description = "Syncthing configuration updater"; + requisite = [ "syncthing.service" ]; after = [ "syncthing.service" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix index b6afd83a9abd..22caf9f4ec56 100644 --- a/nixpkgs/nixos/modules/services/networking/tinc.nix +++ b/nixpkgs/nixos/modules/services/networking/tinc.nix @@ -351,7 +351,7 @@ in config = mkIf (cfg.networks != { }) { - environment.etc = fold (a: b: a // b) { } + environment.etc = foldr (a: b: a // b) { } (flip mapAttrsToList cfg.networks (network: data: flip mapAttrs' data.hosts (host: text: nameValuePair ("tinc/${network}/hosts/${host}") @@ -427,9 +427,12 @@ in nameValuePair ("tinc.${network}") ({ description = "Tinc daemon user for ${network}"; isSystemUser = true; + group = "tinc.${network}"; }) ); - + users.groups = flip mapAttrs' cfg.networks (network: _: + nameValuePair "tinc.${network}" {} + ); }; meta.maintainers = with maintainers; [ minijackson ]; diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix index 62bcf7a14972..2e320378cc9a 100644 --- a/nixpkgs/nixos/modules/services/networking/unifi.nix +++ b/nixpkgs/nixos/modules/services/networking/unifi.nix @@ -173,6 +173,41 @@ in User = "unifi"; UMask = "0077"; WorkingDirectory = "${stateDir}"; + + # Hardening + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + DevicePolicy = "closed"; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + + # Required for ProtectSystem=strict + BindPaths = [ stateDir ]; + + # Needs network access + PrivateNetwork = false; + # Cannot be true due to OpenJDK + MemoryDenyWriteExecute = false; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/v2ray.nix b/nixpkgs/nixos/modules/services/networking/v2ray.nix index 6a924a16449a..0b8b5b56e25b 100644 --- a/nixpkgs/nixos/modules/services/networking/v2ray.nix +++ b/nixpkgs/nixos/modules/services/networking/v2ray.nix @@ -25,7 +25,7 @@ with lib; Either <literal>configFile</literal> or <literal>config</literal> must be specified. - See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>. + See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>. ''; }; @@ -47,7 +47,7 @@ with lib; Either `configFile` or `config` must be specified. - See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>. + See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>. ''; }; }; diff --git a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix index 35ff67937fc7..c6291366b0f1 100644 --- a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix +++ b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix @@ -19,7 +19,7 @@ let ${ethtool} -s ${interface} ${methodParameter {inherit method password;}} ''; - concatStrings = fold (x: y: x + y) ""; + concatStrings = foldr (x: y: x + y) ""; lines = concatStrings (map (l: line l) interfaces); in @@ -31,6 +31,20 @@ in services.wakeonlan.interfaces = mkOption { default = [ ]; + type = types.listOf (types.submodule { options = { + interface = mkOption { + type = types.str; + description = "Interface to enable for Wake-On-Lan."; + }; + method = mkOption { + type = types.enum [ "magicpacket" "password"]; + description = "Wake-On-Lan method for this interface."; + }; + password = mkOption { + type = types.strMatching "[a-fA-F0-9]{2}:([a-fA-F0-9]{2}:){4}[a-fA-F0-9]{2}"; + description = "The password has the shape of six bytes in hexadecimal separated by a colon each."; + }; + };}); example = [ { interface = "eth0"; diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix index c0a4ce40760a..155c6fdd0ab0 100644 --- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix @@ -8,28 +8,108 @@ let else pkgs.wpa_supplicant; cfg = config.networking.wireless; - configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" '' - ${optionalString cfg.userControlled.enable '' - ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group} - update_config=1''} - ${cfg.extraConfig} - ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let - key = if psk != null - then ''"${psk}"'' - else pskRaw; - baseAuth = if key != null - then "psk=${key}" - else "key_mgmt=NONE"; - in '' - network={ - ssid="${ssid}" - ${optionalString (priority != null) ''priority=${toString priority}''} - ${optionalString hidden "scan_ssid=1"} - ${if (auth != null) then auth else baseAuth} - ${extraConfig} - } - '') cfg.networks)} - '' else "/etc/wpa_supplicant.conf"; + + # Content of wpa_supplicant.conf + generatedConfig = concatStringsSep "\n" ( + (mapAttrsToList mkNetwork cfg.networks) + ++ optional cfg.userControlled.enable (concatStringsSep "\n" + [ "ctrl_interface=/run/wpa_supplicant" + "ctrl_interface_group=${cfg.userControlled.group}" + "update_config=1" + ]) + ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"'' + ++ optional (cfg.extraConfig != "") cfg.extraConfig); + + configFile = + if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable + then pkgs.writeText "wpa_supplicant.conf" generatedConfig + else "/etc/wpa_supplicant.conf"; + + # Creates a network block for wpa_supplicant.conf + mkNetwork = ssid: opts: + let + quote = x: ''"${x}"''; + indent = x: " " + x; + + pskString = if opts.psk != null + then quote opts.psk + else opts.pskRaw; + + options = [ + "ssid=${quote ssid}" + (if pskString != null || opts.auth != null + then "key_mgmt=${concatStringsSep " " opts.authProtocols}" + else "key_mgmt=NONE") + ] ++ optional opts.hidden "scan_ssid=1" + ++ optional (pskString != null) "psk=${pskString}" + ++ optionals (opts.auth != null) (filter (x: x != "") (splitString "\n" opts.auth)) + ++ optional (opts.priority != null) "priority=${toString opts.priority}" + ++ optional (opts.extraConfig != "") opts.extraConfig; + in '' + network={ + ${concatMapStringsSep "\n" indent options} + } + ''; + + # Creates a systemd unit for wpa_supplicant bound to a given (or any) interface + mkUnit = iface: + let + deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device"; + configStr = if cfg.allowAuxiliaryImperativeNetworks + then "-c /etc/wpa_supplicant.conf -I ${configFile}" + else "-c ${configFile}"; + in { + description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}"; + + after = deviceUnit; + before = [ "network.target" ]; + wants = [ "network.target" ]; + requires = deviceUnit; + wantedBy = [ "multi-user.target" ]; + stopIfChanged = false; + + path = [ package ]; + + script = + '' + if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then + echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." + fi + + iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}" + + ${if iface == null then '' + # detect interfaces automatically + + # check if there are no wireless interfaces + if ! find -H /sys/class/net/* -name wireless | grep -q .; then + # if so, wait until one appears + echo "Waiting for wireless interfaces" + grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu) + # Note: the above line has been carefully written: + # 1. The process substitution avoids udevadm hanging (after grep has quit) + # until it tries to write to the pipe again. Not even pipefail works here. + # 2. stdbuf is needed because udevadm output is buffered by default and grep + # may hang until more udev events enter the pipe. + fi + + # add any interface found to the daemon arguments + for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do + echo "Adding interface $name" + args+="''${args:+ -N} -i$name $iface_args" + done + '' else '' + # add known interface to the daemon arguments + args="-i${iface} $iface_args" + ''} + + # finally start daemon + exec wpa_supplicant $args + ''; + }; + + systemctl = "/run/current-system/systemd/bin/systemctl"; + in { options = { networking.wireless = { @@ -42,11 +122,10 @@ in { description = '' The interfaces <command>wpa_supplicant</command> will use. If empty, it will automatically use all wireless interfaces. - <warning><para> - The automatic discovery of interfaces does not work reliably on boot: - it may fail and leave the system without network. When possible, specify - a known interface name. - </para></warning> + + <note><para> + A separate wpa_supplicant instance will be started for each interface. + </para></note> ''; }; @@ -66,6 +145,16 @@ in { ''; }; + scanOnLowSignal = mkOption { + type = types.bool; + default = true; + description = '' + Whether to periodically scan for (better) networks when the signal of + the current one is low. This will make roaming between access points + faster, but will consume more power. + ''; + }; + networks = mkOption { type = types.attrsOf (types.submodule { options = { @@ -94,11 +183,52 @@ in { ''; }; + authProtocols = mkOption { + default = [ + # WPA2 and WPA3 + "WPA-PSK" "WPA-EAP" "SAE" + # 802.11r variants of the above + "FT-PSK" "FT-EAP" "FT-SAE" + ]; + # The list can be obtained by running this command + # awk ' + # /^# key_mgmt: /{ run=1 } + # /^#$/{ run=0 } + # /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} } + # ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example + type = types.listOf (types.enum [ + "WPA-PSK" + "WPA-EAP" + "IEEE8021X" + "NONE" + "WPA-NONE" + "FT-PSK" + "FT-EAP" + "FT-EAP-SHA384" + "WPA-PSK-SHA256" + "WPA-EAP-SHA256" + "SAE" + "FT-SAE" + "WPA-EAP-SUITE-B" + "WPA-EAP-SUITE-B-192" + "OSEN" + "FILS-SHA256" + "FILS-SHA384" + "FT-FILS-SHA256" + "FT-FILS-SHA384" + "OWE" + "DPP" + ]); + description = '' + The list of authentication protocols accepted by this network. + This corresponds to the <literal>key_mgmt</literal> option in wpa_supplicant. + ''; + }; + auth = mkOption { type = types.nullOr types.str; default = null; example = '' - key_mgmt=WPA-EAP eap=PEAP identity="user@example.com" password="secret" @@ -205,6 +335,16 @@ in { description = "Members of this group can control wpa_supplicant."; }; }; + + dbusControlled = mkOption { + type = types.bool; + default = lib.length cfg.interfaces < 2; + description = '' + Whether to enable the DBus control interface. + This is only needed when using NetworkManager or connman. + ''; + }; + extraConfig = mkOption { type = types.str; default = ""; @@ -228,73 +368,47 @@ in { assertions = flip mapAttrsToList cfg.networks (name: cfg: { assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1; message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive''; - }); + }) ++ [ + { + assertion = length cfg.interfaces > 1 -> !cfg.dbusControlled; + message = + let daemon = if config.networking.networkmanager.enable then "NetworkManager" else + if config.services.connman.enable then "connman" else null; + n = toString (length cfg.interfaces); + in '' + It's not possible to run multiple wpa_supplicant instances with DBus support. + Note: you're seeing this error because `networking.wireless.interfaces` has + ${n} entries, implying an equal number of wpa_supplicant instances. + '' + optionalString (daemon != null) '' + You don't need to change `networking.wireless.interfaces` when using ${daemon}: + in this case the interfaces will be configured automatically for you. + ''; + } + ]; - warnings = - optional (cfg.interfaces == [] && config.systemd.services.wpa_supplicant.wantedBy != []) - '' - No network interfaces for wpa_supplicant have been configured: the service - may randomly fail to start at boot. You should specify at least one using the option - networking.wireless.interfaces. - ''; + hardware.wirelessRegulatoryDatabase = true; environment.systemPackages = [ package ]; - - services.dbus.packages = [ package ]; - services.udev.packages = [ pkgs.crda ]; - - # FIXME: start a separate wpa_supplicant instance per interface. - systemd.services.wpa_supplicant = let - ifaces = cfg.interfaces; - deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ]; - in { - description = "WPA Supplicant"; - - after = lib.concatMap deviceUnit ifaces; - before = [ "network.target" ]; - wants = [ "network.target" ]; - requires = lib.concatMap deviceUnit ifaces; - wantedBy = [ "multi-user.target" ]; - stopIfChanged = false; - - path = [ package ]; - - script = let - configStr = if cfg.allowAuxiliaryImperativeNetworks - then "-c /etc/wpa_supplicant.conf -I ${configFile}" - else "-c ${configFile}"; - in '' - if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ] - then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead." - fi - iface_args="-s -u -D${cfg.driver} ${configStr}" - ${if ifaces == [] then '' - for i in $(cd /sys/class/net && echo *); do - DEVTYPE= - UEVENT_PATH=/sys/class/net/$i/uevent - if [ -e "$UEVENT_PATH" ]; then - source "$UEVENT_PATH" - if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then - args+="''${args:+ -N} -i$i $iface_args" - fi - fi - done - '' else '' - args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}" - ''} - exec wpa_supplicant $args - ''; - }; - - powerManagement.resumeCommands = '' - /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant - ''; - - # Restart wpa_supplicant when a wlan device appears or disappears. - services.udev.extraRules = '' - ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service" + services.dbus.packages = optional cfg.dbusControlled package; + + systemd.services = + if cfg.interfaces == [] + then { wpa_supplicant = mkUnit null; } + else listToAttrs (map (i: nameValuePair "wpa_supplicant-${i}" (mkUnit i)) cfg.interfaces); + + # Restart wpa_supplicant after resuming from sleep + powerManagement.resumeCommands = concatStringsSep "\n" ( + optional (cfg.interfaces == []) "${systemctl} try-restart wpa_supplicant" + ++ map (i: "${systemctl} try-restart wpa_supplicant-${i}") cfg.interfaces + ); + + # Restart wpa_supplicant when a wlan device appears or disappears. This is + # only needed when an interface hasn't been specified by the user. + services.udev.extraRules = optionalString (cfg.interfaces == []) '' + ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", \ + RUN+="${systemctl} try-restart wpa_supplicant.service" ''; }; - meta.maintainers = with lib.maintainers; [ globin ]; + meta.maintainers = with lib.maintainers; [ globin rnhmjoj ]; } diff --git a/nixpkgs/nixos/modules/services/networking/zeronet.nix b/nixpkgs/nixos/modules/services/networking/zeronet.nix index f354a9d42c79..a34b2d871541 100644 --- a/nixpkgs/nixos/modules/services/networking/zeronet.nix +++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix @@ -32,7 +32,7 @@ in with lib; { }; port = mkOption { - type = types.int; + type = types.port; default = 43110; example = 43110; description = "Optional zeronet web UI port."; @@ -41,7 +41,7 @@ in with lib; { fileserverPort = mkOption { # Not optional: when absent zeronet tries to write one to the # read-only config file and crashes - type = types.int; + type = types.port; default = 12261; example = 12261; description = "Zeronet fileserver port."; diff --git a/nixpkgs/nixos/modules/services/search/elasticsearch.nix b/nixpkgs/nixos/modules/services/search/elasticsearch.nix index 91d8f544e16b..440f34b3dc5c 100644 --- a/nixpkgs/nixos/modules/services/search/elasticsearch.nix +++ b/nixpkgs/nixos/modules/services/search/elasticsearch.nix @@ -5,8 +5,6 @@ with lib; let cfg = config.services.elasticsearch; - es6 = builtins.compareVersions cfg.package.version "6" >= 0; - esConfig = '' network.host: ${cfg.listenAddress} cluster.name: ${cfg.cluster_name} @@ -36,7 +34,8 @@ let postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins"; }; -in { +in +{ ###### interface @@ -116,20 +115,20 @@ in { extraCmdLineOptions = mkOption { description = "Extra command line options for the elasticsearch launcher."; - default = []; + default = [ ]; type = types.listOf types.str; }; extraJavaOptions = mkOption { description = "Extra command line options for Java."; - default = []; + default = [ ]; type = types.listOf types.str; example = [ "-Djava.net.preferIPv4Stack=true" ]; }; plugins = mkOption { description = "Extra elasticsearch plugins"; - default = []; + default = [ ]; type = types.listOf types.package; example = lib.literalExample "[ pkgs.elasticsearchPlugins.discovery-ec2 ]"; }; @@ -146,9 +145,7 @@ in { path = [ pkgs.inetutils ]; environment = { ES_HOME = cfg.dataDir; - ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ] - ++ cfg.extraJavaOptions); - } // optionalAttrs es6 { + ES_JAVA_OPTS = toString cfg.extraJavaOptions; ES_PATH_CONF = configDir; }; serviceConfig = { @@ -187,7 +184,10 @@ in { rm -f "${configDir}/logging.yml" cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename} mkdir -p ${configDir}/scripts - ${optionalString es6 "cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options"} + cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options + # redirect jvm logs to the data directory + mkdir -m 0700 -p ${cfg.dataDir}/logs + ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \ if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi ''; diff --git a/nixpkgs/nixos/modules/services/system/uptimed.nix b/nixpkgs/nixos/modules/services/system/uptimed.nix index 1e256c51408e..67a03876e19f 100644 --- a/nixpkgs/nixos/modules/services/system/uptimed.nix +++ b/nixpkgs/nixos/modules/services/system/uptimed.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.uptimed; - stateDir = "/var/spool/uptimed"; + stateDir = "/var/lib/uptimed"; in { options = { @@ -21,12 +21,16 @@ in }; config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.uptimed ]; + users.users.uptimed = { description = "Uptimed daemon user"; home = stateDir; - createHome = true; uid = config.ids.uids.uptimed; + group = "uptimed"; }; + users.groups.uptimed = {}; systemd.services.uptimed = { unitConfig.Documentation = "man:uptimed(8) man:uprecords(1)"; @@ -41,7 +45,7 @@ in PrivateTmp = "yes"; PrivateNetwork = "yes"; NoNewPrivileges = "yes"; - ReadWriteDirectories = stateDir; + StateDirectory = [ "uptimed" ]; InaccessibleDirectories = "/home"; ExecStart = "${pkgs.uptimed}/sbin/uptimed -f -p ${stateDir}/pid"; }; diff --git a/nixpkgs/nixos/modules/services/torrent/deluge.nix b/nixpkgs/nixos/modules/services/torrent/deluge.nix index 7ca4fdcf64d4..151a1dd638d1 100644 --- a/nixpkgs/nixos/modules/services/torrent/deluge.nix +++ b/nixpkgs/nixos/modules/services/torrent/deluge.nix @@ -149,7 +149,7 @@ in { package = mkOption { type = types.package; - example = literalExample "pkgs.deluge-1_x"; + example = literalExample "pkgs.deluge-2_x"; description = '' Deluge package to use. ''; @@ -184,6 +184,13 @@ in { if versionAtLeast config.system.stateVersion "20.09" then pkgs.deluge-2_x else + # deluge-1_x is no longer packaged and this will resolve to an error + # thanks to the alias for this name. This is left here so that anyone + # using NixOS older than 20.09 receives that error when they upgrade + # and is forced to make an intentional choice to switch to deluge-2_x. + # That might be slightly inconvenient but there is no path to + # downgrade from 2.x to 1.x so NixOS should not automatically perform + # this state migration. pkgs.deluge-1_x ); diff --git a/nixpkgs/nixos/modules/services/video/replay-sorcery.nix b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix new file mode 100644 index 000000000000..d78e782c7968 --- /dev/null +++ b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.replay-sorcery; + configFile = generators.toKeyValue {} cfg.settings; +in +{ + options = with types; { + services.replay-sorcery = { + enable = mkEnableOption "the ReplaySorcery service for instant-replays"; + + enableSysAdminCapability = mkEnableOption '' + the system admin capability to support hardware accelerated + video capture. This is equivalent to running ReplaySorcery as + root, so use with caution''; + + autoStart = mkOption { + type = bool; + default = false; + description = "Automatically start ReplaySorcery when graphical-session.target starts."; + }; + + settings = mkOption { + type = attrsOf (oneOf [ str int ]); + default = {}; + description = "System-wide configuration for ReplaySorcery (/etc/replay-sorcery.conf)."; + example = literalExample '' + { + videoInput = "hwaccel"; # requires `services.replay-sorcery.enableSysAdminCapability = true` + videoFramerate = 60; + } + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment = { + systemPackages = [ pkgs.replay-sorcery ]; + etc."replay-sorcery.conf".text = configFile; + }; + + security.wrappers = mkIf cfg.enableSysAdminCapability { + replay-sorcery = { + source = "${pkgs.replay-sorcery}/bin/replay-sorcery"; + capabilities = "cap_sys_admin+ep"; + }; + }; + + systemd = { + packages = [ pkgs.replay-sorcery ]; + user.services.replay-sorcery = { + wantedBy = mkIf cfg.autoStart [ "graphical-session.target" ]; + partOf = mkIf cfg.autoStart [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = mkIf cfg.enableSysAdminCapability [ + "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it. + "${config.security.wrapperDir}/replay-sorcery" + ]; + }; + }; + }; + }; + + meta = { + maintainers = with maintainers; [ kira-bruneau ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/wayland/cage.nix b/nixpkgs/nixos/modules/services/wayland/cage.nix index 2e71abb69fc4..bd97a674eb86 100644 --- a/nixpkgs/nixos/modules/services/wayland/cage.nix +++ b/nixpkgs/nixos/modules/services/wayland/cage.nix @@ -82,7 +82,7 @@ in { auth required pam_unix.so nullok account required pam_unix.so session required pam_unix.so - session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0 + session required pam_env.so conffile=/etc/pam/environment readenv=0 session required ${pkgs.systemd}/lib/security/pam_systemd.so ''; diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix index 8d5302ba267b..050e4ee3d329 100644 --- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix +++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix @@ -771,7 +771,6 @@ in "tmp" "assets/javascripts/plugins" "public" - "plugins" "sockets" ]; RuntimeDirectoryMode = 0750; diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.xml b/nixpkgs/nixos/modules/services/web-apps/discourse.xml index 1d6866e7b352..184c9c6363e5 100644 --- a/nixpkgs/nixos/modules/services/web-apps/discourse.xml +++ b/nixpkgs/nixos/modules/services/web-apps/discourse.xml @@ -284,12 +284,23 @@ services.discourse = { Ruby dependencies are listed in its <filename>plugin.rb</filename> file as function calls to <literal>gem</literal>. To construct the corresponding - <filename>Gemfile</filename>, run <command>bundle + <filename>Gemfile</filename> manually, run <command>bundle init</command>, then add the <literal>gem</literal> lines to it verbatim. </para> <para> + Much of the packaging can be done automatically by the + <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename> + script - just add the plugin to the <literal>plugins</literal> + list in the <function>update_plugins</function> function and run + the script: + <programlisting language="bash"> +./update.py update-plugins +</programlisting>. + </para> + + <para> Some plugins provide <link linkend="module-services-discourse-site-settings">site settings</link>. Their defaults can be configured using <xref diff --git a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix new file mode 100644 index 000000000000..c632b8ff7199 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.fluidd; + moonraker = config.services.moonraker; +in +{ + options.services.fluidd = { + enable = mkEnableOption "Fluidd, a Klipper web interface for managing your 3d printer"; + + package = mkOption { + type = types.package; + description = "Fluidd package to be used in the module"; + default = pkgs.fluidd; + defaultText = "pkgs.fluidd"; + }; + + hostName = mkOption { + type = types.str; + default = "localhost"; + description = "Hostname to serve fluidd on"; + }; + + nginx = mkOption { + type = types.submodule + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }); + default = { }; + example = { + serverAliases = [ "fluidd.\${config.networking.domain}" ]; + }; + description = "Extra configuration for the nginx virtual host of fluidd."; + }; + }; + + config = mkIf cfg.enable { + services.nginx = { + enable = true; + upstreams.fluidd-apiserver.servers."${moonraker.address}:${toString moonraker.port}" = { }; + virtualHosts."${cfg.hostName}" = mkMerge [ + cfg.nginx + { + root = mkForce "${cfg.package}/share/fluidd/htdocs"; + locations = { + "/" = { + index = "index.html"; + tryFiles = "$uri $uri/ /index.html"; + }; + "/index.html".extraConfig = '' + add_header Cache-Control "no-store, no-cache, must-revalidate"; + ''; + "/websocket" = { + proxyWebsockets = true; + proxyPass = "http://fluidd-apiserver/websocket"; + }; + "~ ^/(printer|api|access|machine|server)/" = { + proxyWebsockets = true; + proxyPass = "http://fluidd-apiserver$request_uri"; + }; + }; + } + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/isso.nix b/nixpkgs/nixos/modules/services/web-apps/isso.nix new file mode 100644 index 000000000000..d05a99a3eedc --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/isso.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkEnableOption mkIf mkOption types literalExample; + + cfg = config.services.isso; + + settingsFormat = pkgs.formats.ini { }; + configFile = settingsFormat.generate "isso.conf" cfg.settings; +in { + + options = { + services.isso = { + enable = mkEnableOption '' + A commenting server similar to Disqus. + + Note: The application's author suppose to run isso behind a reverse proxy. + The embedded solution offered by NixOS is also only suitable for small installations + below 20 requests per second. + ''; + + settings = mkOption { + description = '' + Configuration for <package>isso</package>. + + See <link xlink:href="https://posativ.org/isso/docs/configuration/server/">Isso Server Configuration</link> + for supported values. + ''; + + type = types.submodule { + freeformType = settingsFormat.type; + }; + + example = literalExample '' + { + general = { + host = "http://localhost"; + }; + } + ''; + }; + }; + }; + + config = mkIf cfg.enable { + services.isso.settings.general.dbpath = lib.mkDefault "/var/lib/isso/comments.db"; + + systemd.services.isso = { + description = "isso, a commenting server similar to Disqus"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "isso"; + Group = "isso"; + + DynamicUser = true; + + StateDirectory = "isso"; + + ExecStart = '' + ${pkgs.isso}/bin/isso -c ${configFile} + ''; + + Restart = "on-failure"; + RestartSec = 1; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix index dc66c2966564..b1bea222c7f7 100644 --- a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix +++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix @@ -281,7 +281,7 @@ in createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql"; createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql"; - mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} '' + mySqlCaKeystore = pkgs.runCommand "mysql-ca-keystore" {} '' ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt ''; @@ -553,7 +553,7 @@ in jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig'); - keycloakConfig = pkgs.runCommandNoCC "keycloak-config" { + keycloakConfig = pkgs.runCommand "keycloak-config" { nativeBuildInputs = [ cfg.package ]; } '' export JBOSS_BASE_DIR="$(pwd -P)"; diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix index 01710b1bd59c..1bbadafa2078 100644 --- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix +++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix @@ -98,6 +98,29 @@ in EnvironmentFile = if cfg.adminCredentialsFile == null then defaultCredentials else cfg.adminCredentialsFile; + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + UMask = "0077"; }; environment = cfg.config; diff --git a/nixpkgs/nixos/modules/services/web-apps/moodle.nix b/nixpkgs/nixos/modules/services/web-apps/moodle.nix index ad1e55d62d1d..c854e084e14d 100644 --- a/nixpkgs/nixos/modules/services/web-apps/moodle.nix +++ b/nixpkgs/nixos/modules/services/web-apps/moodle.nix @@ -56,7 +56,7 @@ let mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; - phpExt = pkgs.php.withExtensions + phpExt = pkgs.php74.withExtensions ({ enabled, all }: with all; [ iconv mbstring curl openssl tokenizer xmlrpc soap ctype zip gd simplexml dom intl json sqlite3 pgsql pdo_sqlite pdo_pgsql pdo_odbc pdo_mysql pdo mysqli session zlib xmlreader fileinfo filter ]); in { diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix index 111b31734696..ba5f6582cbec 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix @@ -502,8 +502,6 @@ in { ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"''; ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; "--database-pass" = dbpass; - ${if c.dbtableprefix != null - then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"''; "--admin-user" = ''"${c.adminuser}"''; "--admin-pass" = adminpass; "--data-dir" = ''"${cfg.home}/data"''; @@ -699,7 +697,6 @@ in { }; extraConfig = '' index index.php index.html /index.php$request_uri; - expires 1m; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml index 3af37b15dd56..ed84487d233a 100644 --- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml +++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml @@ -84,47 +84,93 @@ </para> </section> - <section xml:id="module-services-nextcloud-pitfalls-during-upgrade"> - <title>Pitfalls</title> - - <para> - Unfortunately Nextcloud appears to be very stateful when it comes to - managing its own configuration. The config file lives in the home directory - of the <literal>nextcloud</literal> user (by default - <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to - track several states of the application (e.g. whether installed or not). - </para> - <para> - All configuration parameters are also stored in - <literal>/var/lib/nextcloud/config/override.config.php</literal> which is generated by - the module and linked from the store to ensure that all values from <literal>config.php</literal> - can be modified by the module. - However <literal>config.php</literal> manages the application's state and shouldn't be touched - manually because of that. - </para> - - <warning> - <para>Don't delete <literal>config.php</literal>! This file - tracks the application's state and a deletion can cause unwanted - side-effects!</para> - </warning> - - <warning> - <para>Don't rerun <literal>nextcloud-occ - maintenance:install</literal>! This command tries to install the application - and can cause unwanted side-effects!</para> - </warning> + <section xml:id="module-services-nextcloud-pitfalls-during-upgrade"> + <title>Common problems</title> + <itemizedlist> + <listitem> + <formalpara> + <title>General notes</title> + <para> + Unfortunately Nextcloud appears to be very stateful when it comes to + managing its own configuration. The config file lives in the home directory + of the <literal>nextcloud</literal> user (by default + <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to + track several states of the application (e.g., whether installed or not). + </para> + </formalpara> + <para> + All configuration parameters are also stored in + <filename>/var/lib/nextcloud/config/override.config.php</filename> which is generated by + the module and linked from the store to ensure that all values from + <filename>config.php</filename> can be modified by the module. + However <filename>config.php</filename> manages the application's state and shouldn't be + touched manually because of that. + </para> + <warning> + <para>Don't delete <filename>config.php</filename>! This file + tracks the application's state and a deletion can cause unwanted + side-effects!</para> + </warning> - <para> - Nextcloud doesn't allow to move more than one major-version forward. If you're e.g. on - <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to - <literal>v17</literal> first. This is ensured automatically as long as the - <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case - the oldest version available (one major behind the one from the previous NixOS - release) will be selected by default and the module will generate a warning that reminds - the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy. - </para> + <warning> + <para>Don't rerun <literal>nextcloud-occ + maintenance:install</literal>! This command tries to install the application + and can cause unwanted side-effects!</para> + </warning> + </listitem> + <listitem> + <formalpara> + <title>Multiple version upgrades</title> + <para> + Nextcloud doesn't allow to move more than one major-version forward. E.g., if you're on + <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to + <literal>v17</literal> first. This is ensured automatically as long as the + <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case + the oldest version available (one major behind the one from the previous NixOS + release) will be selected by default and the module will generate a warning that reminds + the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy. + </para> + </formalpara> + </listitem> + <listitem> + <formalpara> + <title><literal>Error: Command "upgrade" is not defined.</literal></title> + <para> + This error usually occurs if the initial installation + (<command>nextcloud-occ maintenance:install</command>) has failed. After that, the application + is not installed, but the upgrade is attempted to be executed. Further context can + be found in <link xlink:href="https://github.com/NixOS/nixpkgs/issues/111175">NixOS/nixpkgs#111175</link>. + </para> + </formalpara> + <para> + First of all, it makes sense to find out what went wrong by looking at the logs + of the installation via <command>journalctl -u nextcloud-setup</command> and try to fix + the underlying issue. + </para> + <itemizedlist> + <listitem> + <para> + If this occurs on an <emphasis>existing</emphasis> setup, this is most likely because + the maintenance mode is active. It can be deactivated by running + <command>nextcloud-occ maintenance:mode --off</command>. It's advisable though to + check the logs first on why the maintenance mode was activated. + </para> + </listitem> + <listitem> + <warning><para>Only perform the following measures on + <emphasis>freshly installed instances!</emphasis></para></warning> + <para> + A re-run of the installer can be forced by <emphasis>deleting</emphasis> + <filename>/var/lib/nextcloud/config/config.php</filename>. This is the only time + advisable because the fresh install doesn't have any state that can be lost. + In case that doesn't help, an entire re-creation can be forced via + <command>rm -rf ~nextcloud/</command>. + </para> + </listitem> + </itemizedlist> + </listitem> + </itemizedlist> </section> <section xml:id="module-services-nextcloud-httpd"> diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix new file mode 100644 index 000000000000..4f6850ace214 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.node-red; + defaultUser = "node-red"; + finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package; + node-red_withNpmAndGcc = pkgs.runCommand "node-red" { + nativeBuildInputs = [ pkgs.makeWrapper ]; + } + '' + mkdir -p $out/bin + makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \ + --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \ + ''; +in +{ + options.services.node-red = { + enable = mkEnableOption "the Node-RED service"; + + package = mkOption { + default = pkgs.nodePackages.node-red; + defaultText = "pkgs.nodePackages.node-red"; + type = types.package; + description = "Node-RED package to use."; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the server. + ''; + }; + + withNpmAndGcc = mkOption { + type = types.bool; + default = false; + description = '' + Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be + downloaded and managed imperatively via the 'Palette Manager'. + ''; + }; + + configFile = mkOption { + type = types.path; + default = "${cfg.package}/lib/node_modules/node-red/settings.js"; + defaultText = "\${cfg.package}/lib/node_modules/node-red/settings.js"; + description = '' + Path to the JavaScript configuration file. + See <link + xlink:href="https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js"/> + for a configuration example. + ''; + }; + + port = mkOption { + type = types.port; + default = 1880; + description = "Listening port."; + }; + + user = mkOption { + type = types.str; + default = defaultUser; + description = '' + User under which Node-RED runs.If left as the default value this user + will automatically be created on system activation, otherwise the + sysadmin is responsible for ensuring the user exists. + ''; + }; + + group = mkOption { + type = types.str; + default = defaultUser; + description = '' + Group under which Node-RED runs.If left as the default value this group + will automatically be created on system activation, otherwise the + sysadmin is responsible for ensuring the group exists. + ''; + }; + + userDir = mkOption { + type = types.path; + default = "/var/lib/node-red"; + description = '' + The directory to store all user data, such as flow and credential files and all library data. If left + as the default value this directory will automatically be created before the node-red service starts, + otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership + and permissions. + ''; + }; + + safe = mkOption { + type = types.bool; + default = false; + description = "Whether to launch Node-RED in --safe mode."; + }; + + define = mkOption { + type = types.attrs; + default = {}; + description = "List of settings.js overrides to pass via -D to Node-RED."; + example = literalExample '' + { + "logging.console.level" = "trace"; + } + ''; + }; + }; + + config = mkIf cfg.enable { + users.users = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = { + isSystemUser = true; + }; + }; + + users.groups = optionalAttrs (cfg.group == defaultUser) { + ${defaultUser} = { }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + + systemd.services.node-red = { + description = "Node-RED Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" ]; + environment = { + HOME = cfg.userDir; + }; + serviceConfig = mkMerge [ + { + User = cfg.user; + Group = cfg.group; + ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}"; + PrivateTmp = true; + Restart = "always"; + WorkingDirectory = cfg.userDir; + } + (mkIf (cfg.userDir == "/var/lib/node-red") { StateDirectory = "node-red"; }) + ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix index b78487cc9281..bc18c824f394 100644 --- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix @@ -6,10 +6,6 @@ let configVersion = 26; - cacheDir = "cache"; - lockDir = "lock"; - feedIconsDir = "feed-icons"; - dbPort = if cfg.database.port == null then (if cfg.database.type == "pgsql" then 5432 else 3306) else cfg.database.port; @@ -19,86 +15,104 @@ let mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; - tt-rss-config = pkgs.writeText "config.php" '' + tt-rss-config = let + password = + if (cfg.database.password != null) then + "${(escape ["'" "\\"] cfg.database.password)}" + else if (cfg.database.passwordFile != null) then + "file_get_contents('${cfg.database.passwordFile}'" + else + "" + ; + in pkgs.writeText "config.php" '' <?php + putenv('TTRSS_PHP_EXECUTABLE=${pkgs.php}/bin/php'); - define('PHP_EXECUTABLE', '${pkgs.php}/bin/php'); - - define('LOCK_DIRECTORY', '${lockDir}'); - define('CACHE_DIR', '${cacheDir}'); - define('ICONS_DIR', '${feedIconsDir}'); - define('ICONS_URL', '${feedIconsDir}'); - define('SELF_URL_PATH', '${cfg.selfUrlPath}'); + putenv('TTRSS_LOCK_DIRECTORY=${cfg.root}/lock'); + putenv('TTRSS_CACHE_DIR=${cfg.root}/cache'); + putenv('TTRSS_ICONS_DIR=${cfg.root}/feed-icons'); + putenv('TTRSS_ICONS_URL=feed-icons'); + putenv('TTRSS_SELF_URL_PATH=${cfg.selfUrlPath}'); - define('MYSQL_CHARSET', 'UTF8'); + putenv('TTRSS_MYSQL_CHARSET=UTF8'); - define('DB_TYPE', '${cfg.database.type}'); - define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}'); - define('DB_USER', '${cfg.database.user}'); - define('DB_NAME', '${cfg.database.name}'); - define('DB_PASS', ${ - if (cfg.database.password != null) then - "'${(escape ["'" "\\"] cfg.database.password)}'" - else if (cfg.database.passwordFile != null) then - "file_get_contents('${cfg.database.passwordFile}')" - else - "''" - }); - define('DB_PORT', '${toString dbPort}'); + putenv('TTRSS_DB_TYPE=${cfg.database.type}'); + putenv('TTRSS_DB_HOST=${optionalString (cfg.database.host != null) cfg.database.host}'); + putenv('TTRSS_DB_USER=${cfg.database.user}'); + putenv('TTRSS_DB_NAME=${cfg.database.name}'); + putenv('TTRSS_DB_PASS=${password}'); + putenv('TTRSS_DB_PORT=${toString dbPort}'); - define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate}); - define('AUTH_AUTO_LOGIN', ${boolToString cfg.auth.autoLogin}); + putenv('TTRSS_AUTH_AUTO_CREATE=${boolToString cfg.auth.autoCreate}'); + putenv('TTRSS_AUTH_AUTO_LOGIN=${boolToString cfg.auth.autoLogin}'); - define('FEED_CRYPT_KEY', '${escape ["'" "\\"] cfg.feedCryptKey}'); + putenv('TTRSS_FEED_CRYPT_KEY=${escape ["'" "\\"] cfg.feedCryptKey}'); - define('SINGLE_USER_MODE', ${boolToString cfg.singleUserMode}); + putenv('TTRSS_SINGLE_USER_MODE=${boolToString cfg.singleUserMode}'); - define('SIMPLE_UPDATE_MODE', ${boolToString cfg.simpleUpdateMode}); + putenv('TTRSS_SIMPLE_UPDATE_MODE=${boolToString cfg.simpleUpdateMode}'); - // Never check for updates - the running version of the code should be - // controlled entirely by the version of TT-RSS active in the current Nix - // profile. If TT-RSS updates itself to a version requiring a database - // schema upgrade, and then the SystemD tt-rss.service is restarted, the - // old code copied from the Nix store will overwrite the updated version, - // causing the code to detect the need for a schema "upgrade" (since the - // schema version in the database is different than in the code), but the - // update schema operation in TT-RSS will do nothing because the schema - // version in the database is newer than that in the code. - define('CHECK_FOR_UPDATES', false); + # Never check for updates - the running version of the code should + # be controlled entirely by the version of TT-RSS active in the + # current Nix profile. If TT-RSS updates itself to a version + # requiring a database schema upgrade, and then the SystemD + # tt-rss.service is restarted, the old code copied from the Nix + # store will overwrite the updated version, causing the code to + # detect the need for a schema "upgrade" (since the schema version + # in the database is different than in the code), but the update + # schema operation in TT-RSS will do nothing because the schema + # version in the database is newer than that in the code. + putenv('TTRSS_CHECK_FOR_UPDATES=false'); - define('FORCE_ARTICLE_PURGE', ${toString cfg.forceArticlePurge}); - define('SESSION_COOKIE_LIFETIME', ${toString cfg.sessionCookieLifetime}); - define('ENABLE_GZIP_OUTPUT', ${boolToString cfg.enableGZipOutput}); + putenv('TTRSS_FORCE_ARTICLE_PURGE=${toString cfg.forceArticlePurge}'); + putenv('TTRSS_SESSION_COOKIE_LIFETIME=${toString cfg.sessionCookieLifetime}'); + putenv('TTRSS_ENABLE_GZIP_OUTPUT=${boolToString cfg.enableGZipOutput}'); - define('PLUGINS', '${builtins.concatStringsSep "," cfg.plugins}'); + putenv('TTRSS_PLUGINS=${builtins.concatStringsSep "," cfg.plugins}'); - define('LOG_DESTINATION', '${cfg.logDestination}'); - define('CONFIG_VERSION', ${toString configVersion}); + putenv('TTRSS_LOG_DESTINATION=${cfg.logDestination}'); + putenv('TTRSS_CONFIG_VERSION=${toString configVersion}'); - define('PUBSUBHUBBUB_ENABLED', ${boolToString cfg.pubSubHubbub.enable}); - define('PUBSUBHUBBUB_HUB', '${cfg.pubSubHubbub.hub}'); + putenv('TTRSS_PUBSUBHUBBUB_ENABLED=${boolToString cfg.pubSubHubbub.enable}'); + putenv('TTRSS_PUBSUBHUBBUB_HUB=${cfg.pubSubHubbub.hub}'); - define('SPHINX_SERVER', '${cfg.sphinx.server}'); - define('SPHINX_INDEX', '${builtins.concatStringsSep "," cfg.sphinx.index}'); + putenv('TTRSS_SPHINX_SERVER=${cfg.sphinx.server}'); + putenv('TTRSS_SPHINX_INDEX=${builtins.concatStringsSep "," cfg.sphinx.index}'); - define('ENABLE_REGISTRATION', ${boolToString cfg.registration.enable}); - define('REG_NOTIFY_ADDRESS', '${cfg.registration.notifyAddress}'); - define('REG_MAX_USERS', ${toString cfg.registration.maxUsers}); + putenv('TTRSS_ENABLE_REGISTRATION=${boolToString cfg.registration.enable}'); + putenv('TTRSS_REG_NOTIFY_ADDRESS=${cfg.registration.notifyAddress}'); + putenv('TTRSS_REG_MAX_USERS=${toString cfg.registration.maxUsers}'); - define('SMTP_SERVER', '${cfg.email.server}'); - define('SMTP_LOGIN', '${cfg.email.login}'); - define('SMTP_PASSWORD', '${escape ["'" "\\"] cfg.email.password}'); - define('SMTP_SECURE', '${cfg.email.security}'); + putenv('TTRSS_SMTP_SERVER=${cfg.email.server}'); + putenv('TTRSS_SMTP_LOGIN=${cfg.email.login}'); + putenv('TTRSS_SMTP_PASSWORD=${escape ["'" "\\"] cfg.email.password}'); + putenv('TTRSS_SMTP_SECURE=${cfg.email.security}'); - define('SMTP_FROM_NAME', '${escape ["'" "\\"] cfg.email.fromName}'); - define('SMTP_FROM_ADDRESS', '${escape ["'" "\\"] cfg.email.fromAddress}'); - define('DIGEST_SUBJECT', '${escape ["'" "\\"] cfg.email.digestSubject}'); + putenv('TTRSS_SMTP_FROM_NAME=${escape ["'" "\\"] cfg.email.fromName}'); + putenv('TTRSS_SMTP_FROM_ADDRESS=${escape ["'" "\\"] cfg.email.fromAddress}'); + putenv('TTRSS_DIGEST_SUBJECT=${escape ["'" "\\"] cfg.email.digestSubject}'); ${cfg.extraConfig} ''; + # tt-rss and plugins and themes and config.php + servedRoot = pkgs.runCommand "tt-rss-served-root" {} '' + cp --no-preserve=mode -r ${pkgs.tt-rss} $out + cp ${tt-rss-config} $out/config.php + ${optionalString (cfg.pluginPackages != []) '' + for plugin in ${concatStringsSep " " cfg.pluginPackages}; do + cp -r "$plugin"/* "$out/plugins.local/" + done + ''} + ${optionalString (cfg.themePackages != []) '' + for theme in ${concatStringsSep " " cfg.themePackages}; do + cp -r "$theme"/* "$out/themes.local/" + done + ''} + ''; + in { ###### interface @@ -542,12 +556,16 @@ let enable = true; virtualHosts = { ${cfg.virtualHost} = { - root = "${cfg.root}"; + root = "${cfg.root}/www"; locations."/" = { index = "index.php"; }; + locations."^~ /feed-icons" = { + root = "${cfg.root}"; + }; + locations."~ \\.php$" = { extraConfig = '' fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -560,13 +578,22 @@ let }; systemd.tmpfiles.rules = [ - "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -" - "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}' 0555 ${cfg.user} tt_rss - -" + "d '${cfg.root}/lock' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}/cache' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}/cache/upload' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}/cache/images' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}/cache/export' 0755 ${cfg.user} tt_rss - -" + "d '${cfg.root}/feed-icons' 0755 ${cfg.user} tt_rss - -" + "L+ '${cfg.root}/www' - - - - ${servedRoot}" ]; - systemd.services.tt-rss = - { + systemd.services = { + phpfpm-tt-rss = mkIf (cfg.pool == "${poolName}") { + restartTriggers = [ servedRoot ]; + }; + tt-rss = { description = "Tiny Tiny RSS feeds update daemon"; preStart = let @@ -589,24 +616,7 @@ let else ""; - in '' - rm -rf "${cfg.root}/*" - cp -r "${pkgs.tt-rss}/"* "${cfg.root}" - ${optionalString (cfg.pluginPackages != []) '' - for plugin in ${concatStringsSep " " cfg.pluginPackages}; do - cp -r "$plugin"/* "${cfg.root}/plugins.local/" - done - ''} - ${optionalString (cfg.themePackages != []) '' - for theme in ${concatStringsSep " " cfg.themePackages}; do - cp -r "$theme"/* "${cfg.root}/themes.local/" - done - ''} - ln -sf "${tt-rss-config}" "${cfg.root}/config.php" - chmod -R 755 "${cfg.root}" - '' - - + (optionalString (cfg.database.type == "pgsql") '' + in (optionalString (cfg.database.type == "pgsql") '' exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \ | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//') @@ -631,7 +641,7 @@ let serviceConfig = { User = "${cfg.user}"; Group = "tt_rss"; - ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon --quiet"; + ExecStart = "${pkgs.php}/bin/php ${cfg.root}/www/update.php --daemon --quiet"; Restart = "on-failure"; RestartSec = "60"; SyslogIdentifier = "tt-rss"; @@ -640,6 +650,7 @@ let wantedBy = [ "multi-user.target" ]; requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + }; }; services.mysql = mkIf mysqlLocal { diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix index df7035c03cc2..ceb199870975 100644 --- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -36,11 +36,12 @@ let dependentCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts); mkListenInfo = hostOpts: - if hostOpts.listen != [] then hostOpts.listen - else ( - optional (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) { ip = "*"; port = 443; ssl = true; } ++ - optional (!hostOpts.onlySSL) { ip = "*"; port = 80; ssl = false; } - ); + if hostOpts.listen != [] then + hostOpts.listen + else + optionals (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) (map (addr: { ip = addr; port = 443; ssl = true; }) hostOpts.listenAddresses) ++ + optionals (!hostOpts.onlySSL) (map (addr: { ip = addr; port = 80; ssl = false; }) hostOpts.listenAddresses) + ; listenInfo = unique (concatMap mkListenInfo vhosts); @@ -462,7 +463,7 @@ in default = "common"; example = "combined"; description = '' - Log format for log files. Possible values are: combined, common, referer, agent. + Log format for log files. Possible values are: combined, common, referer, agent, none. See <link xlink:href="https://httpd.apache.org/docs/2.4/logs.html"/> for more details. ''; }; diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix index 394f9a305546..3f732a5c9f33 100644 --- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix +++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix @@ -47,10 +47,27 @@ in ]; description = '' Listen addresses and ports for this virtual host. - <note><para> + <note> + <para> This option overrides <literal>addSSL</literal>, <literal>forceSSL</literal> and <literal>onlySSL</literal>. - </para></note> + </para> + <para> + If you only want to set the addresses manually and not the ports, take a look at <literal>listenAddresses</literal>. + </para> + </note> + ''; + }; + + listenAddresses = mkOption { + type = with types; nonEmptyListOf str; + + description = '' + Listen addresses for this virtual host. + Compared to <literal>listen</literal> this only sets the addreses + and the ports are chosen automatically. ''; + default = [ "*" ]; + example = [ "127.0.0.1" ]; }; enableSSL = mkOption { diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix index 955b9756406d..fd7102096343 100644 --- a/nixpkgs/nixos/modules/services/web-servers/caddy.nix +++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix @@ -4,42 +4,57 @@ with lib; let cfg = config.services.caddy; - configFile = pkgs.writeText "Caddyfile" cfg.config; + vhostToConfig = vhostName: vhostAttrs: '' + ${vhostName} ${builtins.concatStringsSep " " vhostAttrs.serverAliases} { + ${vhostAttrs.extraConfig} + } + ''; + configFile = pkgs.writeText "Caddyfile" (builtins.concatStringsSep "\n" + ([ cfg.config ] ++ (mapAttrsToList vhostToConfig cfg.virtualHosts))); + + formattedConfig = pkgs.runCommand "formattedCaddyFile" { } '' + ${cfg.package}/bin/caddy fmt ${configFile} > $out + ''; tlsConfig = { apps.tls.automation.policies = [{ - issuer = { + issuers = [{ inherit (cfg) ca email; module = "acme"; - }; + }]; }]; }; adaptedConfig = pkgs.runCommand "caddy-config-adapted.json" { } '' ${cfg.package}/bin/caddy adapt \ - --config ${configFile} --adapter ${cfg.adapter} > $out + --config ${formattedConfig} --adapter ${cfg.adapter} > $out ''; tlsJSON = pkgs.writeText "tls.json" (builtins.toJSON tlsConfig); # merge the TLS config options we expose with the ones originating in the Caddyfile configJSON = - let tlsConfigMerge = '' - {"apps": - {"tls": - {"automation": - {"policies": - (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies - then .[0].apps.tls.automation.policies - else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies) - end) + if cfg.ca != null then + let tlsConfigMerge = '' + {"apps": + {"tls": + {"automation": + {"policies": + (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies + then .[0].apps.tls.automation.policies + else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies) + end) + } } } - } - }''; - in pkgs.runCommand "caddy-config.json" { } '' - ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out - ''; -in { + }''; + in + pkgs.runCommand "caddy-config.json" { } '' + ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out + '' + else + adaptedConfig; +in +{ imports = [ (mkRemovedOptionModule [ "services" "caddy" "agree" ] "this option is no longer necessary for Caddy 2") ]; @@ -63,6 +78,27 @@ in { ''; }; + virtualHosts = mkOption { + type = types.attrsOf (types.submodule (import ./vhost-options.nix { + inherit config lib; + })); + default = { }; + example = literalExample '' + { + "hydra.example.com" = { + serverAliases = [ "www.hydra.example.com" ]; + extraConfig = '''''' + encode gzip + log + root /srv/http + ''''''; + }; + }; + ''; + description = "Declarative vhost config"; + }; + + user = mkOption { default = "caddy"; type = types.str; @@ -85,11 +121,24 @@ in { ''; }; + resume = mkOption { + default = false; + type = types.bool; + description = '' + Use saved config, if any (and prefer over configuration passed with <option>services.caddy.config</option>). + ''; + }; + ca = mkOption { default = "https://acme-v02.api.letsencrypt.org/directory"; example = "https://acme-staging-v02.api.letsencrypt.org/directory"; - type = types.str; - description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people."; + type = types.nullOr types.str; + description = '' + Certificate authority ACME server. The default (Let's Encrypt + production server) should be fine for most people. Set it to null if + you don't want to include any authority (or if you want to write a more + fine-graned configuration manually) + ''; }; email = mkOption { @@ -132,7 +181,7 @@ in { startLimitIntervalSec = 14400; startLimitBurst = 10; serviceConfig = { - ExecStart = "${cfg.package}/bin/caddy run --config ${configJSON}"; + ExecStart = "${cfg.package}/bin/caddy run ${optionalString cfg.resume "--resume"} --config ${configJSON}"; ExecReload = "${cfg.package}/bin/caddy reload --config ${configJSON}"; Type = "simple"; User = cfg.user; diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix new file mode 100644 index 000000000000..1f74295fc9a2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix @@ -0,0 +1,28 @@ +# This file defines the options that can be used both for the Nginx +# main server configuration, and for the virtual hosts. (The latter +# has additional options that affect the web server as a whole, like +# the user/group to run under.) + +{ lib, ... }: + +with lib; +{ + options = { + serverAliases = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "www.example.org" "example.org" ]; + description = '' + Additional names of virtual hosts served by this virtual host configuration. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + These lines go into the vhost verbatim + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/web-servers/minio.nix b/nixpkgs/nixos/modules/services/web-servers/minio.nix index d075449012f7..6b10afad4991 100644 --- a/nixpkgs/nixos/modules/services/web-servers/minio.nix +++ b/nixpkgs/nixos/modules/services/web-servers/minio.nix @@ -19,7 +19,13 @@ in listenAddress = mkOption { default = ":9000"; type = types.str; - description = "Listen on a specific IP address and port."; + description = "IP address and port of the server."; + }; + + consoleAddress = mkOption { + default = ":9001"; + type = types.str; + description = "IP address and port of the web UI (console)."; }; dataDir = mkOption { @@ -99,7 +105,7 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}"; + ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --console-address ${cfg.consoleAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}"; Type = "simple"; User = "minio"; Group = "minio"; diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix index ebb3c38d6c25..6682472fdb8e 100644 --- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix @@ -22,7 +22,9 @@ let } // (optionalAttrs (vhostConfig.enableACME || vhostConfig.useACMEHost != null) { sslCertificate = "${certs.${certName}.directory}/fullchain.pem"; sslCertificateKey = "${certs.${certName}.directory}/key.pem"; - sslTrustedCertificate = "${certs.${certName}.directory}/chain.pem"; + sslTrustedCertificate = if vhostConfig.sslTrustedCertificate != null + then vhostConfig.sslTrustedCertificate + else "${certs.${certName}.directory}/chain.pem"; }) ) cfg.virtualHosts; enableIPv6 = config.networking.enableIPv6; @@ -169,6 +171,14 @@ let map_hash_max_size ${toString cfg.mapHashMaxSize}; ''} + ${optionalString (cfg.serverNamesHashBucketSize != null) '' + server_names_hash_bucket_size ${toString cfg.serverNamesHashBucketSize}; + ''} + + ${optionalString (cfg.serverNamesHashMaxSize != null) '' + server_names_hash_max_size ${toString cfg.serverNamesHashMaxSize}; + ''} + # $connection_upgrade is used for websocket proxying map $http_upgrade $connection_upgrade { default upgrade; @@ -230,13 +240,13 @@ let defaultListen = if vhost.listen != [] then vhost.listen - else optionals (hasSSL || vhost.rejectSSL) ( - singleton { addr = "0.0.0.0"; port = 443; ssl = true; } - ++ optional enableIPv6 { addr = "[::]"; port = 443; ssl = true; } - ) ++ optionals (!onlySSL) ( - singleton { addr = "0.0.0.0"; port = 80; ssl = false; } - ++ optional enableIPv6 { addr = "[::]"; port = 80; ssl = false; } - ); + else + let addrs = if vhost.listenAddresses != [] then vhost.listenAddresses else ( + [ "0.0.0.0" ] ++ optional enableIPv6 "[::0]" + ); + in + optionals (hasSSL || vhost.rejectSSL) (map (addr: { inherit addr; port = 443; ssl = true; }) addrs) + ++ optionals (!onlySSL) (map (addr: { inherit addr; port = 80; ssl = false; }) addrs); hostListen = if vhost.forceSSL @@ -641,6 +651,23 @@ in ''; }; + serverNamesHashBucketSize = mkOption { + type = types.nullOr types.ints.positive; + default = null; + description = '' + Sets the bucket size for the server names hash tables. Default + value depends on the processor’s cache line size. + ''; + }; + + serverNamesHashMaxSize = mkOption { + type = types.nullOr types.ints.positive; + default = null; + description = '' + Sets the maximum size of the server names hash tables. + ''; + }; + resolver = mkOption { type = types.submodule { options = { diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix index bc18bcaa7b34..94645e927f86 100644 --- a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -43,7 +43,24 @@ with lib; IPv6 addresses must be enclosed in square brackets. Note: this option overrides <literal>addSSL</literal> and <literal>onlySSL</literal>. + + If you only want to set the addresses manually and not + the ports, take a look at <literal>listenAddresses</literal> + ''; + }; + + listenAddresses = mkOption { + type = with types; listOf str; + + description = '' + Listen addresses for this virtual host. + Compared to <literal>listen</literal> this only sets the addreses + and the ports are choosen automatically. + + Note: This option overrides <literal>enableIPv6</literal> ''; + default = []; + example = [ "127.0.0.1" "::1" ]; }; enableACME = mkOption { @@ -145,7 +162,7 @@ with lib; sslTrustedCertificate = mkOption { type = types.nullOr types.path; default = null; - example = "/var/root.cert"; + example = "\${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; description = "Path to root SSL certificate for stapling and client certificates."; }; diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix index b0859321a525..4bc42525906c 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix @@ -372,6 +372,13 @@ in xdg.portal.enable = true; xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; + # Harmonize Qt5 application style and also make them use the portal for file chooser dialog. + qt5 = { + enable = mkDefault true; + platformTheme = mkDefault "gnome"; + style = mkDefault "adwaita"; + }; + networking.networkmanager.enable = mkDefault true; services.xserver.updateDbusEnvironment = true; diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix index b6be524aea66..aac905fea437 100644 --- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -271,13 +271,14 @@ in kmenuedit kscreen kscreenlocker - ksysguard + ksystemstats kwayland kwin kwrited libkscreen libksysguard milou + plasma-systemmonitor plasma-browser-integration plasma-integration polkit-kde-agent diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix index dec5c06cb3ca..861976d1186f 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix @@ -39,6 +39,6 @@ python3.pkgs.buildPythonApplication { ''; meta = with lib; { - maintainers = with maintainers; [ ]; + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; } diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix index e04fcdaf4145..584dfb63c4dc 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix @@ -18,7 +18,6 @@ let fontconfig = config.fonts.fontconfig; xresourcesXft = pkgs.writeText "Xresources-Xft" '' - ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''} Xft.antialias: ${if fontconfig.antialias then "1" else "0"} Xft.rgba: ${fontconfig.subpixel.rgba} Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter} diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix index ef9ec438cc1c..5c4c6c67fd02 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix @@ -164,6 +164,11 @@ in systemd.packages = with pkgs.gnome; [ gdm gnome-session gnome-shell ]; environment.systemPackages = [ pkgs.gnome.adwaita-icon-theme ]; + # We dont use the upstream gdm service + # it has to be disabled since the gdm package has it + # https://github.com/NixOS/nixpkgs/issues/108672 + systemd.services.gdm.enable = false; + systemd.services.display-manager.wants = [ # Because sd_login_monitor_new requires /run/systemd/machines "systemd-machined.service" @@ -309,7 +314,7 @@ in password required pam_deny.so session required pam_succeed_if.so audit quiet_success user = gdm - session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0 + session required pam_env.so conffile=/etc/pam/environment readenv=0 session optional ${pkgs.systemd}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix index 76f16646cf5e..f18e4a914e57 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix @@ -10,8 +10,8 @@ let in { - meta = { - maintainers = with maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; options = { diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix index 3d497c9f25ee..41c1b635f5d6 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix @@ -69,8 +69,8 @@ let in { - meta = { - maintainers = with maintainers; [ ]; + meta = with lib; { + maintainers = with maintainers; [ ] ++ teams.pantheon.members; }; # Note: the order in which lightdm greeter modules are imported @@ -284,7 +284,7 @@ in password required pam_deny.so session required pam_succeed_if.so audit quiet_success user = lightdm - session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0 + session required pam_env.so conffile=/etc/pam/environment readenv=0 session optional ${pkgs.systemd}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix index 116994db1c14..d79b3cda2fcc 100644 --- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix @@ -229,7 +229,7 @@ in password required pam_deny.so session required pam_succeed_if.so audit quiet_success user = sddm - session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0 + session required pam_env.so conffile=/etc/pam/environment readenv=0 session optional ${pkgs.systemd}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py index 0cca80af44e8..75940efe32b4 100755 --- a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py +++ b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py @@ -72,11 +72,14 @@ def main(): f"Setting session name: {session}, as we found the existing wayland-session: {session_file}" ) user.set_session(session) + user.set_session_type("wayland") elif is_session_xsession(session_file): logging.debug( f"Setting session name: {session}, as we found the existing xsession: {session_file}" ) user.set_x_session(session) + user.set_session(session) + user.set_session_type("x11") else: logging.error(f"Couldn't figure out session type for {session_file}") sys.exit(1) diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix new file mode 100644 index 000000000000..132531c0ddc0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.xserver.displayManager.sx; + +in { + options = { + services.xserver.displayManager.sx = { + enable = mkEnableOption "sx pseudo-display manager" // { + description = '' + Whether to enable the "sx" pseudo-display manager, which allows users + to start manually via the "sx" command from a vt shell. The X server + runs under the user's id, not as root. The user must provide a + ~/.config/sx/sxrc file containing session startup commands, see + sx(1). This is not automatically generated from the desktopManager + and windowManager settings. sx doesn't have a way to directly set + X server flags, but it can be done by overriding its xorgserver + dependency. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.sx ]; + services.xserver = { + exportConfiguration = true; + displayManager = { + job.execCmd = ""; + lightdm.enable = mkForce false; + }; + logFile = mkDefault null; + }; + systemd.services.display-manager.enable = false; + }; +} diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix index 089e9f769f0a..37a14e34f57e 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix @@ -27,7 +27,7 @@ in default = []; type = types.listOf types.package; description = "List of lua packages available for being used in the Awesome configuration."; - example = literalExample "[ luaPackages.oocairo ]"; + example = literalExample "[ pkgs.luaPackages.vicious ]"; }; package = mkOption { diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix index 171660c53ac3..5015852db69f 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix @@ -8,17 +8,27 @@ in { options = { - services.xserver.windowManager.clfswm.enable = mkEnableOption "clfswm"; + services.xserver.windowManager.clfswm = { + enable = mkEnableOption "clfswm"; + package = mkOption { + type = types.package; + default = pkgs.lispPackages.clfswm; + defaultText = "pkgs.lispPackages.clfswm"; + description = '' + clfswm package to use. + ''; + }; + }; }; config = mkIf cfg.enable { services.xserver.windowManager.session = singleton { name = "clfswm"; start = '' - ${pkgs.lispPackages.clfswm}/bin/clfswm & + ${cfg.package}/bin/clfswm & waitPID=$! ''; }; - environment.systemPackages = [ pkgs.lispPackages.clfswm ]; + environment.systemPackages = [ cfg.package ]; }; } diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix index 53285fbce877..d71738ea633f 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix @@ -26,6 +26,7 @@ in ./leftwm.nix ./lwm.nix ./metacity.nix + ./mlvwm.nix ./mwm.nix ./openbox.nix ./pekwm.nix diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix new file mode 100644 index 000000000000..08dd04020296 --- /dev/null +++ b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix @@ -0,0 +1,41 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.xserver.windowManager.mlvwm; + +in +{ + + options.services.xserver.windowManager.mlvwm = { + enable = mkEnableOption "Macintosh-like Virtual Window Manager"; + + configFile = mkOption { + default = null; + type = with types; nullOr path; + description = '' + Path to the mlvwm configuration file. + If left at the default value, $HOME/.mlvwmrc will be used. + ''; + }; + }; + + config = mkIf cfg.enable { + + services.xserver.windowManager.session = [{ + name = "mlvwm"; + start = '' + ${pkgs.mlvwm}/bin/mlvwm ${optionalString (cfg.configFile != null) + "-f /etc/mlvwm/mlvwmrc" + } & + waitPID=$! + ''; + }]; + + environment.etc."mlvwm/mlvwmrc" = mkIf (cfg.configFile != null) { + source = cfg.configFile; + }; + + environment.systemPackages = [ pkgs.mlvwm ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix index cadc316bbc4f..835b41d4ada9 100644 --- a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix +++ b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix @@ -15,7 +15,7 @@ in services.xserver.windowManager.session = [{ name = "qtile"; start = '' - ${pkgs.qtile}/bin/qtile & + ${pkgs.qtile}/bin/qtile start & waitPID=$! ''; }]; diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix index 37e004ae80a7..ad9bd88f98aa 100644 --- a/nixpkgs/nixos/modules/services/x11/xserver.nix +++ b/nixpkgs/nixos/modules/services/x11/xserver.nix @@ -297,7 +297,11 @@ in dpi = mkOption { type = types.nullOr types.int; default = null; - description = "DPI resolution to use for X server."; + description = '' + Force global DPI resolution to use for X server. It's recommended to + use this only when DPI is detected incorrectly; also consider using + <literal>Monitor</literal> section in configuration file instead. + ''; }; updateDbusEnvironment = mkOption { @@ -657,6 +661,7 @@ in pkgs.xterm pkgs.xdg-utils xorg.xf86inputevdev.out # get evdev.4 man page + pkgs.nixos-icons # needed for gnome and pantheon about dialog, nixos-manual and maybe more ] ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh; @@ -680,7 +685,7 @@ in systemd.services.display-manager = { description = "X11 Server"; - after = [ "acpid.service" "systemd-logind.service" ]; + after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ]; restartIfChanged = false; |