1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.resolved;
6
7 dnsmasqResolve = config.services.dnsmasq.enable &&
8 config.services.dnsmasq.resolveLocalQueries;
9
10in
11{
12
13 options = {
14
15 services.resolved.enable = mkOption {
16 default = false;
17 type = types.bool;
18 description = lib.mdDoc ''
19 Whether to enable the systemd DNS resolver daemon, `systemd-resolved`.
20
21 Search for `services.resolved` to see all options.
22 '';
23 };
24
25 services.resolved.fallbackDns = mkOption {
26 default = [ ];
27 example = [ "8.8.8.8" "2001:4860:4860::8844" ];
28 type = types.listOf types.str;
29 description = lib.mdDoc ''
30 A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
31 If this option is empty, a compiled-in list of DNS servers is used instead.
32 '';
33 };
34
35 services.resolved.domains = mkOption {
36 default = config.networking.search;
37 defaultText = literalExpression "config.networking.search";
38 example = [ "example.com" ];
39 type = types.listOf types.str;
40 description = lib.mdDoc ''
41 A list of domains. These domains are used as search suffixes
42 when resolving single-label host names (domain names which
43 contain no dot), in order to qualify them into fully-qualified
44 domain names (FQDNs).
45
46 For compatibility reasons, if this setting is not specified,
47 the search domains listed in
48 {file}`/etc/resolv.conf` are used instead, if
49 that file exists and any domains are configured in it.
50 '';
51 };
52
53 services.resolved.llmnr = mkOption {
54 default = "true";
55 example = "false";
56 type = types.enum [ "true" "resolve" "false" ];
57 description = lib.mdDoc ''
58 Controls Link-Local Multicast Name Resolution support
59 (RFC 4795) on the local host.
60
61 If set to
62 - `"true"`: Enables full LLMNR responder and resolver support.
63 - `"false"`: Disables both.
64 - `"resolve"`: Only resolution support is enabled, but responding is disabled.
65 '';
66 };
67
68 services.resolved.dnssec = mkOption {
69 default = "allow-downgrade";
70 example = "true";
71 type = types.enum [ "true" "allow-downgrade" "false" ];
72 description = lib.mdDoc ''
73 If set to
74 - `"true"`:
75 all DNS lookups are DNSSEC-validated locally (excluding
76 LLMNR and Multicast DNS). Note that this mode requires a
77 DNS server that supports DNSSEC. If the DNS server does
78 not properly support DNSSEC all validations will fail.
79 - `"allow-downgrade"`:
80 DNSSEC validation is attempted, but if the server does not
81 support DNSSEC properly, DNSSEC mode is automatically
82 disabled. Note that this mode makes DNSSEC validation
83 vulnerable to "downgrade" attacks, where an attacker might
84 be able to trigger a downgrade to non-DNSSEC mode by
85 synthesizing a DNS response that suggests DNSSEC was not
86 supported.
87 - `"false"`: DNS lookups are not DNSSEC validated.
88 '';
89 };
90
91 services.resolved.extraConfig = mkOption {
92 default = "";
93 type = types.lines;
94 description = lib.mdDoc ''
95 Extra config to append to resolved.conf.
96 '';
97 };
98
99 };
100
101 config = mkIf cfg.enable {
102
103 assertions = [
104 { assertion = !config.networking.useHostResolvConf;
105 message = "Using host resolv.conf is not supported with systemd-resolved";
106 }
107 ];
108
109 users.users.systemd-resolve.group = "systemd-resolve";
110
111 # add resolve to nss hosts database if enabled and nscd enabled
112 # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
113 # added with order 501 to allow modules to go before with mkBefore
114 system.nssDatabases.hosts = (mkOrder 501 ["resolve [!UNAVAIL=return]"]);
115
116 systemd.additionalUpstreamSystemUnits = [
117 "systemd-resolved.service"
118 ];
119
120 systemd.services.systemd-resolved = {
121 wantedBy = [ "multi-user.target" ];
122 aliases = [ "dbus-org.freedesktop.resolve1.service" ];
123 restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
124 };
125
126 environment.etc = {
127 "systemd/resolved.conf".text = ''
128 [Resolve]
129 ${optionalString (config.networking.nameservers != [])
130 "DNS=${concatStringsSep " " config.networking.nameservers}"}
131 ${optionalString (cfg.fallbackDns != [])
132 "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
133 ${optionalString (cfg.domains != [])
134 "Domains=${concatStringsSep " " cfg.domains}"}
135 LLMNR=${cfg.llmnr}
136 DNSSEC=${cfg.dnssec}
137 ${config.services.resolved.extraConfig}
138 '';
139
140 # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
141 # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
142 "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
143 } // optionalAttrs dnsmasqResolve {
144 "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
145 };
146
147 # If networkmanager is enabled, ask it to interface with resolved.
148 networking.networkmanager.dns = "systemd-resolved";
149
150 networking.resolvconf.package = pkgs.systemd;
151
152 };
153
154}