at 25.11-pre 8.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9let 10 cfg = config.services.resolved; 11 12 dnsmasqResolve = config.services.dnsmasq.enable && config.services.dnsmasq.resolveLocalQueries; 13 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 ''; 26 27in 28{ 29 30 options = { 31 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`. 37 38 Search for `services.resolved` to see all options. 39 ''; 40 }; 41 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 }; 55 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). 66 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 }; 73 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. 85 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 }; 92 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. 117 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 }; 125 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 }; 152 153 services.resolved.extraConfig = mkOption { 154 default = ""; 155 type = types.lines; 156 description = '' 157 Extra config to append to resolved.conf. 158 ''; 159 }; 160 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 }; 169 170 }; 171 172 config = mkMerge [ 173 (mkIf cfg.enable { 174 175 assertions = [ 176 { 177 assertion = !config.networking.useHostResolvConf; 178 message = "Using host resolv.conf is not supported with systemd-resolved"; 179 } 180 ]; 181 182 users.users.systemd-resolve.group = "systemd-resolve"; 183 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]" ]); 188 189 systemd.additionalUpstreamSystemUnits = [ 190 "systemd-resolved.service" 191 ]; 192 193 systemd.services.systemd-resolved = { 194 wantedBy = [ "sysinit.target" ]; 195 aliases = [ "dbus-org.freedesktop.resolve1.service" ]; 196 reloadTriggers = [ config.environment.etc."systemd/resolved.conf".source ]; 197 stopIfChanged = false; 198 }; 199 200 environment.etc = 201 { 202 "systemd/resolved.conf".text = resolvedConf; 203 204 # symlink the dynamic stub resolver of resolv.conf as recommended by upstream: 205 # https://www.freedesktop.org/software/systemd/man/systemd-resolved.html#/etc/resolv.conf 206 "resolv.conf".source = "/run/systemd/resolve/stub-resolv.conf"; 207 } 208 // optionalAttrs dnsmasqResolve { 209 "dnsmasq-resolv.conf".source = "/run/systemd/resolve/resolv.conf"; 210 }; 211 212 # If networkmanager is enabled, ask it to interface with resolved. 213 networking.networkmanager.dns = "systemd-resolved"; 214 215 networking.resolvconf.package = pkgs.systemd; 216 217 }) 218 219 (mkIf config.boot.initrd.services.resolved.enable { 220 221 assertions = [ 222 { 223 assertion = config.boot.initrd.systemd.enable; 224 message = "'boot.initrd.services.resolved.enable' can only be enabled with systemd stage 1."; 225 } 226 ]; 227 228 boot.initrd.systemd = { 229 contents = { 230 "/etc/systemd/resolved.conf".text = resolvedConf; 231 }; 232 233 tmpfiles.settings.systemd-resolved-stub."/etc/resolv.conf".L.argument = 234 "/run/systemd/resolve/stub-resolv.conf"; 235 236 additionalUpstreamUnits = [ "systemd-resolved.service" ]; 237 users.systemd-resolve = { }; 238 groups.systemd-resolve = { }; 239 storePaths = [ "${config.boot.initrd.systemd.package}/lib/systemd/systemd-resolved" ]; 240 services.systemd-resolved = { 241 wantedBy = [ "sysinit.target" ]; 242 aliases = [ "dbus-org.freedesktop.resolve1.service" ]; 243 }; 244 }; 245 246 }) 247 ]; 248 249}