diff --git a/examples/default.nix b/examples/default.nix
index 0a065b9..c359dc5 100644
--- a/examples/default.nix
+++ b/examples/default.nix
@@ -12,11 +12,36 @@
nixng,
}:
let
+ modifiedMakeSystem =
+ {
+ config ? { },
+ specialArgs ? { },
+ ...
+ }@args:
+ nglib.makeSystem (
+ args
+ // {
+ config = {
+ nixos.acceptRisks = "I accept the risks";
+
+ imports = [ config ];
+ };
+ specialArgs = specialArgs // {
+ __enableExperimentalNixOSCompatibility = true;
+ };
+ }
+ );
+
+ modifiedNglib = nglib // {
+ makeSystem = modifiedMakeSystem;
+ };
+
examples = {
"gitea" = ./gitea;
"gitea-sane" = ./gitea/sane.nix;
"apache" = ./apache;
"nginx" = ./nginx;
+ "nginx-nixos" = ./nginx-nixos;
"crond" = ./crond;
"nix" = ./nix;
"hydra" = ./hydra;
@@ -33,4 +58,10 @@ let
"ntfy-sh" = ./ntfy-sh;
};
in
-nixpkgs.lib.mapAttrs (_: v: import v { inherit nixpkgs nglib nixng; }) examples
+nixpkgs.lib.mapAttrs (
+ _: v:
+ import v {
+ inherit nixpkgs nixng;
+ nglib = modifiedNglib;
+ }
+) examples
diff --git a/examples/nginx-nixos/default.nix b/examples/nginx-nixos/default.nix
new file mode 100644
index 0000000..84692e0
--- /dev/null
+++ b/examples/nginx-nixos/default.nix
@@ -0,0 +1,41 @@
+# SPDX-FileCopyrightText: 2021 Richard Brežák and NixNG contributors
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+{ nglib, nixpkgs, ... }:
+nglib.makeSystem {
+ inherit nixpkgs;
+ system = "x86_64-linux";
+ name = "nixng-nginx-nixos";
+ config = (
+ { pkgs, config, ... }:
+ {
+ config = {
+ dinit = {
+ enable = true;
+ };
+
+ init.services.nginx = {
+ shutdownOnExit = true;
+ ensureSomething.link."documentRoot" = {
+ src = "${pkgs.apacheHttpd}/htdocs";
+ dst = "/var/www";
+ };
+ };
+
+ nixos.services.nginx = {
+ enable = true;
+ virtualHosts."example.org" = {
+ locations."/" = {
+ proxyPass = "google.com";
+ };
+ };
+ };
+ };
+ }
+ );
+}
diff --git a/lib/dag.nix b/lib/dag.nix
index 2c5d7a1..298c33c 100644
--- a/lib/dag.nix
+++ b/lib/dag.nix
@@ -6,7 +6,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-{ lib }:
+{ lib, ... }:
import (builtins.fetchurl {
url = "https://raw.githubusercontent.com/nix-community/home-manager/45abf3d38a2b51c00c347cab6950f3734e023bba/modules/lib/dag.nix";
sha256 = "sha256-NN9iKanf86D1MH9Nx8nsQj9T2+Poy9XeW9pLcZIyFHU=";
diff --git a/lib/default.nix b/lib/default.nix
index f3a6a25..dd54b19 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -1,13 +1,15 @@
lib:
-let
- inherit (lib) types;
- this = {
- makeSystem = import ./make-system.nix {
- nglib = this;
- overlay = import ../overlay;
+lib.fix (
+ nglib:
+ let
+ overlay = import ../overlay;
+ args = {
+ inherit lib nglib overlay;
};
- dag = import ./dag.nix { inherit lib; };
- generators = import ./generators.nix { inherit lib; };
+ in
+ {
+ dag = import ./dag.nix args;
+ generators = import ./generators.nix args;
mkDefaultRec = lib.mapAttrsRecursive (_: v: lib.mkDefault v);
mkApply = fun: x: {
original = x;
@@ -18,31 +20,31 @@ let
description:
lib.mkOption {
inherit description;
- type = types.attrsOf (
- types.submodule {
+ type = lib.types.attrsOf (
+ lib.types.submodule {
options = {
data = lib.mkOption {
description = ''
Script fragment which to run.
'';
- type = types.str;
+ type = lib.types.str;
};
before = lib.mkOption {
description = ''
Script before dependencies. See /lib/dag.nix.
'';
- type = with types; listOf str;
+ type = lib.types.listOf lib.types.str;
};
after = lib.mkOption {
description = ''
Script after dependencies. See /lib/dag.nix
'';
- type = with types; listOf str;
+ type = lib.types.listOf lib.types.str;
};
};
}
);
- apply = this.dag.dagTopoSort;
+ apply = nglib.dag.dagTopoSort;
default = { };
};
@@ -63,11 +65,7 @@ let
'') fragments
)}
'';
-
- nottmpfiles = import ./nottmpfiles {
- inherit lib;
- nglib = this;
- };
+ nottmpfiles = import ./nottmpfiles args;
maybeChangeUserAndGroup =
user: group: script:
@@ -75,6 +73,26 @@ let
"chpst -u ${user}${lib.optionalString (group != null) ":${group}"} ${script}"
else
script;
- };
-in
-this
+
+ errorExperimentalNixOS =
+ config:
+ lib.throwIfNot (config.nixos.acceptRisks == "I accept the risks") ''
+ NixOS module compatibility is highly experimental, severely unfinished and most definitely has
+ functional and security bugs. Unless you know what you're doing and are willing to accept the risks
+ reconsider it's usage. To signify you are aware of these risks, set the option
+ `config.nixos.acceptRisks` to `"I accept the risks"`.
+
+ If you run into any of the aforementioned deficiencies please reach out on Matrix at
+ `#nixng:matrix.redalder.org`.
+ '';
+
+ inherit (import ./options.nix args)
+ mkUserOption
+ mkGroupOption
+ mkOptionsEqual
+ getOptionFromPath
+ ;
+
+ makeSystem = import ./make-system.nix { inherit lib nglib overlay; };
+ }
+)
diff --git a/lib/generators.nix b/lib/generators.nix
index 551241e..451cd96 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -6,7 +6,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-{ lib }:
+{ lib, ... }:
let
inherit (lib)
isAttrs
diff --git a/lib/make-system.nix b/lib/make-system.nix
index 1f8ae23..1918807 100644
--- a/lib/make-system.nix
+++ b/lib/make-system.nix
@@ -7,7 +7,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# These arguments are provided by the overarching NixNG repository and are not user confugurable.
-{ nglib, overlay }:
+{ nglib, overlay, ... }:
# These arguments are user configurable
{
nixpkgs,
@@ -24,6 +24,8 @@ let
evaledModules = evalModules {
specialArgs = specialArgs // {
inherit nglib;
+ __enableExperimentalNixOSCompatibility =
+ specialArgs.__enableExperimentalNixOSCompatibility or false;
};
modules =
diff --git a/lib/options.nix b/lib/options.nix
new file mode 100644
index 0000000..40321fe
--- /dev/null
+++ b/lib/options.nix
@@ -0,0 +1,56 @@
+{ lib, nglib, ... }:
+{
+ mkUserOption =
+ user: description:
+ lib.mkOption {
+ inherit description;
+ type = lib.types.str;
+ default = user;
+ };
+
+ mkGroupOption =
+ group: description:
+ lib.mkOption {
+ inherit description;
+ type = lib.types.str;
+ default = group;
+ };
+
+ getOptionFromPath =
+ path: options:
+ let
+ getOptionFromPath' =
+ pathLeft: pathRight: subtree:
+ if pathRight == [ ] then
+ subtree
+ else
+ let
+ newSubtree = (
+ subtree.${lib.head pathRight} or (lib.evalModules {
+ modules = subtree.type.getSubModules ++ subtree.definitions;
+ inherit (subtree.type.functor.payload) class specialArgs;
+ }).options.${lib.head pathRight}
+ or (abort ("cannot find option '" + lib.concatStringsSep "." path + "'"))
+ );
+ in
+ getOptionFromPath' (pathLeft ++ [ (lib.head pathRight) ]) (lib.tail pathRight) newSubtree;
+ in
+ getOptionFromPath' [ ] path options;
+
+ mkOptionsEqual =
+ to: from: mapper:
+ { config, options, ... }:
+ let
+ fromOpt = nglib.getOptionFromPath from options;
+ toOpt = nglib.getOptionFromPath to options;
+
+ prio = fromOpt.highestPrio or lib.defaultOverridePriority;
+ defsWithPrio = map (def: lib.mkOverride prio (mapper def)) fromOpt.definitions;
+ in
+ {
+ config = lib.attrsets.setAttrByPath to (lib.mkMerge defsWithPrio);
+ options = lib.attrsets.setAttrByPath from (
+ lib.mkOption { apply = x: lib.attrsets.getAttrFromPath to config; }
+ );
+ };
+}
diff --git a/modules/list.nix b/modules/list.nix
index 9c60b6a..7d7df3a 100644
--- a/modules/list.nix
+++ b/modules/list.nix
@@ -50,4 +50,6 @@
./services/dnsmasq.nix
./services/attic.nix
./services/ntfy-sh.nix
+
+ ./nixos
]
diff --git a/modules/nixos/assertions.nix b/modules/nixos/assertions.nix
new file mode 100644
index 0000000..a41f3f1
--- /dev/null
+++ b/modules/nixos/assertions.nix
@@ -0,0 +1,16 @@
+{
+ config,
+ lib,
+ nglib,
+ ...
+}:
+{
+ options.nixos = lib.mkOption { type = lib.types.submodule { imports = [ ../assertions.nix ]; }; };
+
+ imports = [
+ (nglib.mkOptionsEqual [ "assertions" ] [
+ "nixos"
+ "assertions"
+ ] lib.id)
+ ];
+}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
new file mode 100644
index 0000000..0429fb6
--- /dev/null
+++ b/modules/nixos/default.nix
@@ -0,0 +1,55 @@
+{
+ lib,
+ pkgs,
+ config,
+ __enableExperimentalNixOSCompatibility,
+ ...
+}:
+{
+ imports = lib.optionals __enableExperimentalNixOSCompatibility ([
+ ./systemd.nix
+ ./nginx.nix
+ ./users.nix
+ ./postgresql.nix
+ ./assertions.nix
+ ./oauth2-proxy.nix
+ ./nix.nix
+ ./meta.nix
+ ./networking.nix
+ ]);
+
+ options.nixos = lib.mkOption {
+ type = lib.types.submodule {
+ options = {
+ acceptRisks = lib.mkOption {
+ visible = false;
+ description = ''
+ This is an invisible option, intention is for the user to hit the scary warning first
+ and only then learn of this option and declare acceptance of the risks.
+ '';
+ type = lib.types.str;
+ default = "I don't know of the risks";
+ };
+ };
+ config._module.args = {
+ inherit pkgs;
+ };
+ };
+ default = { };
+ };
+
+ config.assertions = lib.optionals __enableExperimentalNixOSCompatibility [
+ {
+ assertion = (config.nixos.acceptRisks == "I accept the risks");
+ message = ''
+ NixOS module compatibility is highly experimental, severely unfinished and most definitely has
+ functional and security bugs. Unless you know what you're doing and are willing to accept the risks
+ reconsider it's usage. To signify you are aware of these risks, set the option
+ `config.nixos.acceptRisks` to `"I accept the risks"`.
+
+ If you run into any of the aforementioned deficiencies please reach out on Matrix at
+ `#nixng:matrix.redalder.org`.
+ '';
+ }
+ ];
+}
diff --git a/modules/nixos/meta.nix b/modules/nixos/meta.nix
new file mode 100644
index 0000000..603a78d
--- /dev/null
+++ b/modules/nixos/meta.nix
@@ -0,0 +1,6 @@
+{ lib, ... }:
+{
+ options = {
+ nixos.meta = lib.mkOption { type = lib.types.unspecified; };
+ };
+}
diff --git a/modules/nixos/networking.nix b/modules/nixos/networking.nix
new file mode 100644
index 0000000..0fd6602
--- /dev/null
+++ b/modules/nixos/networking.nix
@@ -0,0 +1,13 @@
+{ lib, config, ... }:
+{
+ options = {
+ nixos.networking.hostName = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Machine hostname, this has currently no effect on NixNG and is completely
+ local to the NixOS compatibility layer.
+ '';
+ default = "unnamed";
+ };
+ };
+}
diff --git a/modules/nixos/nginx.nix b/modules/nixos/nginx.nix
new file mode 100644
index 0000000..0c293c8
--- /dev/null
+++ b/modules/nixos/nginx.nix
@@ -0,0 +1,235 @@
+{
+ lib,
+ config,
+ pkgs,
+ options,
+ nglib,
+ ...
+}:
+let
+ cfg = config.nixos.services.nginx;
+
+ recommendedProxyConfig = {
+ proxy_set_header = [
+ [
+ "Host"
+ "$$host"
+ ]
+ [
+ "X-Real-IP"
+ "$$remote_addr"
+ ]
+ [
+ "X-Forwarded-For"
+ "$$proxy_add_x_forwarded_for"
+ ]
+ [
+ "X-Forwarded-Proto"
+ "$$scheme"
+ ]
+ [
+ "X-Forwarded-Host"
+ "$$host"
+ ]
+ [
+ "X-Forwarded-Server"
+ "$$host"
+ ]
+ ];
+ };
+in
+{
+ options = {
+ nixos.services.nginx = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Enable NixOS compatible nginx module. This module should have the same
+ semantics as the upstream module, as such see
+ [the upstream options](https://search.nixos.org/options?query=services.nginx).
+
+ WARNING: this module only implements a rather small subset of the upstream
+ NixOS module, but the parts that are implemented should have mostly the same
+ semantics.
+ '';
+ };
+
+ recommendedProxySettings = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Applies recommended proxy settings, should be the same options as the
+ upstream NixOS module.
+ '';
+ };
+
+ proxyTimeout = lib.mkOption {
+ type = lib.types.str;
+ default = "60s";
+ example = "20s";
+ description = ''
+ Change the proxy related timeouts in recommendedProxySettings.
+ '';
+ };
+
+ virtualHosts = lib.mkOption {
+ description = ''
+ Declarative virtual host configuration.
+ '';
+ type = lib.types.attrsOf (
+ lib.types.submodule {
+ options = {
+ locations = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule {
+ options.proxyPass = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ };
+
+ options.proxyWebsockets = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+
+ options.extraConfig = lib.mkOption {
+ type = lib.types.lines;
+ default = "";
+ };
+ }
+ );
+ default = { };
+ };
+
+ forceSSL = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+
+ addSSL = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+
+ useHTTPS = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ };
+ };
+ }
+ );
+ default = { };
+ };
+ };
+ };
+
+ imports = [
+ (nglib.mkOptionsEqual
+ [
+ "services"
+ "nginx"
+ "enable"
+ ]
+ [
+ "nixos"
+ "services"
+ "nginx"
+ "enable"
+ ]
+ lib.id
+ )
+ ];
+
+ config = {
+ services.nginx = lib.mkIf config.services.nginx.enable ({
+ envsubst = true;
+ configuration = lib.singleton {
+ daemon = "off";
+ worker_processes = 8;
+ user = "nginx";
+
+ events."" = {
+ use = "epoll";
+ worker_connections = 512;
+ };
+
+ error_log = [
+ "/dev/stderr"
+ "warn"
+ ];
+
+ pid = "/nginx.pid";
+
+ http."" =
+ [
+ {
+ server_tokens = "off";
+ include = [ [ "${pkgs.nginx}/conf/mime.types" ] ];
+ charset = "utf-8";
+ access_log = [
+ "/dev/stdout"
+ "combined"
+ ];
+
+ # $connection_upgrade is used for websocket proxying
+ map."$$http_upgrade $$connection_upgrade" = {
+ default = "upgrade";
+ "''" = "close";
+ };
+ }
+ ]
+ ++ (lib.optionals cfg.recommendedProxySettings [
+ {
+ proxy_redirect = "off";
+ proxy_connect_timeout = cfg.proxyTimeout;
+ proxy_send_timeout = cfg.proxyTimeout;
+ proxy_read_timeout = cfg.proxyTimeout;
+ proxy_http_version = "1.1";
+ # don't let clients close the keep-alive connection to upstream. See the nginx blog for details:
+ # https://www.nginx.com/blog/avoiding-top-10-nginx-configuration-mistakes/#no-keepalives
+ proxy_set_header = [
+ "Connection"
+ "''"
+ ];
+ }
+ recommendedProxyConfig
+ ])
+ ++ (lib.flip lib.mapAttrsToList cfg.virtualHosts (
+ server_name: server: {
+ server."" = {
+ listen = [
+ "80"
+ "http2"
+ ];
+ inherit server_name;
+
+ location = lib.flip lib.mapAttrs server.locations (
+ location: settings: [
+ (lib.optionalAttrs (
+ settings.proxyPass != null && cfg.recommendedProxySettings
+ ) recommendedProxyConfig)
+ (lib.optionalAttrs settings.proxyWebsockets {
+ proxy_http_version = "1.1";
+ proxy_set_header = [
+ [
+ "Upgrade"
+ "$$http_upgrade"
+ ]
+ [
+ "Connection"
+ "$$connection_upgrade"
+ ]
+ ];
+ })
+ settings.extraConfig
+ (lib.optionalAttrs (settings.proxyPass != null) { proxy_pass = settings.proxyPass; })
+ ]
+ );
+ };
+ }
+ ));
+ };
+ });
+ };
+}
diff --git a/modules/nixos/nix.nix b/modules/nixos/nix.nix
new file mode 100644
index 0000000..a92809e
--- /dev/null
+++ b/modules/nixos/nix.nix
@@ -0,0 +1,22 @@
+{
+ nglib,
+ lib,
+ config,
+ ...
+}:
+{
+ imports = [
+ (nglib.mkOptionsEqual
+ [
+ "nix"
+ "config"
+ ]
+ [
+ "nixos"
+ "nix"
+ "config"
+ ]
+ lib.id
+ )
+ ];
+}
diff --git a/modules/nixos/oauth2-proxy.nix b/modules/nixos/oauth2-proxy.nix
new file mode 100644
index 0000000..d08e2d6
--- /dev/null
+++ b/modules/nixos/oauth2-proxy.nix
@@ -0,0 +1,12 @@
+{ lib, ... }:
+{
+ options = {
+ nixos.services.oauth2-proxy = lib.mkOption {
+ type = lib.types.unspecified;
+ default = { };
+ description = ''
+ WARNING: this is only a stub module that does nothing.
+ '';
+ };
+ };
+}
diff --git a/modules/nixos/postgresql.nix b/modules/nixos/postgresql.nix
new file mode 100644
index 0000000..23f551f
--- /dev/null
+++ b/modules/nixos/postgresql.nix
@@ -0,0 +1,38 @@
+{
+ nglib,
+ lib,
+ config,
+ ...
+}:
+{
+ options = {
+ nixos.services.postgresql = lib.mkOption {
+ type = lib.types.unspecified;
+ default = { };
+ description = ''
+ Enable NixOS compatible PostgreSQL module. This module should have the same
+ semantics as the upstream module, as such see
+ [the upstream options](https://search.nixos.org/options?query=services.postgresql).
+
+ WARNING: this module implements a rather complete subset of the upstream
+ NixOS module, but even then, it's highly experimental and may have semantic
+ differences.
+ '';
+ };
+ };
+
+ imports = [
+ (nglib.mkOptionsEqual
+ [
+ "services"
+ "postgresql"
+ ]
+ [
+ "nixos"
+ "services"
+ "postgresql"
+ ]
+ lib.id
+ )
+ ];
+}
diff --git a/modules/nixos/systemd.nix b/modules/nixos/systemd.nix
new file mode 100644
index 0000000..d7791c4
--- /dev/null
+++ b/modules/nixos/systemd.nix
@@ -0,0 +1,206 @@
+{
+ lib,
+ nglib,
+ config,
+ pkgs,
+ ...
+}:
+{
+ options = {
+ nixos.systemd.services = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule (
+ { name, ... }:
+ {
+ options = {
+ description = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ };
+
+ after = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ wants = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ wantedBy = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ requires = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ requiredBy = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ environment = lib.mkOption {
+ type = lib.types.attrsOf lib.types.str;
+ default = { };
+ };
+
+ path = lib.mkOption {
+ type = lib.types.listOf lib.types.package;
+ default = { };
+ };
+
+ preStart = lib.mkOption {
+ type = lib.types.lines;
+ default = "";
+ };
+
+ reloadIfChanged = lib.mkOption { type = lib.types.bool; };
+
+ serviceConfig = lib.mkOption {
+ type = lib.types.submodule {
+ options = {
+ Type = lib.mkOption { type = lib.types.str; };
+
+ ExecStart = lib.mkOption {
+ type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
+ apply = x: lib.toList x;
+ };
+
+ ExecReload = lib.mkOption {
+ type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
+ apply = x: lib.toList x;
+ };
+
+ User = lib.mkOption { type = lib.types.str; };
+
+ Group = lib.mkOption { type = lib.types.str; };
+
+ WorkingDirectory = lib.mkOption { type = lib.types.path; };
+
+ LoadCredential = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ RuntimeDirectory = lib.mkOption { type = lib.types.str; };
+
+ Environment = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+
+ OOMPolicy = lib.mkOption {
+ type = lib.types.str;
+ default = "";
+ };
+ };
+ };
+ };
+ };
+ }
+ )
+ );
+ default = { };
+ description = ''
+ NixOS compatible services module. This module does not have the same
+ semantics as the upstream module, as such take upstream behavior with
+ a lot of salt.
+
+ WARNING: this module only implements a tiny subset of the upstream
+ NixOS module and the parts that are implemented do NOT have the same
+ semantics. This module is enough to get very basic NixOS services to work
+ but for anything more significat it'll for sure break.
+ '';
+ };
+
+ nixos.systemd.tmpfiles = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ };
+ };
+
+ config = {
+ init.services = (
+ lib.flip lib.mapAttrs config.nixos.systemd.services (
+ n: v:
+ let
+ combinedDeps = lib.unique (v.after ++ v.wants ++ v.requires);
+ withCredentials = v.serviceConfig.LoadCredential != [ ];
+ credentialsDirectory = "/run/credentials/${n}";
+ in
+ {
+ dependencies = lib.flip lib.map combinedDeps (
+ e:
+ lib.pipe e [
+ (lib.strings.removeSuffix ".service")
+ (lib.strings.removeSuffix ".target")
+ ]
+ );
+ enabled = lib.elem "multi-user.target" (v.wantedBy ++ v.requiredBy);
+ environment = lib.mkMerge [
+ v.environment
+ {
+ PATH = lib.makeBinPath v.path;
+ CREDENTIALS_DIRECTORY = lib.mkIf withCredentials credentialsDirectory;
+ }
+ (lib.listToAttrs (
+ lib.map (
+ env:
+ let
+ parts = lib.splitString "=" env;
+ envKey = lib.elemAt parts 0;
+ envValue = lib.replaceStrings [ "%d" ] [ credentialsDirectory ] (lib.elemAt parts 1);
+ in
+ assert (lib.assertMsg (lib.length parts == 2) "\"${env}\" is not of the format \"KEY=VALUE\"");
+ lib.nameValuePair envKey envValue
+ ) v.serviceConfig.Environment
+ ))
+ ];
+ execStartPre = pkgs.writeShellScript "${n}-start-pre" ''
+ ${nglib.mergeShellFragmentsIsolated (
+ lib.map (x: {
+ name = x;
+ data = ''
+ umask 077
+ set -euo pipefail
+ _source="$(cut -f 2 -d ':'<<<"${x}")"
+ _dest="${credentialsDirectory}/$(cut -f 1 -d ':'<<<"${x}")"
+
+ if ! [ -e "$_source" ] ; then
+ printf "Credential $_source for service ${n} not found.\n"
+ exit 1
+ fi
+
+ cp "$_source" "$_dest"
+ chown ${v.serviceConfig.User}:${v.serviceConfig.Group} "$_dest"
+ '';
+ }) v.serviceConfig.LoadCredential
+ )}
+
+ if ! [ "$_status" = "0" ] ; then
+ exit "$_status"
+ fi
+
+ ${v.preStart}
+ '';
+ execStart = pkgs.writeShellScript "${n}-start" (
+ lib.concatStringsSep "\n" v.serviceConfig.ExecStart
+ );
+ group = v.serviceConfig.Group;
+ user = v.serviceConfig.User;
+ workingDirectory = v.serviceConfig.WorkingDirectory;
+ tmpfiles =
+ with nglib.nottmpfiles.dsl;
+ lib.optionals withCredentials [
+ (d credentialsDirectory "0700" config.init.services.${n}.user config.init.services.${n}.group _ _)
+ (R credentialsDirectory "0700" config.init.services.${n}.user config.init.services.${n}.group _ _)
+ ];
+ }
+ )
+ );
+ };
+}
diff --git a/modules/nixos/users.nix b/modules/nixos/users.nix
new file mode 100644
index 0000000..958a333
--- /dev/null
+++ b/modules/nixos/users.nix
@@ -0,0 +1,138 @@
+{
+ nglib,
+ lib,
+ config,
+ ...
+}:
+{
+ options = {
+ nixos.users.users = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule (
+ { name, ... }:
+ {
+ options = {
+ description = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The users description;
+ '';
+ };
+ createHome = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to create the user's home folder.
+ '';
+ };
+ home = lib.mkOption {
+ type = lib.types.path;
+ description = ''
+ Where should the users home folder be located.
+ '';
+ };
+ group = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The user's primary group.
+ '';
+ };
+ extraGroups = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = ''
+ The user's supplementary groups.
+ '';
+ };
+ useDefaultShell = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to use the default shell.
+ '';
+ };
+ isSystemUser = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ This doesn't do anything on NixNG but is here for eval-time compatibility.
+ Slight difference of behavior is expected.
+ '';
+ };
+ isNormalUser = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Indicates whether is an account for a 'real' user.
+ '';
+ };
+ name = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ readOnly = true;
+ };
+ };
+ }
+ )
+ );
+ default = { };
+ description = ''
+ NixOS compatible users module. This module should have the same
+ semantics as the upstream module, as such see
+ [the upstream options](https://search.nixos.org/options?query=users.users).
+
+ WARNING: this module only implements a rather small subset of the upstream
+ NixOS module, but the parts that are implemented should have mostly the same
+ semantics.
+ '';
+ };
+
+ nixos.users.groups = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.submodule { });
+ default = { };
+ description = ''
+ NixOS compatible users module. This module should have the same
+ semantics as the upstream module, as such see
+ [the upstream options](https://search.nixos.org/options?query=users.users).
+
+ Also see the documentation for NixNG's `users.users`, this compatibility module
+ maps almost exactly onto it.
+
+ WARNING: this module only implements a rather small subset of the upstream
+ NixOS module, but the parts that are implemented should have mostly the same
+ semantics.
+ '';
+ };
+ };
+
+ imports = [
+ (nglib.mkOptionsEqual
+ [
+ "users"
+ "users"
+ ]
+ [
+ "nixos"
+ "users"
+ "users"
+ ]
+ (
+ def:
+ lib.removeAttrs (lib.traceValSeq def) [
+ "name"
+ "isSystemUser"
+ ]
+ )
+ )
+ (nglib.mkOptionsEqual
+ [
+ "users"
+ "groups"
+ ]
+ [
+ "nixos"
+ "users"
+ "groups"
+ ]
+ lib.id
+ )
+ ];
+}
diff --git a/modules/services/postgresql.nix b/modules/services/postgresql.nix
index e090401..48345dd 100644
--- a/modules/services/postgresql.nix
+++ b/modules/services/postgresql.nix
@@ -105,7 +105,10 @@ in
initdbArgs = lib.mkOption {
type = with lib.types; listOf str;
- default = [ ];
+ default = [
+ "-E"
+ "UTF8"
+ ];
example = [
"--data-checksums"
"--allow-group-access"