diff options
Diffstat (limited to 'nixos')
26 files changed, 515 insertions, 94 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index cf5dd6c0916..af2aecda0da 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -1045,6 +1045,14 @@ </listitem> <listitem> <para> + The <literal>taskserver</literal> module no longer implicitly + opens ports in the firewall configuration. This is now + controlled through the option + <literal>services.taskserver.openFirewall</literal>. + </para> + </listitem> + <listitem> + <para> The <literal>autorestic</literal> package has been upgraded from 1.3.0 to 1.5.0 which introduces breaking changes in config file, check @@ -1265,6 +1273,15 @@ </listitem> <listitem> <para> + <literal>services.github-runner</literal> has been hardened. + Notably address families and system calls have been + restricted, which may adversely affect some kinds of testing, + e.g. using <literal>AF_BLUETOOTH</literal> to test bluetooth + devices. + </para> + </listitem> + <listitem> + <para> The terraform 0.12 compatibility has been removed and the <literal>terraform.withPlugins</literal> and <literal>terraform-providers.mkProvider</literal> @@ -1921,12 +1938,53 @@ </listitem> <listitem> <para> - ORY Kratos was updated to version 0.8.3-alpha.1.pre.0, which + ORY Kratos was updated to version 0.9.0-alpha.3, which introduces some breaking changes: </para> <itemizedlist spacing="compact"> <listitem> <para> + All endpoints at the Admin API are now exposed at + <literal>/admin/</literal>. For example, endpoint + <literal>https://kratos:4434/identities</literal> is now + exposed at + <literal>https://kratos:4434/admin/identities</literal> + </para> + </listitem> + <listitem> + <para> + Configuration key + <literal>selfservice.whitelisted_return_urls</literal> has + been renamed to <literal>allowed_return_urls</literal> + </para> + </listitem> + <listitem> + <para> + The <literal>password_identifier</literal> form field of + the password login strategy has been renamed to + <literal>identifier</literal> to make compatibility with + passwordless flows possible. + </para> + </listitem> + <listitem> + <para> + Instead of having a global + <literal>default_schema_url</literal> which developers + used to update their schema, you now need to define the + <literal>default_schema_id</literal> which must reference + schema ID in your config. + </para> + </listitem> + <listitem> + <para> + Calling <literal>/self-service/recovery</literal> without + flow ID or with an invalid flow ID while authenticated + will now respond with an error instead of redirecting to + the default page. + </para> + </listitem> + <listitem> + <para> If you are relying on the SQLite images, update your Docker Pull commands as follows: </para> @@ -1961,6 +2019,18 @@ Notes for v0.8.2-alpha-1</link> </para> </listitem> + <listitem> + <para> + <link xlink:href="https://github.com/ory/kratos/releases/tag/v0.9.0-alpha.1">Release + Notes for v0.9.0-alpha-1</link> + </para> + </listitem> + <listitem> + <para> + <link xlink:href="https://github.com/ory/kratos/releases/tag/v0.9.0-alpha.3">Release + Notes for v0.9.0-alpha-3</link> + </para> + </listitem> </itemizedlist> </listitem> </itemizedlist> @@ -2090,6 +2160,12 @@ </listitem> <listitem> <para> + The <literal>vscode-extensions.ionide.ionide-fsharp</literal> + package has been updated to 6.0.0 and now requires .NET 6.0. + </para> + </listitem> + <listitem> + <para> The <literal>zrepl</literal> package has been updated from 0.4.0 to 0.5: </para> @@ -2234,6 +2310,14 @@ </listitem> <listitem> <para> + The Nextcloud module now supports to create a Mysql database + automatically with + <literal>services.nextcloud.database.createLocally</literal> + enabled. + </para> + </listitem> + <listitem> + <para> The <literal>spark3</literal> package has been updated from 3.1.2 to 3.2.1 (<link xlink:href="https://github.com/NixOS/nixpkgs/pull/160075">#160075</link>): @@ -2263,6 +2347,15 @@ generating host-global NNCP configuration. </para> </listitem> + <listitem> + <para> + The option <literal>services.snapserver.openFirewall</literal> + will no longer default to <literal>true</literal> starting + with NixOS 22.11. Enable it explicitly if you need to control + Snapserver remotely or connect streamig clients from other + hosts. + </para> + </listitem> </itemizedlist> </section> </section> diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index 675a53996ef..6d22973fb9b 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -443,6 +443,10 @@ In addition to numerous new and upgraded packages, this release has the followin - `services.miniflux.adminCredentialFiles` is now required, instead of defaulting to `admin` and `password`. +- The `taskserver` module no longer implicitly opens ports in the firewall + configuration. This is now controlled through the option + `services.taskserver.openFirewall`. + - The `autorestic` package has been upgraded from 1.3.0 to 1.5.0 which introduces breaking changes in config file, check [their migration guide](https://autorestic.vercel.app/migration/1.4_1.5) for more details. - For `pkgs.python3.pkgs.ipython`, its direct dependency `pkgs.python3.pkgs.matplotlib-inline` @@ -498,6 +502,10 @@ In addition to numerous new and upgraded packages, this release has the followin - The Tor SOCKS proxy is now actually disabled if `services.tor.client.enable` is set to `false` (the default). If you are using this functionality but didn't change the setting or set it to `false`, you now need to set it to `true`. +- `services.github-runner` has been hardened. Notably address families and + system calls have been restricted, which may adversely affect some kinds of + testing, e.g. using `AF_BLUETOOTH` to test bluetooth devices. + - The terraform 0.12 compatibility has been removed and the `terraform.withPlugins` and `terraform-providers.mkProvider` implementations simplified. Providers now need to be stored under `$out/libexec/terraform-providers/<registry>/<owner>/<name>/<version>/<os>_<arch>/terraform-provider-<name>_v<version>` (which mkProvider does). @@ -716,13 +724,21 @@ In addition to numerous new and upgraded packages, this release has the followin - `nixos-generate-config` now puts the dhcp configuration in `hardware-configuration.nix` instead of `configuration.nix`. -- ORY Kratos was updated to version 0.8.3-alpha.1.pre.0, which introduces some breaking changes: +- ORY Kratos was updated to version 0.9.0-alpha.3, which introduces some breaking changes: + - All endpoints at the Admin API are now exposed at `/admin/`. For example, endpoint `https://kratos:4434/identities` is now exposed at `https://kratos:4434/admin/identities` + - Configuration key `selfservice.whitelisted_return_urls` has been renamed to `allowed_return_urls` + - The `password_identifier` form field of the password login strategy has been renamed to `identifier` to make compatibility with passwordless flows possible. + - Instead of having a global `default_schema_url` which developers used to update their schema, you now need to define the `default_schema_id` which must reference schema ID in your config. + - Calling `/self-service/recovery` without flow ID or with an invalid flow ID while authenticated will now respond with an error instead of redirecting to the default page. - If you are relying on the SQLite images, update your Docker Pull commands as follows: - `docker pull oryd/kratos:{version}` - Additionally, all passwords now have to be at least 8 characters long. - For more details, see: - [Release Notes for v0.8.1-alpha-1](https://github.com/ory/kratos/releases/tag/v0.8.1-alpha.1) - [Release Notes for v0.8.2-alpha-1](https://github.com/ory/kratos/releases/tag/v0.8.2-alpha.1) + - [Release Notes for v0.9.0-alpha-1](https://github.com/ory/kratos/releases/tag/v0.9.0-alpha.1) + - [Release Notes for v0.9.0-alpha-3](https://github.com/ory/kratos/releases/tag/v0.9.0-alpha.3) + - `fetchFromSourcehut` now allows fetching repositories recursively using `fetchgit` or `fetchhg` if the argument `fetchSubmodules` @@ -762,6 +778,8 @@ In addition to numerous new and upgraded packages, this release has the followin - `security.pam.ussh` has been added, which allows authorizing PAM sessions based on SSH _certificates_ held within an SSH agent, using [pam-ussh](https://github.com/uber/pam-ussh). +- The `vscode-extensions.ionide.ionide-fsharp` package has been updated to 6.0.0 and now requires .NET 6.0. + - The `zrepl` package has been updated from 0.4.0 to 0.5: - The RPC protocol version was bumped; all zrepl daemons in a setup must be updated and restarted before replication can resume. @@ -802,6 +820,9 @@ In addition to numerous new and upgraded packages, this release has the followin - The `nss` package was split into `nss_esr` and `nss_latest`, with `nss` being an alias for `nss_esr`. This was done to ease maintenance of `nss` and dependent high-profile packages like `firefox`. +- The Nextcloud module now supports to create a Mysql database automatically + with `services.nextcloud.database.createLocally` enabled. + - The `spark3` package has been updated from 3.1.2 to 3.2.1 ([#160075](https://github.com/NixOS/nixpkgs/pull/160075)): - Testing has been enabled for `aarch64-linux` in addition to `x86_64-linux`. @@ -809,4 +830,8 @@ In addition to numerous new and upgraded packages, this release has the followin - The `programs.nncp` options were added for generating host-global NNCP configuration. +- The option `services.snapserver.openFirewall` will no longer default to + `true` starting with NixOS 22.11. Enable it explicitly if you need to control + Snapserver remotely or connect streamig clients from other hosts. + <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix index d784befc9b8..299a30b0629 100644 --- a/nixos/modules/hardware/video/amdgpu-pro.nix +++ b/nixos/modules/hardware/video/amdgpu-pro.nix @@ -51,9 +51,10 @@ in (isYes "KALLSYMS_ALL") ]; - boot.initrd.extraUdevRulesCommands = '' + boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.systemd.enable) '' cp -v ${package}/etc/udev/rules.d/*.rules $out/ ''; + boot.initrd.services.udev.packages = [ package ]; environment.systemPackages = [ package.vulkan ] ++ diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index dfafda77cb5..1707935ad5b 100644 --- a/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,7 +1,7 @@ { - x86_64-linux = "/nix/store/0n2wfvi1i3fg97cjc54wslvk0804y0sn-nix-2.7.0"; - i686-linux = "/nix/store/4p27c1k9z99pli6x8cxfph20yfyzn9nh-nix-2.7.0"; - aarch64-linux = "/nix/store/r9yr8ijsb0gi9r7y92y3yzyld59yp0kj-nix-2.7.0"; - x86_64-darwin = "/nix/store/hyfj5imsd0c4amlcjpf8l6w4q2draaj3-nix-2.7.0"; - aarch64-darwin = "/nix/store/9l96qllhbb6xrsjaai76dn74ap7rq92n-nix-2.7.0"; + x86_64-linux = "/nix/store/yx36yzxpw1hn4fz8iyf1rfyd56jg3yf4-nix-2.8.0"; + i686-linux = "/nix/store/c0hg806zvwg800qbszzj8ff4a224kjgf-nix-2.8.0"; + aarch64-linux = "/nix/store/wic2832ll53q392r2wks4xr2nrk7p8p5-nix-2.8.0"; + x86_64-darwin = "/nix/store/5yqdvnkmkrhl36xh0qy31pymdphjimdd-nix-2.8.0"; + aarch64-darwin = "/nix/store/izc9592szrnpv8n86hr88bhpyc9g6b4s-nix-2.8.0"; } diff --git a/nixos/modules/services/audio/snapserver.nix b/nixos/modules/services/audio/snapserver.nix index 6d5ce98df89..91d97a0b551 100644 --- a/nixos/modules/services/audio/snapserver.nix +++ b/nixos/modules/services/audio/snapserver.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, options, lib, pkgs, ... }: with lib; @@ -101,6 +101,8 @@ in { openFirewall = mkOption { type = types.bool; + # Make the behavior consistent with other services. Set the default to + # false and remove the accompanying warning after NixOS 22.05 is released. default = true; description = '' Whether to automatically open the specified ports in the firewall. @@ -273,10 +275,16 @@ in { config = mkIf cfg.enable { - # https://github.com/badaix/snapcast/blob/98ac8b2fb7305084376607b59173ce4097c620d8/server/streamreader/stream_manager.cpp#L85 - warnings = filter (w: w != "") (mapAttrsToList (k: v: if v.type == "spotify" then '' - services.snapserver.streams.${k}.type = "spotify" is deprecated, use services.snapserver.streams.${k}.type = "librespot" instead. - '' else "") cfg.streams); + warnings = + # https://github.com/badaix/snapcast/blob/98ac8b2fb7305084376607b59173ce4097c620d8/server/streamreader/stream_manager.cpp#L85 + filter (w: w != "") (mapAttrsToList (k: v: if v.type == "spotify" then '' + services.snapserver.streams.${k}.type = "spotify" is deprecated, use services.snapserver.streams.${k}.type = "librespot" instead. + '' else "") cfg.streams) + # Remove this warning after NixOS 22.05 is released. + ++ optional (options.services.snapserver.openFirewall.highestPrio >= (mkOptionDefault null).priority) '' + services.snapserver.openFirewall will no longer default to true starting with NixOS 22.11. + Enable it explicitly if you need to control Snapserver remotely. + ''; systemd.services.snapserver = { after = [ "network.target" ]; @@ -304,8 +312,8 @@ in { networking.firewall.allowedTCPPorts = optionals cfg.openFirewall [ cfg.port ] - ++ optional cfg.tcp.enable cfg.tcp.port - ++ optional cfg.http.enable cfg.http.port; + ++ optional (cfg.openFirewall && cfg.tcp.enable) cfg.tcp.port + ++ optional (cfg.openFirewall && cfg.http.enable) cfg.http.port; }; meta = { diff --git a/nixos/modules/services/continuous-integration/github-runner.nix b/nixos/modules/services/continuous-integration/github-runner.nix index a7645e1f56e..30dd919b81a 100644 --- a/nixos/modules/services/continuous-integration/github-runner.nix +++ b/nixos/modules/services/continuous-integration/github-runner.nix @@ -299,6 +299,16 @@ in RestrictRealtime = true; RestrictSUIDSGID = true; UMask = "0066"; + ProtectProc = "invisible"; + ProcSubset = "pid"; + SystemCallFilter = [ + "~@debug" + "~@mount" + "~@privileged" + "~@cpu-emulation" + "~@obsolete" + ]; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ]; # Needs network access PrivateNetwork = false; diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index b7e1110f1cd..f1e074587b3 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -318,7 +318,9 @@ in { systemd.services = { mailman = { description = "GNU Mailman Master Process"; - after = [ "network.target" ]; + before = lib.optional cfg.enablePostfix "postfix.service"; + after = [ "network.target" ] + ++ lib.optional cfg.enablePostfix "postfix-setup.service"; restartTriggers = [ config.environment.etc."mailman.cfg".source ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index 23d3574ae27..da14b6eef7e 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -723,23 +723,10 @@ in { ${setgidGroup}.gid = config.ids.gids.postdrop; }; - systemd.services.postfix = - { description = "Postfix mail server"; - - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - path = [ pkgs.postfix ]; - - serviceConfig = { - Type = "forking"; - Restart = "always"; - PIDFile = "/var/lib/postfix/queue/pid/master.pid"; - ExecStart = "${pkgs.postfix}/bin/postfix start"; - ExecStop = "${pkgs.postfix}/bin/postfix stop"; - ExecReload = "${pkgs.postfix}/bin/postfix reload"; - }; - - preStart = '' + systemd.services.postfix-setup = + { description = "Setup for Postfix mail server"; + serviceConfig.Type = "oneshot"; + script = '' # Backwards compatibility if [ ! -d /var/lib/postfix ] && [ -d /var/postfix ]; then mkdir -p /var/lib @@ -777,6 +764,24 @@ in ''; }; + systemd.services.postfix = + { description = "Postfix mail server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "postfix-setup.service" ]; + requires = [ "postfix-setup.service" ]; + path = [ pkgs.postfix ]; + + serviceConfig = { + Type = "forking"; + Restart = "always"; + PIDFile = "/var/lib/postfix/queue/pid/master.pid"; + ExecStart = "${pkgs.postfix}/bin/postfix start"; + ExecStop = "${pkgs.postfix}/bin/postfix stop"; + ExecReload = "${pkgs.postfix}/bin/postfix reload"; + }; + }; + services.postfix.config = (mapAttrs (_: v: mkDefault v) { compatibility_level = pkgs.postfix.version; mail_owner = cfg.user; diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix index ff63c41e193..e2080492998 100644 --- a/nixos/modules/services/misc/taskserver/default.nix +++ b/nixos/modules/services/misc/taskserver/default.nix @@ -106,7 +106,7 @@ let certtool = "${pkgs.gnutls.bin}/bin/certtool"; - nixos-taskserver = with pkgs.python2.pkgs; buildPythonApplication { + nixos-taskserver = with pkgs.python3.pkgs; buildPythonApplication { name = "nixos-taskserver"; src = pkgs.runCommand "nixos-taskserver-src" { preferLocalBuild = true; } '' @@ -277,10 +277,6 @@ in { example = "::"; description = '' The address (IPv4, IPv6 or DNS) to listen on. - - If the value is something else than <literal>localhost</literal> the - port defined by <option>listenPort</option> is automatically added to - <option>networking.firewall.allowedTCPPorts</option>. ''; }; @@ -292,6 +288,14 @@ in { ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Whether to open the firewall for the specified Taskserver port. + ''; + }; + fqdn = mkOption { type = types.str; default = "localhost"; @@ -560,7 +564,7 @@ in { ''; }; }) - (mkIf (cfg.enable && cfg.listenHost != "localhost") { + (mkIf (cfg.enable && cfg.openFirewall) { networking.firewall.allowedTCPPorts = [ cfg.listenPort ]; }) ]; diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py index 22a3d8d5311..fec05728b2b 100644 --- a/nixos/modules/services/misc/taskserver/helper-tool.py +++ b/nixos/modules/services/misc/taskserver/helper-tool.py @@ -90,7 +90,7 @@ def certtool_cmd(*args, **kwargs): """ return subprocess.check_output( [CERTTOOL_COMMAND] + list(args), - preexec_fn=lambda: os.umask(0077), + preexec_fn=lambda: os.umask(0o077), stderr=subprocess.STDOUT, **kwargs ) @@ -164,7 +164,7 @@ def generate_key(org, user): pubcert = os.path.join(basedir, "public.cert") try: - os.makedirs(basedir, mode=0700) + os.makedirs(basedir, mode=0o700) certtool_cmd("-p", "--bits", CERT_BITS, "--outfile", privkey) @@ -301,7 +301,7 @@ class Organisation(object): return None if name not in self.users.keys(): output = taskd_cmd("add", "user", self.name, name, - capture_stdout=True) + capture_stdout=True, encoding='utf-8') key = RE_USERKEY.search(output) if key is None: msg = "Unable to find key while creating user {}." @@ -412,9 +412,9 @@ class Manager(object): if org is not None: if self.ignore_imperative and is_imperative(name): return - for user in org.users.keys(): + for user in list(org.users.keys()): org.del_user(user) - for group in org.groups.keys(): + for group in list(org.groups.keys()): org.del_group(group) taskd_cmd("remove", "org", name) del self._lazy_orgs[name] diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index 3049c4f2bce..d409f060228 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -68,8 +68,7 @@ in systemd.services.bird2 = { description = "BIRD Internet Routing Daemon"; wantedBy = [ "multi-user.target" ]; - reloadIfChanged = true; - restartTriggers = [ config.environment.etc."bird/bird2.conf".source ]; + reloadTriggers = [ config.environment.etc."bird/bird2.conf".source ]; serviceConfig = { Type = "forking"; Restart = "on-failure"; diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix index ca9c422e6d7..cb53cc01f52 100644 --- a/nixos/modules/services/networking/consul.nix +++ b/nixos/modules/services/networking/consul.nix @@ -80,13 +80,21 @@ in The name of the interface to pull the bind_addr from. ''; }; + }; + forceAddrFamily = mkOption { + type = types.enum [ "any" "ipv4" "ipv6" ]; + default = "any"; + description = '' + Whether to bind ipv4/ipv6 or both kind of addresses. + ''; }; forceIpv4 = mkOption { - type = types.bool; - default = false; + type = types.nullOr types.bool; + default = null; description = '' + Deprecated: Use consul.forceAddrFamily instead. Whether we should force the interfaces to only pull ipv4 addresses. ''; }; @@ -175,6 +183,13 @@ in systemPackages = [ cfg.package ]; }; + warnings = lib.flatten [ + (lib.optional (cfg.forceIpv4 != null) '' + The option consul.forceIpv4 is deprecated, please use + consul.forceAddrFamily instead. + '') + ]; + systemd.services.consul = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ] ++ systemdDevices; @@ -196,15 +211,21 @@ in }); path = with pkgs; [ iproute2 gnugrep gawk consul ]; - preStart = '' + preStart = let + family = if cfg.forceAddrFamily == "ipv6" then + "-6" + else if cfg.forceAddrFamily == "ipv4" then + "-4" + else + ""; + in '' mkdir -m 0700 -p ${dataDir} chown -R consul ${dataDir} # Determine interface addresses getAddrOnce () { - ip addr show dev "$1" \ - | grep 'inet${optionalString (cfg.forceIpv4) " "}.*scope global' \ - | awk -F '[ /\t]*' '{print $3}' | head -n 1 + ip ${family} addr show dev "$1" scope global \ + | awk -F '[ /\t]*' '/inet/ {print $3}' | head -n 1 } getAddr () { ADDR="$(getAddrOnce $1)" @@ -234,6 +255,11 @@ in }; } + # deprecated + (mkIf (cfg.forceIpv4 != null && cfg.forceIpv4) { + services.consul.forceAddrFamily = "ipv4"; + }) + (mkIf (cfg.alerts.enable) { systemd.services.consul-alerts = { wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix index 4fe720bf044..e02ab3cb6b3 100644 --- a/nixos/modules/services/ttys/kmscon.nix +++ b/nixos/modules/services/ttys/kmscon.nix @@ -1,6 +1,6 @@ { config, pkgs, lib, ... }: let - inherit (lib) mkOption types mkIf; + inherit (lib) mapAttrs mkIf mkOption optional optionals types; cfg = config.services.kmscon; @@ -28,6 +28,19 @@ in { default = false; }; + fonts = mkOption { + description = "Fonts used by kmscon, in order of priority."; + default = null; + example = lib.literalExpression ''[ { name = "Source Code Pro"; package = pkgs.source-code-pro; } ]''; + type = with types; + let fontType = submodule { + options = { + name = mkOption { type = str; description = "Font name, as used by fontconfig."; }; + package = mkOption { type = package; description = "Package providing the font."; }; + }; + }; in nullOr (nonEmptyListOf fontType); + }; + extraConfig = mkOption { description = "Extra contents of the kmscon.conf file."; type = types.lines; @@ -87,11 +100,17 @@ in { systemd.services.systemd-vconsole-setup.enable = false; - services.kmscon.extraConfig = mkIf cfg.hwRender '' - drm - hwaccel - ''; + services.kmscon.extraConfig = + let + render = optionals cfg.hwRender [ "drm" "hwaccel" ]; + fonts = optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}"; + in lib.concatStringsSep "\n" (render ++ fonts); hardware.opengl.enable = mkIf cfg.hwRender true; + + fonts = mkIf (cfg.fonts != null) { + fontconfig.enable = true; + fonts = map (f: f.package) cfg.fonts; + }; }; } diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index b32220a5e57..f74b6bda0ca 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -251,6 +251,23 @@ in { ''; }; + database = { + + createLocally = mkOption { + type = types.bool; + default = false; + description = '' + Create the database and database user locally. Only available for + mysql database. + Note that this option will use the latest version of MariaDB which + is not officially supported by Nextcloud. As for now a workaround + is used to also support MariaDB version >= 10.6. + ''; + }; + + }; + + config = { dbtype = mkOption { type = types.enum [ "sqlite" "pgsql" "mysql" ]; @@ -583,6 +600,12 @@ in { else pkgs.php80; } + { assertions = [ + { assertion = cfg.database.createLocally -> cfg.config.dbtype == "mysql"; + message = ''services.nextcloud.config.dbtype must be set to mysql if services.nextcloud.database.createLocally is set to true.''; + } + ]; } + { systemd.timers.nextcloud-cron = { wantedBy = [ "timers.target" ]; timerConfig.OnBootSec = "5m"; @@ -811,6 +834,32 @@ in { environment.systemPackages = [ occ ]; + services.mysql = lib.mkIf cfg.database.createLocally { + enable = true; + package = lib.mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.config.dbname ]; + ensureUsers = [{ + name = cfg.config.dbuser; + ensurePermissions = { "${cfg.config.dbname}.*" = "ALL PRIVILEGES"; }; + }]; + # FIXME(@Ma27) Nextcloud isn't compatible with mariadb 10.6, + # this is a workaround. + # See https://help.nextcloud.com/t/update-to-next-cloud-21-0-2-has-get-an-error/117028/22 + settings = { + mysqld = { + innodb_read_only_compressed = 0; + }; + }; + initialScript = pkgs.writeText "mysql-init" '' + CREATE USER '${cfg.config.dbname}'@'localhost' IDENTIFIED BY '${builtins.readFile( cfg.config.dbpassFile )}'; + CREATE DATABASE IF NOT EXISTS ${cfg.config.dbname}; + GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, + CREATE TEMPORARY TABLES ON ${cfg.config.dbname}.* TO '${cfg.config.dbuser}'@'localhost' + IDENTIFIED BY '${builtins.readFile( cfg.config.dbpassFile )}'; + FLUSH privileges; + ''; + }; + services.nginx.enable = mkDefault true; services.nginx.virtualHosts.${cfg.hostName} = { diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix index 258d39d4918..e7e626c66f0 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome.nix @@ -22,6 +22,9 @@ let favorite-apps=[ 'org.gnome.Epiphany.desktop', 'org.gnome.Geary.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Photos.desktop', 'org.gnome.Nautilus.desktop' ] ''; + nixos-background-ligtht = pkgs.nixos-artwork.wallpapers.simple-blue; + nixos-background-dark = pkgs.nixos-artwork.wallpapers.simple-dark-gray; + nixos-gsettings-desktop-schemas = let defaultPackages = with pkgs; [ gsettings-desktop-schemas gnome.gnome-shell ]; in @@ -42,11 +45,11 @@ let chmod -R a+w $out/share/gsettings-schemas/nixos-gsettings-overrides cat - > $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/nixos-defaults.gschema.override <<- EOF [org.gnome.desktop.background] - picture-uri='file://${pkgs.nixos-artwork.wallpapers.simple-blue.gnomeFilePath}' - picture-uri-dark='file://${pkgs.nixos-artwork.wallpapers.simple-dark-gray.gnomeFilePath}' + picture-uri='file://${nixos-background-ligtht.gnomeFilePath}' + picture-uri-dark='file://${nixos-background-dark.gnomeFilePath}' [org.gnome.desktop.screensaver] - picture-uri='file://${pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom.gnomeFilePath}' + picture-uri='file://${nixos-background-dark.gnomeFilePath}' ${cfg.favoriteAppsOverride} @@ -56,6 +59,26 @@ let ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/ ''; + nixos-background-info = pkgs.writeTextFile rec { + name = "nixos-background-info"; + text = '' + <?xml version="1.0"?> + <!DOCTYPE wallpapers SYSTEM "gnome-wp-list.dtd"> + <wallpapers> + <wallpaper deleted="false"> + <name>Blobs</name> + <filename>${nixos-background-ligtht.gnomeFilePath}</filename> + <filename-dark>${nixos-background-dark.gnomeFilePath}</filename-dark> + <options>zoom</options> + <shade_type>solid</shade_type> + <pcolor>#3a4ba0</pcolor> + <scolor>#2f302f</scolor> + </wallpaper> + </wallpapers> + ''; + destination = "/share/gnome-background-properties/nixos.xml"; + }; + flashbackEnabled = cfg.flashback.enableMetacity || length cfg.flashback.customSessions > 0; flashbackWms = optional cfg.flashback.enableMetacity { wmName = "metacity"; @@ -431,6 +454,7 @@ in # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-3-38/elements/core/meta-gnome-core-shell.bst environment.systemPackages = with pkgs.gnome; [ adwaita-icon-theme + nixos-background-info gnome-backgrounds gnome-bluetooth gnome-color-manager @@ -439,8 +463,6 @@ in gnome-shell-extensions gnome-themes-extra pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in. - pkgs.nixos-artwork.wallpapers.simple-dark-gray - pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom pkgs.gnome-user-docs pkgs.orca pkgs.glib # for gsettings diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index dde07571b3e..57fc02a2e32 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -1,10 +1,11 @@ -{ config, lib, pkgs, ... }: +{ config, options, lib, pkgs, ... }: with lib; let luks = config.boot.initrd.luks; kernelPackages = config.boot.kernelPackages; + defaultPrio = (mkOptionDefault {}).priority; commonFunctions = '' die() { @@ -474,6 +475,16 @@ let preLVM = filterAttrs (n: v: v.preLVM) luks.devices; postLVM = filterAttrs (n: v: !v.preLVM) luks.devices; + stage1Crypttab = pkgs.writeText "initrd-crypttab" (lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: let + opts = v.crypttabExtraOpts + ++ optional v.allowDiscards "discard" + ++ optionals v.bypassWorkqueues [ "no-read-workqueue" "no-write-workqueue" ] + ++ optional (v.header != null) "header=${v.header}" + ++ optional (v.keyFileOffset != null) "keyfile-offset=${v.keyFileOffset}" + ++ optional (v.keyFileSize != null) "keyfile-size=${v.keyFileSize}" + ; + in "${n} ${v.device} ${if v.keyFile == null then "-" else v.keyFile} ${lib.concatStringsSep "," opts}") luks.devices)); + in { imports = [ @@ -802,6 +813,18 @@ in Commands that should be run right after we have mounted our LUKS device. ''; }; + + crypttabExtraOpts = mkOption { + type = with types; listOf singleLineStr; + default = []; + example = [ "_netdev" ]; + visible = false; + description = '' + Only used with systemd stage 1. + + Extra options to append to the last column of the generated crypttab file. + ''; + }; }; })); }; @@ -853,6 +876,31 @@ in -> versionAtLeast kernelPackages.kernel.version "5.9"; message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9"; } + + { assertion = config.boot.initrd.systemd.enable -> all (dev: !dev.fallbackToPassword) (attrValues luks.devices); + message = "boot.initrd.luks.devices.<name>.fallbackToPassword is implied by systemd stage 1."; + } + { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preLVM) (attrValues luks.devices); + message = "boot.initrd.luks.devices.<name>.preLVM is not used by systemd stage 1."; + } + { assertion = config.boot.initrd.systemd.enable -> options.boot.initrd.luks.reusePassphrases.highestPrio == defaultPrio; + message = "boot.initrd.luks.reusePassphrases has no effect with systemd stage 1."; + } + { assertion = config.boot.initrd.systemd.enable -> all (dev: dev.preOpenCommands == "" && dev.postOpenCommands == "") (attrValues luks.devices); + message = "boot.initrd.luks.devices.<name>.preOpenCommands and postOpenCommands is not supported by systemd stage 1. Please bind a service to cryptsetup.target or cryptsetup-pre.target instead."; + } + # TODO + { assertion = config.boot.initrd.systemd.enable -> !luks.gpgSupport; + message = "systemd stage 1 does not support GPG smartcards yet."; + } + # TODO + { assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support; + message = "systemd stage 1 does not support FIDO2 yet."; + } + # TODO + { assertion = config.boot.initrd.systemd.enable -> !luks.yubikeySupport; + message = "systemd stage 1 does not support Yubikeys yet."; + } ]; # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested @@ -867,7 +915,7 @@ in ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []); # copy the cryptsetup binary and it's dependencies - boot.initrd.extraUtilsCommands = '' + boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) '' copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass @@ -915,7 +963,7 @@ in ''} ''; - boot.initrd.extraUtilsCommandsTest = '' + boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) '' $out/bin/cryptsetup --version ${optionalString luks.yubikeySupport '' $out/bin/ykchalresp -V @@ -932,9 +980,27 @@ in ''} ''; - boot.initrd.preFailCommands = postCommands; - boot.initrd.preLVMCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands; - boot.initrd.postDeviceCommands = commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands; + boot.initrd.systemd = { + contents."/etc/crypttab".source = stage1Crypttab; + + extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup"; + + additionalUpstreamUnits = [ + "cryptsetup-pre.target" + "cryptsetup.target" + "remote-cryptsetup.target" + ]; + storePaths = [ + "${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup" + ]; + + }; + # We do this because we need the udev rules from the package + boot.initrd.services.lvm.enable = true; + + boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) postCommands; + boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand preLVM) + postCommands); + boot.initrd.postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) (commonFunctions + preCommands + concatStrings (mapAttrsToList openCommand postLVM) + postCommands); environment.systemPackages = [ pkgs.cryptsetup ]; }; diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index 5e42eda3875..d10ebac5682 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -420,7 +420,7 @@ let ${lib.optionalString (config.boot.initrd.secrets == {}) "exit 0"} - export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin + export PATH=${pkgs.coreutils}/bin:${pkgs.libarchive}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin function cleanup { if [ -n "$tmp" -a -d "$tmp" ]; then @@ -440,7 +440,7 @@ let ) config.boot.initrd.secrets) } - (cd "$tmp" && find . -print0 | sort -z | cpio --quiet -o -H newc -R +0:+0 --reproducible --null) | \ + (cd "$tmp" && find . -print0 | sort -z | bsdtar --uid 0 --gid 0 -cnf - -T - | bsdtar --null -cf - --format=newc @-) | \ ${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1" ''; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 57c17508aab..5158bc681e0 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -524,6 +524,8 @@ in systemd-confinement = handleTest ./systemd-confinement.nix {}; systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {}; systemd-escaping = handleTest ./systemd-escaping.nix {}; + systemd-initrd-luks-keyfile = handleTest ./systemd-initrd-luks-keyfile.nix {}; + systemd-initrd-luks-password = handleTest ./systemd-initrd-luks-password.nix {}; systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; }; systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {}; systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix {}; diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 30a5b5c45b3..ea2b2d04ed1 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -299,6 +299,13 @@ let virtualisation.qemu.diskInterface = if grubVersion == 1 then "scsi" else "virtio"; + # We don't want to have any networking in the guest whatsoever. + # Also, if any vlans are enabled, the guest will reboot + # (with a different configuration for legacy reasons), + # and spend 5 minutes waiting for the vlan interface to show up + # (which will never happen). + virtualisation.vlans = []; + boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true; hardware.enableAllFirmware = mkForce false; @@ -313,6 +320,7 @@ let docbook5 docbook_xsl_ns kmod.dev + libarchive.dev libxml2.bin libxslt.bin nixos-artwork.wallpapers.simple-dark-gray-bottom diff --git a/nixos/tests/nar-serve.nix b/nixos/tests/nar-serve.nix index 9ee738ffb17..bb95ccb3691 100644 --- a/nixos/tests/nar-serve.nix +++ b/nixos/tests/nar-serve.nix @@ -31,7 +31,7 @@ import ./make-test-python.nix ( # Create a fake cache with Nginx service the static files server.succeed( - "nix copy --to file:///var/www ${pkgs.hello}" + "nix --experimental-features nix-command copy --to file:///var/www ${pkgs.hello}" ) server.wait_for_unit("nginx.service") server.wait_for_open_port(80) diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix index 891001e30b2..63e0e2c5963 100644 --- a/nixos/tests/nextcloud/with-mysql-and-memcached.nix +++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix @@ -26,6 +26,7 @@ in { redis = false; memcached = true; }; + database.createLocally = true; config = { dbtype = "mysql"; dbname = "nextcloud"; @@ -38,28 +39,6 @@ in { }; }; - services.mysql = { - enable = true; - settings.mysqld = { - bind-address = "127.0.0.1"; - - # FIXME(@Ma27) Nextcloud isn't compatible with mariadb 10.6, - # this is a workaround. - # See https://help.nextcloud.com/t/update-to-next-cloud-21-0-2-has-get-an-error/117028/22 - innodb_read_only_compressed = 0; - }; - package = pkgs.mariadb; - - initialScript = pkgs.writeText "mysql-init" '' - CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2'; - CREATE DATABASE IF NOT EXISTS nextcloud; - GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, - CREATE TEMPORARY TABLES ON nextcloud.* TO 'nextcloud'@'localhost' - IDENTIFIED BY 'hunter2'; - FLUSH privileges; - ''; - }; - systemd.services.nextcloud-setup= { requires = ["mysql.service"]; after = ["mysql.service"]; diff --git a/nixos/tests/snapcast.nix b/nixos/tests/snapcast.nix index 30b8343e2ff..9b62e4724e7 100644 --- a/nixos/tests/snapcast.nix +++ b/nixos/tests/snapcast.nix @@ -19,6 +19,7 @@ in { port = port; tcp.port = tcpPort; http.port = httpPort; + openFirewall = true; buffer = bufferSize; streams = { mpd = { diff --git a/nixos/tests/systemd-initrd-luks-keyfile.nix b/nixos/tests/systemd-initrd-luks-keyfile.nix new file mode 100644 index 00000000000..970163c36a4 --- /dev/null +++ b/nixos/tests/systemd-initrd-luks-keyfile.nix @@ -0,0 +1,53 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: let + + keyfile = pkgs.writeText "luks-keyfile" '' + MIGHAoGBAJ4rGTSo/ldyjQypd0kuS7k2OSsmQYzMH6TNj3nQ/vIUjDn7fqa3slt2 + gV6EK3TmTbGc4tzC1v4SWx2m+2Bjdtn4Fs4wiBwn1lbRdC6i5ZYCqasTWIntWn+6 + FllUkMD5oqjOR/YcboxG8Z3B5sJuvTP9llsF+gnuveWih9dpbBr7AgEC + ''; + +in { + name = "systemd-initrd-luks-keyfile"; + + nodes.machine = { pkgs, ... }: { + # Use systemd-boot + virtualisation = { + emptyDiskImages = [ 512 ]; + useBootLoader = true; + useEFIBoot = true; + }; + boot.loader.systemd-boot.enable = true; + + environment.systemPackages = with pkgs; [ cryptsetup ]; + boot.initrd.systemd = { + enable = true; + emergencyAccess = true; + }; + + specialisation.boot-luks.configuration = { + boot.initrd.luks.devices = lib.mkVMOverride { + cryptroot = { + device = "/dev/vdc"; + keyFile = "/etc/cryptroot.key"; + }; + }; + virtualisation.bootDevice = "/dev/mapper/cryptroot"; + boot.initrd.systemd.contents."/etc/cryptroot.key".source = keyfile; + }; + }; + + testScript = '' + # Create encrypted volume + machine.wait_for_unit("multi-user.target") + machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdc") + + # Boot from the encrypted disk + machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") + machine.succeed("sync") + machine.crash() + + # Boot and decrypt the disk + machine.wait_for_unit("multi-user.target") + assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount") + ''; +}) diff --git a/nixos/tests/systemd-initrd-luks-password.nix b/nixos/tests/systemd-initrd-luks-password.nix new file mode 100644 index 00000000000..e8e651f7b35 --- /dev/null +++ b/nixos/tests/systemd-initrd-luks-password.nix @@ -0,0 +1,48 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: { + name = "systemd-initrd-luks-password"; + + nodes.machine = { pkgs, ... }: { + # Use systemd-boot + virtualisation = { + emptyDiskImages = [ 512 512 ]; + useBootLoader = true; + useEFIBoot = true; + }; + boot.loader.systemd-boot.enable = true; + + environment.systemPackages = with pkgs; [ cryptsetup ]; + boot.initrd.systemd = { + enable = true; + emergencyAccess = true; + }; + + specialisation.boot-luks.configuration = { + boot.initrd.luks.devices = lib.mkVMOverride { + # We have two disks and only type one password - key reuse is in place + cryptroot.device = "/dev/vdc"; + cryptroot2.device = "/dev/vdd"; + }; + virtualisation.bootDevice = "/dev/mapper/cryptroot"; + }; + }; + + testScript = '' + # Create encrypted volume + machine.wait_for_unit("multi-user.target") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -") + machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -") + + # Boot from the encrypted disk + machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf") + machine.succeed("sync") + machine.crash() + + # Boot and decrypt the disk + machine.start() + machine.wait_for_console_text("Please enter passphrase for disk cryptroot") + machine.send_console("supersecret\n") + machine.wait_for_unit("multi-user.target") + + assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount") + ''; +}) diff --git a/nixos/tests/taskserver.nix b/nixos/tests/taskserver.nix index f34782c7059..b2bd421e231 100644 --- a/nixos/tests/taskserver.nix +++ b/nixos/tests/taskserver.nix @@ -63,6 +63,7 @@ in { server = { services.taskserver.enable = true; services.taskserver.listenHost = "::"; + services.taskserver.openFirewall = true; services.taskserver.fqdn = "server"; services.taskserver.organisations = { testOrganisation.users = [ "alice" "foo" ]; diff --git a/nixos/tests/web-apps/netbox.nix b/nixos/tests/web-apps/netbox.nix index 95f24029ec9..35decdd49e8 100644 --- a/nixos/tests/web-apps/netbox.nix +++ b/nixos/tests/web-apps/netbox.nix @@ -5,7 +5,7 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: { maintainers = [ n0emis ]; }; - machine = { ... }: { + nodes.machine = { ... }: { services.netbox = { enable = true; secretKeyFile = pkgs.writeText "secret" '' |