about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml17
-rw-r--r--nixos/doc/manual/man-nixos-rebuild.xml8
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md4
-rw-r--r--nixos/modules/services/monitoring/collectd.nix21
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix10
-rw-r--r--nixos/modules/services/networking/stubby.nix220
-rw-r--r--nixos/modules/services/security/privacyidea.nix1
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix15
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix11
-rwxr-xr-x[-rw-r--r--]nixos/modules/system/boot/stage-2-init.sh1
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/collectd.nix33
-rw-r--r--nixos/tests/systemd.nix12
13 files changed, 165 insertions, 190 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 4d3d5700ffca1..d5e3190bf2882 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
@@ -14,8 +14,13 @@
   </itemizedlist>
   <section xml:id="sec-release-22.05-highlights">
     <title>Highlights</title>
-    <para>
-    </para>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          PHP 8.1 is now available
+        </para>
+      </listitem>
+    </itemizedlist>
   </section>
   <section xml:id="sec-release-22.05-new-services">
     <title>New Services</title>
@@ -185,6 +190,14 @@
           usage in non-X11 environments, e.g. Wayland.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The <literal>services.stubby</literal> module was converted to
+          a
+          <link xlink:href="https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md">settings-style</link>
+          configuration.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/man-nixos-rebuild.xml b/nixos/doc/manual/man-nixos-rebuild.xml
index 0e0ea5d74b0b5..6c7fc57f8d83c 100644
--- a/nixos/doc/manual/man-nixos-rebuild.xml
+++ b/nixos/doc/manual/man-nixos-rebuild.xml
@@ -535,12 +535,8 @@
      </para>
 
      <para>
-      If <option>--build-host</option> is not explicitly specified,
-      <option>--build-host</option> will implicitly be set to the same value as
-      <option>--target-host</option>. So, if you only specify
-      <option>--target-host</option> both building and activation will take
-      place remotely (and no build artifacts will be copied to the local
-      machine).
+      If <option>--build-host</option> is not explicitly specified, building
+      will take place locally.
      </para>
 
      <para>
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 966de78893738..98709455ae746 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -6,6 +6,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 ## Highlights {#sec-release-22.05-highlights}
 
