Unverified Commit dfe989d3 authored by Florian Klink's avatar Florian Klink Committed by GitHub
Browse files

nixos/systemd-resolved: add support to generate dns-delegate files (#489824)

parents 70358338 cfc8eb2d
Loading
Loading
Loading
Loading
+37 −3
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  utils,
  ...
}:
@@ -14,12 +13,15 @@ let
    elem
    isList
    literalExpression
    mapAttrs'
    mapAttrsToList
    mkIf
    mkMerge
    mkOption
    mkOrder
    mkRenamedOptionModule
    mkRemovedOptionModule
    nameValuePair
    optionalAttrs
    types
    ;
@@ -132,6 +134,27 @@ in
        };
      };

      dnsDelegates = mkOption {
        description = ''
          dns-delegate files to be created.
          See {manpage}`systemd.dns-delegate(5)` for more info.
        '';
        default = { };
        type = types.attrsOf (
          types.submodule {
            options.Delegate = mkOption {
              description = ''
                Settings option for systemd dns-delegate files.
                See {manpage}`systemd.dns-delegate(5)` for all available options.
              '';
              type = types.submodule {
                freeformType = types.attrsOf unitOption;
              };
            };
          }
        );
      };

    };

    boot.initrd.services.resolved.enable = mkOption {
@@ -167,7 +190,12 @@ in
      systemd.services.systemd-resolved = {
        wantedBy = [ "sysinit.target" ];
        aliases = [ "dbus-org.freedesktop.resolve1.service" ];
        reloadTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
        reloadTriggers = [
          config.environment.etc."systemd/resolved.conf".source
        ]
        ++ mapAttrsToList (
          name: _: config.environment.etc."systemd/dns-delegate.d/${name}.dns-delegate".source
        ) cfg.dnsDelegates;
        stopIfChanged = false;
      };

@@ -180,7 +208,13 @@ in
      }
      // optionalAttrs dnsmasqResolve {
        "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
      };
      }
      // mapAttrs' (
        name: value:
        nameValuePair "systemd/dns-delegate.d/${name}.dns-delegate" {
          text = settingsToSections (transformSettings value);
        }
      ) cfg.dnsDelegates;

      # If networkmanager is enabled, ask it to interface with resolved.
      networking.networkmanager.dns = "systemd-resolved";
+65 −12
Original line number Diff line number Diff line
@@ -36,13 +36,48 @@
      };
    };

  nodes.delegate_server =
    { lib, config, ... }:
    let
      delegateZone = pkgs.writeTextDir "delegated.example.org.zone" ''
        @ SOA ns.delegated.example.org. noc.delegated.example.org. 2019031301 86400 7200 3600000 172800
        test A ${(lib.head config.networking.interfaces.eth1.ipv4.addresses).address}
        test AAAA ${(lib.head config.networking.interfaces.eth1.ipv6.addresses).address}
      '';
    in
    {
      networking.firewall.enable = false;
      networking.useDHCP = false;

      networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
        {
          address = "fd00::3";
          prefixLength = 64;
        }
      ];

      services.knot = {
        enable = true;
        settings = {
          server.listen = [
            "0.0.0.0@53"
            "::@53"
          ];
          template.default.storage = delegateZone;
          zone."delegated.example.org".file = "delegated.example.org.zone";
        };
      };
    };

  nodes.client =
    { nodes, ... }:
    let
      inherit (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses) address;
      serverAddress = (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address;
      delegateAddress =
        (lib.head nodes.delegate_server.networking.interfaces.eth1.ipv4.addresses).address;
    in
    {
      networking.nameservers = [ address ];
      networking.nameservers = [ serverAddress ];
      networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
        {
          address = "fd00::2";
@@ -51,6 +86,12 @@
      ];
      services.resolved.enable = true;
      services.resolved.settings.Resolve.FallbackDNS = [ ];
      services.resolved.dnsDelegates.example-org = {
        Delegate = {
          DNS = delegateAddress;
          Domains = [ "delegated.example.org" ];
        };
      };
      networking.useNetworkd = true;
      networking.useDHCP = false;
      systemd.network.networks."40-eth0".enable = false;
@@ -69,23 +110,35 @@
    let
      address4 = (lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address;
      address6 = (lib.head nodes.server.networking.interfaces.eth1.ipv6.addresses).address;
      delegateAddress4 =
        (lib.head nodes.delegate_server.networking.interfaces.eth1.ipv4.addresses).address;
      delegateAddress6 =
        (lib.head nodes.delegate_server.networking.interfaces.eth1.ipv6.addresses).address;
    in
    #python
    ''
      start_all()
      server.wait_for_unit("multi-user.target")
      delegate_server.wait_for_unit("multi-user.target")

      def test_client():
          query = client.succeed("resolvectl query example.com")
          assert "${address4}" in query
          assert "${address6}" in query
          client.succeed("ping -4 -c 1 example.com")
          client.succeed("ping -6 -c 1 example.com")
      def test_resolve(domain, expected_addrs):
          query = client.succeed(f"resolvectl query {domain}")
          for addr in expected_addrs:
              assert addr in query, f"Expected {addr} in: {query}"
          client.succeed(f"ping -4 -c 1 {domain}")
          client.succeed(f"ping -6 -c 1 {domain}")

      with subtest("resolve in initrd"):
          client.wait_for_unit("initrd.target")
      test_client()
          test_resolve("example.com", ["${address4}", "${address6}"])

      client.switch_root()

      with subtest("resolve after switch-root"):
          client.wait_for_unit("multi-user.target")
      test_client()
          test_resolve("example.com", ["${address4}", "${address6}"])

      with subtest("dns-delegate resolves delegated subdomain"):
          test_resolve("test.delegated.example.org", ["${delegateAddress4}", "${delegateAddress6}"])
    '';
}