# This test verifies that systemd-timesyncd can resolve the NTP server hostname when DNSSEC validation
# fails even though it is enforced in the systemd-resolved settings. It is required in order to solve
# the chicken-and-egg problem when DNSSEC validation needs the correct time to work, but to set the
# correct time, we need to connect to an NTP server, which usually requires resolving its hostname.
#
# This test does the following:
# - Sets up a DNS server (tinydns) listening on the eth1 ip addess, serving .ntp and fake.ntp records.
# - Configures that DNS server as a resolver and enables DNSSEC in systemd-resolved settings.
# - Configures systemd-timesyncd to use fake.ntp hostname as an NTP server.
# - Performs a regular DNS lookup, to ensure it fails due to broken DNSSEC.
# - Waits until systemd-timesyncd resolves fake.ntp by checking its debug output.
#   Here, we don't expect systemd-timesyncd to connect and synchronize time because there is no NTP
#   server running. For this test to succeed, we only need to ensure that systemd-timesyncd
#   resolves the IP address of the fake.ntp host.

import ./make-test-python.nix ({ pkgs, ... }:

let
  ntpHostname = "fake.ntp";
  ntpIP = "192.0.2.1";
in
{
  name = "systemd-timesyncd";
  nodes.machine = { pkgs, lib, config, ... }:
    let
      eth1IP = (lib.head config.networking.interfaces.eth1.ipv4.addresses).address;
    in
    {
      # Setup a local DNS server for the NTP domain on the eth1 IP address
      services.tinydns = {
        enable = true;
        ip = eth1IP;
        data = ''
          .ntp:${eth1IP}
          +.${ntpHostname}:${ntpIP}
        '';
      };

      # Enable systemd-resolved with DNSSEC and use the local DNS as a name server
      services.resolved.enable = true;
      services.resolved.dnssec = "true";
      networking.nameservers = [ eth1IP ];

      # Configure systemd-timesyncd to use our NTP hostname
      services.timesyncd.enable = lib.mkForce true;
      services.timesyncd.servers = [ ntpHostname ];
      services.timesyncd.extraConfig = ''
        FallbackNTP=${ntpHostname}
      '';

      # The debug output is necessary to determine whether systemd-timesyncd successfully resolves our NTP hostname or not
      systemd.services.systemd-timesyncd.environment.SYSTEMD_LOG_LEVEL = "debug";
    };

  testScript = ''
    machine.wait_for_unit("tinydns.service")
    machine.wait_for_unit("systemd-timesyncd.service")
    machine.fail("resolvectl query ${ntpHostname}")
    machine.wait_until_succeeds("journalctl -u systemd-timesyncd.service --grep='Resolved address ${ntpIP}:123 for ${ntpHostname}'")
  '';
})