+- PHP 8.1 is now available
+
 ## New Services {#sec-release-22.05-new-services}
 
 - [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
@@ -76,3 +78,5 @@ In addition to numerous new and upgraded packages, this release has the followin
   added, decoupling the setting of `SSH_ASKPASS` from
   `services.xserver.enable`. This allows easy usage in non-X11 environments,
   e.g. Wayland.
+
+- The `services.stubby` module was converted to a [settings-style](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md) configuration.
diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix
index 660d108587dee..8d81737a3ef0d 100644
--- a/nixos/modules/services/monitoring/collectd.nix
+++ b/nixos/modules/services/monitoring/collectd.nix
@@ -5,7 +5,7 @@ with lib;
 let
   cfg = config.services.collectd;
 
-  conf = pkgs.writeText "collectd.conf" ''
+  unvalidated_conf = pkgs.writeText "collectd-unvalidated.conf" ''
     BaseDir "${cfg.dataDir}"
     AutoLoadPlugin ${boolToString cfg.autoLoadPlugin}
     Hostname "${config.networking.hostName}"
@@ -30,6 +30,15 @@ let
     ${cfg.extraConfig}
   '';
 
+  conf = if cfg.validateConfig then
+    pkgs.runCommand "collectd.conf" {} ''
+      echo testing ${unvalidated_conf}
+      # collectd -t fails if BaseDir does not exist.
+      sed '1s/^BaseDir.*$/BaseDir "."/' ${unvalidated_conf} > collectd.conf
+      ${package}/bin/collectd -t -C collectd.conf
+      cp ${unvalidated_conf} $out
+    '' else unvalidated_conf;
+
   package =
     if cfg.buildMinimalPackage
     then minimalPackage
@@ -43,6 +52,16 @@ in {
   options.services.collectd = with types; {
     enable = mkEnableOption "collectd agent";
 
+    validateConfig = mkOption {
+      default = true;
+      description = ''
+        Validate the syntax of collectd configuration file at build time.
+        Disable this if you use the Include directive on files unavailable in
+        the build sandbox, or when cross-compiling.
+      '';
+      type = types.bool;
+    };
+
     package = mkOption {
       default = pkgs.collectd;
       defaultText = literalExpression "pkgs.collectd";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 3cdd7866bd4db..6f69f5919d1e0 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -46,11 +46,11 @@ in
     serviceConfig = {
       ExecStart = ''
         ${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \
-          --nginx.scrape-uri '${cfg.scrapeUri}' \
-          --nginx.ssl-verify ${boolToString cfg.sslVerify} \
-          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          --web.telemetry-path ${cfg.telemetryPath} \
-          --prometheus.const-labels ${concatStringsSep "," cfg.constLabels} \
+          --nginx.scrape-uri='${cfg.scrapeUri}' \
+          --nginx.ssl-verify=${boolToString cfg.sslVerify} \
+          --web.listen-address=${cfg.listenAddress}:${toString cfg.port} \
+          --web.telemetry-path=${cfg.telemetryPath} \
+          --prometheus.const-labels=${concatStringsSep "," cfg.constLabels} \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
     };
diff --git a/nixos/modules/services/networking/stubby.nix b/nixos/modules/services/networking/stubby.nix
index c5e0f929a1267..78c13798dde2a 100644
--- a/nixos/modules/services/networking/stubby.nix
+++ b/nixos/modules/services/networking/stubby.nix
@@ -1,180 +1,51 @@
-{ config, lib, pkgs, ...}:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
 let
   cfg = config.services.stubby;
+  settingsFormat = pkgs.formats.yaml { };
+  confFile = settingsFormat.generate "stubby.yml" cfg.settings;
+in {
+  imports = map (x:
+    (mkRemovedOptionModule [ "services" "stubby" x ]
+      "Stubby configuration moved to services.stubby.settings.")) [
+        "authenticationMode"
+        "fallbackProtocols"
+        "idleTimeout"
+        "listenAddresses"
+        "queryPaddingBlocksize"
+        "roundRobinUpstreams"
+        "subnetPrivate"
+        "upstreamServers"
+      ];
 
-  fallbacks = concatMapStringsSep "\n  " (x: "- ${x}") cfg.fallbackProtocols;
-  listeners = concatMapStringsSep "\n  " (x: "- ${x}") cfg.listenAddresses;
-
-  # By default, the recursive resolvers maintained by the getdns
-  # project itself are enabled. More information about both getdns's servers,
-  # as well as third party options for upstream resolvers, can be found here:
-  # https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
-  #
-  # You can override these values by supplying a yaml-formatted array of your
-  # preferred upstream resolvers in the following format:
-  #
-  # 106 # - address_data: IPv4 or IPv6 address of the upstream
-  #   port: Port for UDP/TCP (default is 53)
-  #   tls_auth_name: Authentication domain name checked against the server
-  #                  certificate
-  #   tls_pubkey_pinset: An SPKI pinset verified against the keys in the server
-  #                      certificate
-  #     - digest: Only "sha256" is currently supported
-  #       value: Base64 encoded value of the sha256 fingerprint of the public
-  #              key
-  #   tls_port: Port for TLS (default is 853)
-
-  defaultUpstream = ''
-    - address_data: 145.100.185.15
-      tls_auth_name: "dnsovertls.sinodun.com"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
-    - address_data: 145.100.185.16
-      tls_auth_name: "dnsovertls1.sinodun.com"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
-    - address_data: 185.49.141.37
-      tls_auth_name: "getdnsapi.net"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
-    - address_data: 2001:610:1:40ba:145:100:185:15
-      tls_auth_name: "dnsovertls.sinodun.com"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=
-    - address_data: 2001:610:1:40ba:145:100:185:16
-      tls_auth_name: "dnsovertls1.sinodun.com"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA=
-    - address_data: 2a04:b900:0:100::38
-      tls_auth_name: "getdnsapi.net"
-      tls_pubkey_pinset:
-        - digest: "sha256"
-          value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q=
-  '';
-
-  # Resolution type is not changeable here because it is required per the
-  # stubby documentation:
-  #
-  # "resolution_type: Work in stub mode only (not recursive mode) - required for Stubby
-  # operation."
-  #
-  # https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby
-
-  confFile = pkgs.writeText "stubby.yml" ''
-    resolution_type: GETDNS_RESOLUTION_STUB
-    dns_transport_list:
-      ${fallbacks}
-    appdata_dir: "/var/cache/stubby"
-    tls_authentication: ${cfg.authenticationMode}
-    tls_query_padding_blocksize: ${toString cfg.queryPaddingBlocksize}
-    edns_client_subnet_private: ${if cfg.subnetPrivate then "1" else "0"}
-    idle_timeout: ${toString cfg.idleTimeout}
-    listen_addresses:
-      ${listeners}
-    round_robin_upstreams: ${if cfg.roundRobinUpstreams then "1" else "0"}
-    ${cfg.extraConfig}
-    upstream_recursive_servers:
-    ${cfg.upstreamServers}
-  '';
-in
-
-{
   options = {
     services.stubby = {
 
       enable = mkEnableOption "Stubby DNS resolver";
 
-      fallbackProtocols = mkOption {
-        default = [ "GETDNS_TRANSPORT_TLS" ];
-        type = with types; listOf (enum [
-          "GETDNS_TRANSPORT_TLS"
-          "GETDNS_TRANSPORT_TCP"
-          "GETDNS_TRANSPORT_UDP"
-        ]);
-        description = ''
-          Ordered list composed of one or more transport protocols.
-          Strict mode should only use <literal>GETDNS_TRANSPORT_TLS</literal>.
-          Other options are <literal>GETDNS_TRANSPORT_UDP</literal> and
-          <literal>GETDNS_TRANSPORT_TCP</literal>.
+      settings = mkOption {
+        type = types.attrsOf settingsFormat.type;
+        example = lib.literalExpression ''
+          pkgs.stubby.passthru.settingsExample // {
+            upstream_recursive_servers = [{
+              address_data = "158.64.1.29";
+              tls_auth_name = "kaitain.restena.lu";
+              tls_pubkey_pinset = [{
+                digest = "sha256";
+                value = "7ftvIkA+UeN/ktVkovd/7rPZ6mbkhVI7/8HnFJIiLa4=";
+              }];
+            }];
+          };
         '';
-      };
-
-      authenticationMode = mkOption {
-        default = "GETDNS_AUTHENTICATION_REQUIRED";
-        type = types.enum [
-          "GETDNS_AUTHENTICATION_REQUIRED"
-          "GETDNS_AUTHENTICATION_NONE"
-        ];
         description = ''
-          Selects the Strict or Opportunistic usage profile.
-          For strict, set to <literal>GETDNS_AUTHENTICATION_REQUIRED</literal>.
-          for opportunistic, use <literal>GETDNS_AUTHENTICATION_NONE</literal>.
-        '';
-      };
-
-      queryPaddingBlocksize = mkOption {
-        default = 128;
-        type = types.int;
-        description = ''
-          EDNS0 option to pad the size of the DNS query to the given blocksize.
-        '';
-      };
-
-      subnetPrivate = mkOption {
-        default = true;
-        type = types.bool;
-        description = ''
-          EDNS0 option for ECS client privacy. Default is
-          <literal>true</literal>. If set, this option prevents the client
-          subnet from being sent to authoritative nameservers.
-        '';
-      };
-
-      idleTimeout = mkOption {
-        default = 10000;
-        type = types.int;
-        description = "EDNS0 option for keepalive idle timeout expressed in
-        milliseconds.";
-      };
-
-      listenAddresses = mkOption {
-        default = [ "127.0.0.1" "0::1" ];
-        type = with types; listOf str;
-        description = ''
-          Sets the listen address for the stubby daemon.
-          Uses port 53 by default.
-          Ise IP@port to specify a different port.
-        '';
-      };
-
-      roundRobinUpstreams = mkOption {
-        default = true;
-        type = types.bool;
-        description = ''
-          Instructs stubby to distribute queries across all available name
-          servers. Default is <literal>true</literal>. Set to
-          <literal>false</literal> in order to use the first available.
-        '';
-      };
-
-      upstreamServers = mkOption {
-        default = defaultUpstream;
-        type = types.lines;
-        description = ''
-          Replace default upstreams. See <citerefentry><refentrytitle>stubby
-          </refentrytitle><manvolnum>1</manvolnum></citerefentry> for an
-          example of the entry formatting. In Strict mode, at least one of the
-          following settings must be supplied for each nameserver:
-          <literal>tls_auth_name</literal> or
-          <literal>tls_pubkey_pinset</literal>.
+          Content of the Stubby configuration file. All Stubby settings may be set or queried
+          here. The default settings are available at
+          <literal>pkgs.stubby.passthru.settingsExample</literal>. See
+          <link xlink:href="https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby"/>.
+          A list of the public recursive servers can be found here:
+          <link xlink:href="https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers"/>.
         '';
       };
 
@@ -184,20 +55,21 @@ in
         description = "Enable or disable debug level logging.";
       };
 
-      extraConfig = mkOption {
-        default = "";
-        type = types.lines;
-        description = ''
-          Add additional configuration options. see <citerefentry>
-          <refentrytitle>stubby</refentrytitle><manvolnum>1</manvolnum>
-          </citerefentry>for more options.
-        '';
-      };
     };
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.stubby ];
+    assertions = [{
+      assertion =
+        (cfg.settings.resolution_type or "") == "GETDNS_RESOLUTION_STUB";
+      message = ''
+        services.stubby.settings.resolution_type must be set to "GETDNS_RESOLUTION_STUB".
+        Is services.stubby.settings unset?
+      '';
+    }];
+
+    services.stubby.settings.appdata_dir = "/var/cache/stubby";
+
     systemd.services.stubby = {
       description = "Stubby local DNS resolver";
       after = [ "network.target" ];
diff --git a/nixos/modules/services/security/privacyidea.nix b/nixos/modules/services/security/privacyidea.nix
index dacbffec04e02..b8e2d9a8b0dfc 100644
--- a/nixos/modules/services/security/privacyidea.nix
+++ b/nixos/modules/services/security/privacyidea.nix
@@ -204,6 +204,7 @@ in
       systemd.services.privacyidea = let
         piuwsgi = pkgs.writeText "uwsgi.json" (builtins.toJSON {
           uwsgi = {
+            buffer-size = 8192;
             plugins = [ "python3" ];
             pythonpath = "${penv}/${uwsgi.python3.sitePackages}";
             socket = "/run/privacyidea/socket";
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 96e45cfc4f77d..ba8e874f2dede 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -317,9 +317,12 @@ let
           ${optionalString (hasSSL && vhost.sslTrustedCertificate != null) ''
             ssl_trusted_certificate ${vhost.sslTrustedCertificate};
           ''}
-          ${optionalString vhost.rejectSSL ''
+          ${optionalString (hasSSL && vhost.rejectSSL) ''
             ssl_reject_handshake on;
           ''}
+          ${optionalString (hasSSL && vhost.kTLS) ''
+            ssl_conf_command Options KTLS;
+          ''}
 
           ${mkBasicAuth vhostName vhost}
 
@@ -825,6 +828,14 @@ in
       }
 
       {
+        assertion = any (host: host.kTLS) (attrValues virtualHosts) -> versionAtLeast cfg.package.version "1.21.4";
+        message = ''
+          services.nginx.virtualHosts.<name>.kTLS requires nginx version
+          1.21.4 or above; see the documentation for services.nginx.package.
+        '';
+      }
+
+      {
         assertion = all (host: !(host.enableACME && host.useACMEHost != null)) (attrValues virtualHosts);
         message = ''
           Options services.nginx.service.virtualHosts.<name>.enableACME and
@@ -900,7 +911,7 @@ in
         PrivateMounts = true;
         # System Call Filtering
         SystemCallArchitectures = "native";
-        SystemCallFilter = "~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileged @setuid @mincore";
+        SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid @mincore" ] ++ optionals (cfg.package != pkgs.tengine) [ "~@ipc" ];
       };
     };
 
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 7ee041d372113..7f49ce9586cac 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -147,6 +147,17 @@ with lib;
       '';
     };
 
+    kTLS = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable kTLS support.
+        Implementing TLS in the kernel (kTLS) improves performance by significantly
+        reducing the need for copying operations between user space and the kernel.
+        Required Nginx version 1.21.4 or later.
+      '';
+    };
+
     sslCertificate = mkOption {
       type = types.path;
       example = "/var/host.cert";
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index afaca2e4158d7..a90f58042d2d6 100644..100755
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -172,4 +172,5 @@ echo "starting systemd..."
 
 PATH=/run/current-system/systemd/lib/systemd:@fsPackagesPath@ \
     LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive @systemdUnitPathEnvVar@ \
+    TZDIR=/etc/zoneinfo \
     exec @systemdExecutable@
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 80645283c872d..8ac2cb7fe790c 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -70,6 +70,7 @@ in
   cloud-init = handleTest ./cloud-init.nix {};
   cntr = handleTest ./cntr.nix {};
   cockroachdb = handleTestOn ["x86_64-linux"] ./cockroachdb.nix {};
+  collectd = handleTest ./collectd.nix {};
   consul = handleTest ./consul.nix {};
   containers-bridge = handleTest ./containers-bridge.nix {};
   containers-custom-pkgs.nix = handleTest ./containers-custom-pkgs.nix {};
@@ -362,6 +363,7 @@ in
   php = handleTest ./php {};
   php74 = handleTest ./php { php = pkgs.php74; };
   php80 = handleTest ./php { php = pkgs.php80; };
+  php81 = handleTest ./php { php = pkgs.php81; };
   pinnwand = handleTest ./pinnwand.nix {};
   plasma5 = handleTest ./plasma5.nix {};
   plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {};
diff --git a/nixos/tests/collectd.nix b/nixos/tests/collectd.nix
new file mode 100644
index 0000000000000..cb196224a2317
--- /dev/null
+++ b/nixos/tests/collectd.nix
@@ -0,0 +1,33 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "collectd";
+  meta = { };
+
+  machine =
+    { pkgs, ... }:
+
+    {
+      services.collectd = {
+        enable = true;
+        plugins = {
+          rrdtool = ''
+            DataDir "/var/lib/collectd/rrd"
+          '';
+          load = "";
+        };
+      };
+      environment.systemPackages = [ pkgs.rrdtool ];
+    };
+
+  testScript = ''
+    machine.wait_for_unit("collectd.service")
+    hostname = machine.succeed("hostname").strip()
+    file = f"/var/lib/collectd/rrd/{hostname}/load/load.rrd"
+    machine.wait_for_file(file);
+    machine.succeed(f"rrdinfo {file} | logger")
+    # check that this file contains a shortterm metric
+    machine.succeed(f"rrdinfo {file} | grep -F 'ds[shortterm].min = '")
+    # check that there are frequent updates
+    machine.succeed(f"cp {file} before")
+    machine.wait_until_fails(f"cmp before {file}")
+  '';
+})
diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix
index 6561f7efe1a5f..f86daa5eea974 100644
--- a/nixos/tests/systemd.nix
+++ b/nixos/tests/systemd.nix
@@ -31,6 +31,13 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       umount /tmp/shared
     '';
 
+    systemd.services.oncalendar-test = {
+      description = "calendar test";
+      # Japan does not have DST which makes the test a little bit simpler
+      startAt = "Wed 10:00 Asia/Tokyo";
+      script = "true";
+    };
+
     systemd.services.testservice1 = {
       description = "Test Service 1";
       wantedBy = [ "multi-user.target" ];
@@ -69,6 +76,11 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     # wait for user services
     machine.wait_for_unit("default.target", "alice")
 
+    # Regression test for https://github.com/NixOS/nixpkgs/issues/105049
+    with subtest("systemd reads timezone database in /etc/zoneinfo"):
+        timer = machine.succeed("TZ=UTC systemctl show --property=TimersCalendar oncalendar-test.timer")
+        assert re.search("next_elapse=Wed ....-..-.. 01:00:00 UTC", timer), f"got {timer.strip()}"
+
     # Regression test for https://github.com/NixOS/nixpkgs/issues/35415
     with subtest("configuration files are recognized by systemd"):
         machine.succeed("test -e /system_conf_read")