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