1 { config, lib, pkgs, ... }:
5 cfg = config.services.resolved;
7 dnsmasqResolve = config.services.dnsmasq.enable &&
8 config.services.dnsmasq.resolveLocalQueries;
12 ${optionalString (config.networking.nameservers != [])
13 "DNS=${concatStringsSep " " config.networking.nameservers}"}
14 ${optionalString (cfg.fallbackDns != null)
15 "FallbackDNS=${concatStringsSep " " cfg.fallbackDns}"}
16 ${optionalString (cfg.domains != [])
17 "Domains=${concatStringsSep " " cfg.domains}"}
20 DNSOverTLS=${cfg.dnsovertls}
21 ${config.services.resolved.extraConfig}
29 services.resolved.enable = mkOption {
33 Whether to enable the systemd DNS resolver daemon, `systemd-resolved`.
35 Search for `services.resolved` to see all options.
39 services.resolved.fallbackDns = mkOption {
41 example = [ "8.8.8.8" "2001:4860:4860::8844" ];
42 type = types.nullOr (types.listOf types.str);
44 A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
45 If this option is null, a compiled-in list of DNS servers is used instead.
46 Setting this option to an empty list will override the built-in list to an empty list, disabling fallback.
50 services.resolved.domains = mkOption {
51 default = config.networking.search;
52 defaultText = literalExpression "config.networking.search";
53 example = [ "example.com" ];
54 type = types.listOf types.str;
56 A list of domains. These domains are used as search suffixes
57 when resolving single-label host names (domain names which
58 contain no dot), in order to qualify them into fully-qualified
61 For compatibility reasons, if this setting is not specified,
62 the search domains listed in
63 {file}`/etc/resolv.conf` are used instead, if
64 that file exists and any domains are configured in it.
68 services.resolved.llmnr = mkOption {
71 type = types.enum [ "true" "resolve" "false" ];
73 Controls Link-Local Multicast Name Resolution support
74 (RFC 4795) on the local host.
77 - `"true"`: Enables full LLMNR responder and resolver support.
78 - `"false"`: Disables both.
79 - `"resolve"`: Only resolution support is enabled, but responding is disabled.
83 services.resolved.dnssec = mkOption {
86 type = types.enum [ "true" "allow-downgrade" "false" ];
90 all DNS lookups are DNSSEC-validated locally (excluding
91 LLMNR and Multicast DNS). Note that this mode requires a
92 DNS server that supports DNSSEC. If the DNS server does
93 not properly support DNSSEC all validations will fail.
94 - `"allow-downgrade"`:
95 DNSSEC validation is attempted, but if the server does not
96 support DNSSEC properly, DNSSEC mode is automatically
97 disabled. Note that this mode makes DNSSEC validation
98 vulnerable to "downgrade" attacks, where an attacker might
99 be able to trigger a downgrade to non-DNSSEC mode by
100 synthesizing a DNS response that suggests DNSSEC was not
102 - `"false"`: DNS lookups are not DNSSEC validated.
104 At the time of September 2023, systemd upstream advise
105 to disable DNSSEC by default as the current code
106 is not robust enough to deal with "in the wild" non-compliant
107 servers, which will usually give you a broken bad experience
108 in addition of insecure.
112 services.resolved.dnsovertls = mkOption {
115 type = types.enum [ "true" "opportunistic" "false" ];
119 all DNS lookups will be encrypted. This requires
120 that the DNS server supports DNS-over-TLS and
121 has a valid certificate. If the hostname was specified
122 via the `address#hostname` format in {option}`services.resolved.domains`
123 then the specified hostname is used to validate its certificate.
125 all DNS lookups will attempt to be encrypted, but will fallback
126 to unecrypted requests if the server does not support DNS-over-TLS.
127 Note that this mode does allow for a malicious party to conduct a
128 downgrade attack by immitating the DNS server and pretending to not
131 all DNS lookups are done unencrypted.
135 services.resolved.extraConfig = mkOption {
139 Extra config to append to resolved.conf.
143 boot.initrd.services.resolved.enable = mkOption {
144 default = config.boot.initrd.systemd.network.enable;
145 defaultText = "config.boot.initrd.systemd.network.enable";
147 Whether to enable resolved for stage 1 networking.
148 Uses the toplevel 'services.resolved' options for 'resolved.conf'
158 { assertion = !config.networking.useHostResolvConf;
159 message = "Using host resolv.conf is not supported with systemd-resolved";
163 users.users.systemd-resolve.group = "systemd-resolve";
165 # add resolve to nss hosts database if enabled and nscd enabled
166 # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
167 # added with order 501 to allow modules to go before with mkBefore
168 system.nssDatabases.hosts = (mkOrder 501 ["resolve [!UNAVAIL=return]"]);
170 systemd.additionalUpstreamSystemUnits = [
171 "systemd-resolved.service"
174 systemd.services.systemd-resolved = {
175 wantedBy = [ "sysinit.target" ];
176 aliases = [ "dbus-org.freedesktop.resolve1.service" ];
177 restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ];
181 "systemd/resolved.conf".text = resolvedConf;
183 # symlink the dynamic stub resolver of resolv.conf as recommended by upstream:
184 # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf
185 "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf";
186 } // optionalAttrs dnsmasqResolve {
187 "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf";
190 # If networkmanager is enabled, ask it to interface with resolved.
191 networking.networkmanager.dns = "systemd-resolved";
193 networking.resolvconf.package = pkgs.systemd;
197 (mkIf config.boot.initrd.services.resolved.enable {
201 assertion = config.boot.initrd.systemd.enable;
202 message = "'boot.initrd.services.resolved.enable' can only be enabled with systemd stage 1.";
206 boot.initrd.systemd = {
208 "/etc/systemd/resolved.conf".text = resolvedConf;
211 tmpfiles.settings.systemd-resolved-stub."/etc/resolv.conf".L.argument =
212 "/run/systemd/resolve/stub-resolv.conf";
214 additionalUpstreamUnits = ["systemd-resolved.service"];
215 users.systemd-resolve = {};
216 groups.systemd-resolve = {};
217 storePaths = ["${config.boot.initrd.systemd.package}/lib/systemd/systemd-resolved"];
218 services.systemd-resolved = {
219 wantedBy = ["sysinit.target"];
220 aliases = [ "dbus-org.freedesktop.resolve1.service" ];