Merge #361424: refactor lib.packagesFromDirectoryRecursive (v2)
[NixPkgs.git] / nixos / modules / system / boot / resolved.nix
blob987480521d1c8b6a855a9349dd124370d2669f26
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 with lib;
9 let
10   cfg = config.services.resolved;
12   dnsmasqResolve = config.services.dnsmasq.enable && config.services.dnsmasq.resolveLocalQueries;
14   resolvedConf = ''
15     [Resolve]
16     ${optionalString (
17       config.networking.nameservers != [ ]
18     ) "DNS=${concatStringsSep " " config.networking.nameservers}"}
19     ${optionalString (cfg.fallbackDns != null) "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
20     ${optionalString (cfg.domains != [ ]) "Domains=${concatStringsSep " " cfg.domains}"}
21     LLMNR=${cfg.llmnr}
22     DNSSEC=${cfg.dnssec}
23     DNSOverTLS=${cfg.dnsovertls}
24     ${config.services.resolved.extraConfig}
25   '';
30   options = {
32     services.resolved.enable = mkOption {
33       default = false;
34       type = types.bool;
35       description = ''
36         Whether to enable the systemd DNS resolver daemon, `systemd-resolved`.
38         Search for `services.resolved` to see all options.
39       '';
40     };
42     services.resolved.fallbackDns = mkOption {
43       default = null;
44       example = [
45         "8.8.8.8"
46         "2001:4860:4860::8844"
47       ];
48       type = types.nullOr (types.listOf types.str);
49       description = ''
50         A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
51         If this option is null, a compiled-in list of DNS servers is used instead.
52         Setting this option to an empty list will override the built-in list to an empty list, disabling fallback.
53       '';
54     };
56     services.resolved.domains = mkOption {
57       default = config.networking.search;
58       defaultText = literalExpression "config.networking.search";
59       example = [ "example.com" ];
60       type = types.listOf types.str;
61       description = ''
62         A list of domains. These domains are used as search suffixes
63         when resolving single-label host names (domain names which
64         contain no dot), in order to qualify them into fully-qualified
65         domain names (FQDNs).
67         For compatibility reasons, if this setting is not specified,
68         the search domains listed in
69         {file}`/etc/resolv.conf` are used instead, if
70         that file exists and any domains are configured in it.
71       '';
72     };
74     services.resolved.llmnr = mkOption {
75       default = "true";
76       example = "false";
77       type = types.enum [
78         "true"
79         "resolve"
80         "false"
81       ];
82       description = ''
83         Controls Link-Local Multicast Name Resolution support
84         (RFC 4795) on the local host.
86         If set to
87         - `"true"`: Enables full LLMNR responder and resolver support.
88         - `"false"`: Disables both.
89         - `"resolve"`: Only resolution support is enabled, but responding is disabled.
90       '';
91     };
93     services.resolved.dnssec = mkOption {
94       default = "false";
95       example = "true";
96       type = types.enum [
97         "true"
98         "allow-downgrade"
99         "false"
100       ];
101       description = ''
102         If set to
103         - `"true"`:
104             all DNS lookups are DNSSEC-validated locally (excluding
105             LLMNR and Multicast DNS). Note that this mode requires a
106             DNS server that supports DNSSEC. If the DNS server does
107             not properly support DNSSEC all validations will fail.
108         - `"allow-downgrade"`:
109             DNSSEC validation is attempted, but if the server does not
110             support DNSSEC properly, DNSSEC mode is automatically
111             disabled. Note that this mode makes DNSSEC validation
112             vulnerable to "downgrade" attacks, where an attacker might
113             be able to trigger a downgrade to non-DNSSEC mode by
114             synthesizing a DNS response that suggests DNSSEC was not
115             supported.
116         - `"false"`: DNS lookups are not DNSSEC validated.
118         At the time of September 2023, systemd upstream advise
119         to disable DNSSEC by default as the current code
120         is not robust enough to deal with "in the wild" non-compliant
121         servers, which will usually give you a broken bad experience
122         in addition of insecure.
123       '';
124     };
126     services.resolved.dnsovertls = mkOption {
127       default = "false";
128       example = "true";
129       type = types.enum [
130         "true"
131         "opportunistic"
132         "false"
133       ];
134       description = ''
135         If set to
136         - `"true"`:
137             all DNS lookups will be encrypted. This requires
138             that the DNS server supports DNS-over-TLS and
139             has a valid certificate. If the hostname was specified
140             via the `address#hostname` format in {option}`services.resolved.domains`
141             then the specified hostname is used to validate its certificate.
142         - `"opportunistic"`:
143             all DNS lookups will attempt to be encrypted, but will fallback
144             to unecrypted requests if the server does not support DNS-over-TLS.
145             Note that this mode does allow for a malicious party to conduct a
146             downgrade attack by immitating the DNS server and pretending to not
147             support encryption.
148         - `"false"`:
149             all DNS lookups are done unencrypted.
150       '';
151     };
153     services.resolved.extraConfig = mkOption {
154       default = "";
155       type = types.lines;
156       description = ''
157         Extra config to append to resolved.conf.
158       '';
159     };
161     boot.initrd.services.resolved.enable = mkOption {
162       default = config.boot.initrd.systemd.network.enable;
163       defaultText = "config.boot.initrd.systemd.network.enable";
164       description = ''
165         Whether to enable resolved for stage 1 networking.
166         Uses the toplevel 'services.resolved' options for 'resolved.conf'
167       '';
168     };
170   };
172   config = mkMerge [
173     (mkIf cfg.enable {
175       assertions = [
176         {
177           assertion = !config.networking.useHostResolvConf;
178           message = "Using host resolv.conf is not supported with systemd-resolved";
179         }
180       ];
182       users.users.systemd-resolve.group = "systemd-resolve";
184       # add resolve to nss hosts database if enabled and nscd enabled
185       # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
186       # added with order 501 to allow modules to go before with mkBefore
187       system.nssDatabases.hosts = (mkOrder 501 [ "resolve [!UNAVAIL=return]" ]);
189       systemd.additionalUpstreamSystemUnits = [
190         "systemd-resolved.service"
191       ];
193       systemd.services.systemd-resolved = {
194         wantedBy = [ "sysinit.target" ];
195         aliases = [ "dbus-org.freedesktop.resolve1.service" ];
196         restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
197       };
199       environment.etc =
200         {
201           "systemd/resolved.conf".text = resolvedConf;
203           # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
204           # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
205           "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
206         }
207         // optionalAttrs dnsmasqResolve {
208           "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
209         };
211       # If networkmanager is enabled, ask it to interface with resolved.
212       networking.networkmanager.dns = "systemd-resolved";
214       networking.resolvconf.package = pkgs.systemd;
216     })
218     (mkIf config.boot.initrd.services.resolved.enable {
220       assertions = [
221         {
222           assertion = config.boot.initrd.systemd.enable;
223           message = "'boot.initrd.services.resolved.enable' can only be enabled with systemd stage 1.";
224         }
225       ];
227       boot.initrd.systemd = {
228         contents = {
229           "/etc/systemd/resolved.conf".text = resolvedConf;
230         };
232         tmpfiles.settings.systemd-resolved-stub."/etc/resolv.conf".L.argument =
233           "/run/systemd/resolve/stub-resolv.conf";
235         additionalUpstreamUnits = [ "systemd-resolved.service" ];
236         users.systemd-resolve = { };
237         groups.systemd-resolve = { };
238         storePaths = [ "${config.boot.initrd.systemd.package}/lib/systemd/systemd-resolved" ];
239         services.systemd-resolved = {
240           wantedBy = [ "sysinit.target" ];
241           aliases = [ "dbus-org.freedesktop.resolve1.service" ];
242         };
243       };
245     })
246   ];