diff options
author | github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> | 2023-12-06 00:02:22 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-06 00:02:22 +0000 |
commit | 2622221e95392f62f81ae60c35504e338e9ac2cf (patch) | |
tree | a23c2759fc45495f899f07220bb1d8b1ecde1253 /nixos | |
parent | 9497a77fed58d22866d632901da3ed344e82f2b5 (diff) | |
parent | ad7955279dd0dcab03e02767a5794668cb160c04 (diff) | |
download | nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar.gz nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar.bz2 nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar.lz nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar.xz nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.tar.zst nixlib-2622221e95392f62f81ae60c35504e338e9ac2cf.zip |
Merge master into staging-next
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2405.section.md | 2 | ||||
-rwxr-xr-x | nixos/maintainers/scripts/ec2/create-amis.sh | 54 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/monitoring/ups.nix | 421 | ||||
-rw-r--r-- | nixos/modules/services/security/clamav.nix | 52 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/windmill.nix | 177 | ||||
-rw-r--r-- | nixos/modules/system/boot/networkd.nix | 2 |
7 files changed, 640 insertions, 69 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index af0e2a2fe40e..ebc67e98b193 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -27,6 +27,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> +- The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS. + - `mkosi` was updated to v19. Parts of the user interface have changed. Consult the [release notes](https://github.com/systemd/mkosi/releases/tag/v19) for a list of changes. diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh index 0c1656efaf1c..d182c5c2a479 100755 --- a/nixos/maintainers/scripts/ec2/create-amis.sh +++ b/nixos/maintainers/scripts/ec2/create-amis.sh @@ -27,31 +27,37 @@ var ${bucket:=nixos-amis} var ${service_role_name:=vmimport} # Output of the command: -# > aws ec2 describe-regions --all-regions --query "Regions[].{Name:RegionName}" --output text | sort +# $ nix-shell -I nixpkgs=. -p awscli --run 'aws ec2 describe-regions --region us-east-1 --all-regions --query "Regions[].{Name:RegionName}" --output text | sort | sed -e s/^/\ \ /' var ${regions:= - af-south-1 - ap-east-1 - ap-northeast-1 - ap-northeast-2 - ap-northeast-3 - ap-south-1 - ap-southeast-1 - ap-southeast-2 - ap-southeast-3 - ca-central-1 - eu-central-1 - eu-north-1 - eu-south-1 - eu-west-1 - eu-west-2 - eu-west-3 - me-south-1 - sa-east-1 - us-east-1 - us-east-2 - us-west-1 - us-west-2 - } + af-south-1 + ap-east-1 + ap-northeast-1 + ap-northeast-2 + ap-northeast-3 + ap-south-1 + ap-south-2 + ap-southeast-1 + ap-southeast-2 + ap-southeast-3 + ap-southeast-4 + ca-central-1 + eu-central-1 + eu-central-2 + eu-north-1 + eu-south-1 + eu-south-2 + eu-west-1 + eu-west-2 + eu-west-3 + il-central-1 + me-central-1 + me-south-1 + sa-east-1 + us-east-1 + us-east-2 + us-west-1 + us-west-2 +} regions=($regions) diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index fee7c35ed8f4..4a93cd1fd9c8 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1334,6 +1334,7 @@ ./services/web-apps/vikunja.nix ./services/web-apps/whitebophir.nix ./services/web-apps/wiki-js.nix + ./services/web-apps/windmill.nix ./services/web-apps/wordpress.nix ./services/web-apps/writefreely.nix ./services/web-apps/youtrack.nix diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix index efef2d777acd..c9dda8a8c093 100644 --- a/nixos/modules/services/monitoring/ups.nix +++ b/nixos/modules/services/monitoring/ups.nix @@ -6,9 +6,83 @@ with lib; let cfg = config.power.ups; -in + defaultPort = 3493; + + nutFormat = { + + type = with lib.types; let + + singleAtom = nullOr (oneOf [ + bool + int + float + str + ]) // { + description = "atom (null, bool, int, float or string)"; + }; + + in attrsOf (oneOf [ + singleAtom + (listOf (nonEmptyListOf singleAtom)) + ]); + + generate = name: value: + let + normalizedValue = + lib.mapAttrs (key: val: + if lib.isList val + then forEach val (elem: if lib.isList elem then elem else [elem]) + else + if val == null + then [] + else [[val]] + ) value; + + mkValueString = concatMapStringsSep " " (v: + let str = generators.mkValueStringDefault {} v; + in + # Quote the value if it has spaces and isn't already quoted. + if (hasInfix " " str) && !(hasPrefix "\"" str && hasSuffix "\"" str) + then "\"${str}\"" + else str + ); + + in pkgs.writeText name (lib.generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " "; + listsAsDuplicateKeys = true; + } normalizedValue); + + }; + + installSecrets = source: target: secrets: + pkgs.writeShellScript "installSecrets.sh" '' + install -m0600 -D ${source} "${target}" + ${concatLines (forEach secrets (name: '' + ${pkgs.replace-secret}/bin/replace-secret \ + '@${name}@' \ + "$CREDENTIALS_DIRECTORY/${name}" \ + "${target}" + ''))} + chmod u-w "${target}" + ''; + + upsmonConf = nutFormat.generate "upsmon.conf" cfg.upsmon.settings; + + upsdUsers = pkgs.writeText "upsd.users" (let + # This looks like INI, but it's not quite because the + # 'upsmon' option lacks a '='. See: man upsd.users + userConfig = name: user: concatStringsSep "\n " (concatLists [ + [ + "[${name}]" + "password = \"@upsdusers_password_${name}@\"" + ] + (optional (user.upsmon != null) "upsmon ${user.upsmon}") + (forEach user.actions (action: "actions = ${action}")) + (forEach user.instcmds (instcmd: "instcmds = ${instcmd}")) + ]); + in concatStringsSep "\n\n" (mapAttrsToList userConfig cfg.users)); + -let upsOptions = {name, config, ...}: { options = { @@ -95,6 +169,213 @@ let }; }; + listenOptions = { + options = { + address = mkOption { + type = types.str; + description = lib.mdDoc '' + Address of the interface for `upsd` to listen on. + See `man upsd.conf` for details. + ''; + }; + + port = mkOption { + type = types.port; + default = defaultPort; + description = lib.mdDoc '' + TCP port for `upsd` to listen on. + See `man upsd.conf` for details. + ''; + }; + }; + }; + + upsdOptions = { + options = { + enable = mkOption { + type = types.bool; + defaultText = literalMD "`true` if `mode` is one of `standalone`, `netserver`"; + description = mdDoc "Whether to enable `upsd`."; + }; + + listen = mkOption { + type = with types; listOf (submodule listenOptions); + default = []; + example = [ + { + address = "192.168.50.1"; + } + { + address = "::1"; + port = 5923; + } + ]; + description = lib.mdDoc '' + Address of the interface for `upsd` to listen on. + See `man upsd` for details`. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = lib.mdDoc '' + Additional lines to add to `upsd.conf`. + ''; + }; + }; + + config = { + enable = mkDefault (elem cfg.mode [ "standalone" "netserver" ]); + }; + }; + + + monitorOptions = { name, config, ... }: { + options = { + system = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + Identifier of the UPS to monitor, in this form: `<upsname>[@<hostname>[:<port>]]` + See `upsmon.conf` for details. + ''; + }; + + powerValue = mkOption { + type = types.int; + default = 1; + description = lib.mdDoc '' + Number of power supplies that the UPS feeds on this system. + See `upsmon.conf` for details. + ''; + }; + + user = mkOption { + type = types.str; + description = lib.mdDoc '' + Username from `upsd.users` for accessing this UPS. + See `upsmon.conf` for details. + ''; + }; + + passwordFile = mkOption { + type = types.str; + defaultText = literalMD "power.ups.users.\${user}.passwordFile"; + description = lib.mdDoc '' + The full path to a file containing the password from + `upsd.users` for accessing this UPS. The password file + is read on service start. + See `upsmon.conf` for details. + ''; + }; + + type = mkOption { + type = types.str; + default = "master"; + description = lib.mdDoc '' + The relationship with `upsd`. + See `upsmon.conf` for details. + ''; + }; + }; + + config = { + passwordFile = mkDefault cfg.users.${config.user}.passwordFile; + }; + }; + + upsmonOptions = { + options = { + enable = mkOption { + type = types.bool; + defaultText = literalMD "`true` if `mode` is one of `standalone`, `netserver`, `netclient`"; + description = mdDoc "Whether to enable `upsmon`."; + }; + + monitor = mkOption { + type = with types; attrsOf (submodule monitorOptions); + default = {}; + description = lib.mdDoc '' + Set of UPS to monitor. See `man upsmon.conf` for details. + ''; + }; + + settings = mkOption { + type = nutFormat.type; + default = {}; + defaultText = literalMD '' + { + MINSUPPLIES = 1; + RUN_AS_USER = "root"; + NOTIFYCMD = "''${pkgs.nut}/bin/upssched"; + SHUTDOWNCMD = "''${pkgs.systemd}/bin/shutdown now"; + } + ''; + description = mdDoc "Additional settings to add to `upsmon.conf`."; + example = literalMD '' + { + MINSUPPLIES = 2; + NOTIFYFLAG = [ + [ "ONLINE" "SYSLOG+EXEC" ] + [ "ONBATT" "SYSLOG+EXEC" ] + ]; + } + ''; + }; + }; + + config = { + enable = mkDefault (elem cfg.mode [ "standalone" "netserver" "netclient" ]); + settings = { + RUN_AS_USER = "root"; # TODO: replace 'root' by another username. + MINSUPPLIES = mkDefault 1; + NOTIFYCMD = mkDefault "${pkgs.nut}/bin/upssched"; + SHUTDOWNCMD = mkDefault "${pkgs.systemd}/bin/shutdown now"; + MONITOR = flip mapAttrsToList cfg.upsmon.monitor (name: monitor: with monitor; [ system powerValue user "\"@upsmon_password_${name}@\"" type ]); + }; + }; + }; + + userOptions = { + options = { + passwordFile = mkOption { + type = types.str; + description = lib.mdDoc '' + The full path to a file that contains the user's (clear text) + password. The password file is read on service start. + ''; + }; + + actions = mkOption { + type = with types; listOf str; + default = []; + description = lib.mdDoc '' + Allow the user to do certain things with upsd. + See `man upsd.users` for details. + ''; + }; + + instcmds = mkOption { + type = with types; listOf str; + default = []; + description = lib.mdDoc '' + Let the user initiate specific instant commands. Use "ALL" to grant all commands automatically. For the full list of what your UPS supports, use "upscmd -l". + See `man upsd.users` for details. + ''; + }; + + upsmon = mkOption { + type = with types; nullOr str; + default = null; + description = lib.mdDoc '' + Add the necessary actions for a upsmon process to work. + See `man upsd.users` for details. + ''; + }; + }; + }; + in @@ -103,19 +384,14 @@ in # powerManagement.powerDownCommands power.ups = { - enable = mkOption { - default = false; - type = with types; bool; - description = lib.mdDoc '' - Enables support for Power Devices, such as Uninterruptible Power - Supplies, Power Distribution Units and Solar Controllers. - ''; - }; + enable = mkEnableOption (lib.mdDoc '' + Enables support for Power Devices, such as Uninterruptible Power + Supplies, Power Distribution Units and Solar Controllers. + ''); - # This option is not used yet. mode = mkOption { default = "standalone"; - type = types.str; + type = types.enum [ "none" "standalone" "netserver" "netclient" ]; description = lib.mdDoc '' The MODE determines which part of the NUT is to be started, and which configuration files must be modified. @@ -148,6 +424,13 @@ in ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open ports in the firewall for `upsd`. + ''; + }; maxStartDelay = mkOption { default = 45; @@ -161,6 +444,22 @@ in ''; }; + upsmon = mkOption { + default = {}; + description = lib.mdDoc '' + Options for the `upsmon.conf` configuration file. + ''; + type = types.submodule upsmonOptions; + }; + + upsd = mkOption { + default = {}; + description = lib.mdDoc '' + Options for the `upsd.conf` configuration file. + ''; + type = types.submodule upsdOptions; + }; + ups = mkOption { default = {}; # see nut/etc/ups.conf.sample @@ -172,46 +471,95 @@ in type = with types; attrsOf (submodule upsOptions); }; + users = mkOption { + default = {}; + description = lib.mdDoc '' + Users that can access upsd. See `man upsd.users`. + ''; + type = with types; attrsOf (submodule userOptions); + }; + }; }; config = mkIf cfg.enable { + assertions = [ + (let + totalPowerValue = foldl' add 0 (map (monitor: monitor.powerValue) (attrValues cfg.upsmon.monitor)); + minSupplies = cfg.upsmon.settings.MINSUPPLIES; + in mkIf cfg.upsmon.enable { + assertion = totalPowerValue >= minSupplies; + message = '' + `power.ups.upsmon`: Total configured power value (${toString totalPowerValue}) must be at least MINSUPPLIES (${toString minSupplies}). + ''; + }) + ]; + environment.systemPackages = [ pkgs.nut ]; - systemd.services.upsmon = { + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = + if cfg.upsd.listen == [] + then [ defaultPort ] + else unique (forEach cfg.upsd.listen (listen: listen.port)); + }; + + systemd.services.upsmon = let + secrets = mapAttrsToList (name: monitor: "upsmon_password_${name}") cfg.upsmon.monitor; + createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" secrets; + in { + enable = cfg.upsmon.enable; description = "Uninterruptible Power Supplies (Monitor)"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.Type = "forking"; - script = "${pkgs.nut}/sbin/upsmon"; - environment.NUT_CONFPATH = "/etc/nut/"; - environment.NUT_STATEPATH = "/var/lib/nut/"; + serviceConfig = { + Type = "forking"; + ExecStartPre = "${createUpsmonConf}"; + ExecStart = "${pkgs.nut}/sbin/upsmon"; + ExecReload = "${pkgs.nut}/sbin/upsmon -c reload"; + LoadCredential = mapAttrsToList (name: monitor: "upsmon_password_${name}:${monitor.passwordFile}") cfg.upsmon.monitor; + }; + environment.NUT_CONFPATH = "/etc/nut"; + environment.NUT_STATEPATH = "/var/lib/nut"; }; - systemd.services.upsd = { + systemd.services.upsd = let + secrets = mapAttrsToList (name: user: "upsdusers_password_${name}") cfg.users; + createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" secrets; + in { + enable = cfg.upsd.enable; description = "Uninterruptible Power Supplies (Daemon)"; after = [ "network.target" "upsmon.service" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.Type = "forking"; - # TODO: replace 'root' by another username. - script = "${pkgs.nut}/sbin/upsd -u root"; - environment.NUT_CONFPATH = "/etc/nut/"; - environment.NUT_STATEPATH = "/var/lib/nut/"; + serviceConfig = { + Type = "forking"; + ExecStartPre = "${createUpsdUsers}"; + # TODO: replace 'root' by another username. + ExecStart = "${pkgs.nut}/sbin/upsd -u root"; + ExecReload = "${pkgs.nut}/sbin/upsd -c reload"; + LoadCredential = mapAttrsToList (name: user: "upsdusers_password_${name}:${user.passwordFile}") cfg.users; + }; + environment.NUT_CONFPATH = "/etc/nut"; + environment.NUT_STATEPATH = "/var/lib/nut"; + restartTriggers = [ + config.environment.etc."nut/upsd.conf".source + ]; }; systemd.services.upsdrv = { + enable = cfg.upsd.enable; description = "Uninterruptible Power Supplies (Register all UPS)"; after = [ "upsd.service" ]; wantedBy = [ "multi-user.target" ]; - # TODO: replace 'root' by another username. - script = "${pkgs.nut}/bin/upsdrvctl -u root start"; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; + # TODO: replace 'root' by another username. + ExecStart = "${pkgs.nut}/bin/upsdrvctl -u root start"; }; - environment.NUT_CONFPATH = "/etc/nut/"; - environment.NUT_STATEPATH = "/var/lib/nut/"; + environment.NUT_CONFPATH = "/etc/nut"; + environment.NUT_STATEPATH = "/var/lib/nut"; }; environment.etc = { @@ -223,24 +571,23 @@ in '' maxstartdelay = ${toString cfg.maxStartDelay} - ${flip concatStringsSep (forEach (attrValues cfg.ups) (ups: ups.summary)) " - - "} + ${concatStringsSep "\n\n" (forEach (attrValues cfg.ups) (ups: ups.summary))} + ''; + "nut/upsd.conf".source = pkgs.writeText "upsd.conf" + '' + ${concatStringsSep "\n" (forEach cfg.upsd.listen (listen: "LISTEN ${listen.address} ${toString listen.port}"))} + ${cfg.upsd.extraConfig} ''; "nut/upssched.conf".source = cfg.schedulerRules; - # These file are containing private information and thus should not - # be stored inside the Nix store. - /* - "nut/upsd.conf".source = ""; - "nut/upsd.users".source = ""; - "nut/upsmon.conf".source = ""; - */ + "nut/upsd.users".source = "/run/nut/upsd.users"; + "nut/upsmon.conf".source = "/run/nut/upsmon.conf"; }; power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample"; systemd.tmpfiles.rules = [ "d /var/state/ups -" + "d /var/lib/nut 700" ]; diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix index 72a195d3a04e..d3164373ec01 100644 --- a/nixos/modules/services/security/clamav.nix +++ b/nixos/modules/services/security/clamav.nix @@ -3,7 +3,6 @@ with lib; let clamavUser = "clamav"; stateDir = "/var/lib/clamav"; - runDir = "/run/clamav"; clamavGroup = clamavUser; cfg = config.services.clamav; pkg = pkgs.clamav; @@ -99,6 +98,29 @@ in ''; }; }; + + scanner = { + enable = mkEnableOption (lib.mdDoc "ClamAV scanner"); + + interval = mkOption { + type = types.str; + default = "*-*-* 04:00:00"; + description = lib.mdDoc '' + How often clamdscan is invoked. See systemd.time(7) for more + information about the format. + By default this runs using 10 cores at most, be sure to run it at a time of low traffic. + ''; + }; + + scanDirectories = mkOption { + type = with types; listOf str; + default = [ "/home" "/var/lib" "/tmp" "/etc" "/var/tmp" ]; + description = lib.mdDoc '' + List of directories to scan. + The default includes everything I could think of that is valid for nixos. Feel free to contribute a PR to add to the default if you see something missing. + ''; + }; + }; }; }; @@ -117,9 +139,8 @@ in services.clamav.daemon.settings = { DatabaseDirectory = stateDir; - LocalSocket = "${runDir}/clamd.ctl"; - PidFile = "${runDir}/clamd.pid"; - TemporaryDirectory = "/tmp"; + LocalSocket = "/run/clamav/clamd.ctl"; + PidFile = "/run/clamav/clamd.pid"; User = "clamav"; Foreground = true; }; @@ -182,7 +203,6 @@ in ExecStart = "${pkg}/bin/freshclam"; SuccessExitStatus = "1"; # if databases are up to date StateDirectory = "clamav"; - RuntimeDirectory = "clamav"; User = clamavUser; Group = clamavGroup; PrivateTmp = "yes"; @@ -204,7 +224,6 @@ in serviceConfig = { Type = "oneshot"; StateDirectory = "clamav"; - RuntimeDirectory = "clamav"; User = clamavUser; Group = clamavGroup; PrivateTmp = "yes"; @@ -230,12 +249,31 @@ in Type = "oneshot"; ExecStart = "${pkgs.fangfrisch}/bin/fangfrisch --conf ${fangfrischConfigFile} refresh"; StateDirectory = "clamav"; - RuntimeDirectory = "clamav"; User = clamavUser; Group = clamavGroup; PrivateTmp = "yes"; PrivateDevices = "yes"; }; }; + + systemd.timers.clamdscan = mkIf cfg.scanner.enable { + description = "Timer for ClamAV virus scanner"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = cfg.scanner.interval; + Unit = "clamdscan.service"; + }; + }; + + systemd.services.clamdscan = mkIf cfg.scanner.enable { + description = "ClamAV virus scanner"; + after = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; + wants = optionals cfg.updater.enable [ "clamav-freshclam.service" ]; + + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkg}/bin/clamdscan --multiscan --fdpass --infected --allmatch ${lib.concatStringsSep " " cfg.scanner.scanDirectories}"; + }; + }; }; } diff --git a/nixos/modules/services/web-apps/windmill.nix b/nixos/modules/services/web-apps/windmill.nix new file mode 100644 index 000000000000..8e940dabdc1f --- /dev/null +++ b/nixos/modules/services/web-apps/windmill.nix @@ -0,0 +1,177 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.windmill; +in +{ + options.services.windmill = { + enable = lib.mkEnableOption (lib.mdDoc "windmill service"); + + serverPort = lib.mkOption { + type = lib.types.port; + default = 8001; + description = lib.mdDoc "Port the windmill server listens on."; + }; + + lspPort = lib.mkOption { + type = lib.types.port; + default = 3001; + description = lib.mdDoc "Port the windmill lsp listens on."; + }; + + database = { + name = lib.mkOption { + type = lib.types.str; + # the simplest database setup is to have the database named like the user. + default = "windmill"; + description = lib.mdDoc "Database name."; + }; + + user = lib.mkOption { + type = lib.types.str; + # the simplest database setup is to have the database user like the name. + default = "windmill"; + description = lib.mdDoc "Database user."; + }; + + urlPath = lib.mkOption { + type = lib.types.path; + description = lib.mdDoc '' + Path to the file containing the database url windmill should connect to. This is not deducted from database user and name as it might contain a secret + ''; + example = "config.age.secrets.DATABASE_URL_FILE.path"; + }; + createLocally = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc "Whether to create a local database automatically."; + }; + }; + + baseUrl = lib.mkOption { + type = lib.types.str; + description = lib.mdDoc '' + The base url that windmill will be served on. + ''; + example = "https://windmill.example.com"; + }; + + logLevel = lib.mkOption { + type = lib.types.enum [ "error" "warn" "info" "debug" "trace" ]; + default = "info"; + description = lib.mdDoc "Log level"; + }; + }; + + config = lib.mkIf cfg.enable { + + services.postgresql = lib.optionalAttrs (cfg.database.createLocally) { + enable = lib.mkDefault true; + + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensureDBOwnership = true; + } + ]; + + }; + + systemd.services = + let + serviceConfig = { + DynamicUser = true; + # using the same user to simplify db connection + User = cfg.database.user; + ExecStart = "${pkgs.windmill}/bin/windmill"; + + Restart = "always"; + LoadCredential = [ + "DATABASE_URL_FILE:${cfg.database.urlPath}" + ]; + }; + in + { + + # coming from https://github.com/windmill-labs/windmill/blob/main/init-db-as-superuser.sql + # modified to not grant priviledges on all tables + # create role windmill_user and windmill_admin only if they don't exist + postgresql.postStart = lib.mkIf cfg.database.createLocally (lib.mkAfter '' + $PSQL -tA <<"EOF" +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT FROM pg_catalog.pg_roles + WHERE rolname = 'windmill_user' + ) THEN + CREATE ROLE windmill_user; + GRANT ALL PRIVILEGES ON DATABASE ${cfg.database.name} TO windmill_user; + ELSE + RAISE NOTICE 'Role "windmill_user" already exists. Skipping.'; + END IF; + IF NOT EXISTS ( + SELECT FROM pg_catalog.pg_roles + WHERE rolname = 'windmill_admin' + ) THEN + CREATE ROLE windmill_admin WITH BYPASSRLS; + GRANT windmill_user TO windmill_admin; + ELSE + RAISE NOTICE 'Role "windmill_admin" already exists. Skipping.'; + END IF; + GRANT windmill_admin TO windmill; +END +$$; +EOF + ''); + + windmill-server = { + description = "Windmill server"; + after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = serviceConfig // { StateDirectory = "windmill";}; + + environment = { + DATABASE_URL_FILE = "%d/DATABASE_URL_FILE"; + PORT = builtins.toString cfg.serverPort; + WM_BASE_URL = cfg.baseUrl; + RUST_LOG = cfg.logLevel; + MODE = "server"; + }; + }; + + windmill-worker = { + description = "Windmill worker"; + after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = serviceConfig // { StateDirectory = "windmill-worker";}; + + environment = { + DATABASE_URL_FILE = "%d/DATABASE_URL_FILE"; + WM_BASE_URL = cfg.baseUrl; + RUST_LOG = cfg.logLevel; + MODE = "worker"; + WORKER_GROUP = "default"; + KEEP_JOB_DIR = "false"; + }; + }; + + windmill-worker-native = { + description = "Windmill worker native"; + after = [ "network.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = serviceConfig // { StateDirectory = "windmill-worker-native";}; + + environment = { + DATABASE_URL_FILE = "%d/DATABASE_URL_FILE"; + WM_BASE_URL = cfg.baseUrl; + RUST_LOG = cfg.logLevel; + MODE = "worker"; + WORKER_GROUP = "native"; + }; + }; + }; + }; +} diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 33261021480f..3e10770812db 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -1612,7 +1612,7 @@ let description = lib.mdDoc '' Each attribute in this set specifies an option in the `[WireGuardPeer]` section of the unit. See - {manpage}`systemd.network(5)` for details. + {manpage}`systemd.netdev(5)` for details. ''; }; }; |