at 24.11-pre 10 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.avahi; 7 8 yesNo = yes: if yes then "yes" else "no"; 9 10 avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" '' 11 [server] 12 ${# Users can set `networking.hostName' to the empty string, when getting 13 # a host name from DHCP. In that case, let Avahi take whatever the 14 # current host name is; setting `host-name' to the empty string in 15 # `avahi-daemon.conf' would be invalid. 16 optionalString (hostName != "") "host-name=${hostName}"} 17 browse-domains=${concatStringsSep ", " browseDomains} 18 use-ipv4=${yesNo ipv4} 19 use-ipv6=${yesNo ipv6} 20 ${optionalString (allowInterfaces!=null) "allow-interfaces=${concatStringsSep "," allowInterfaces}"} 21 ${optionalString (denyInterfaces!=null) "deny-interfaces=${concatStringsSep "," denyInterfaces}"} 22 ${optionalString (domainName!=null) "domain-name=${domainName}"} 23 allow-point-to-point=${yesNo allowPointToPoint} 24 ${optionalString (cacheEntriesMax!=null) "cache-entries-max=${toString cacheEntriesMax}"} 25 26 [wide-area] 27 enable-wide-area=${yesNo wideArea} 28 29 [publish] 30 disable-publishing=${yesNo (!publish.enable)} 31 disable-user-service-publishing=${yesNo (!publish.userServices)} 32 publish-addresses=${yesNo (publish.userServices || publish.addresses)} 33 publish-hinfo=${yesNo publish.hinfo} 34 publish-workstation=${yesNo publish.workstation} 35 publish-domain=${yesNo publish.domain} 36 37 [reflector] 38 enable-reflector=${yesNo reflector} 39 ${extraConfig} 40 ''; 41in 42{ 43 imports = [ 44 (lib.mkRenamedOptionModule [ "services" "avahi" "interfaces" ] [ "services" "avahi" "allowInterfaces" ]) 45 (lib.mkRenamedOptionModule [ "services" "avahi" "nssmdns" ] [ "services" "avahi" "nssmdns4" ]) 46 ]; 47 48 options.services.avahi = { 49 enable = mkOption { 50 type = types.bool; 51 default = false; 52 description = '' 53 Whether to run the Avahi daemon, which allows Avahi clients 54 to use Avahi's service discovery facilities and also allows 55 the local machine to advertise its presence and services 56 (through the mDNS responder implemented by `avahi-daemon`). 57 ''; 58 }; 59 60 package = mkPackageOption pkgs "avahi" { }; 61 62 hostName = mkOption { 63 type = types.str; 64 default = config.networking.hostName; 65 defaultText = literalExpression "config.networking.hostName"; 66 description = '' 67 Host name advertised on the LAN. If not set, avahi will use the value 68 of {option}`config.networking.hostName`. 69 ''; 70 }; 71 72 domainName = mkOption { 73 type = types.str; 74 default = "local"; 75 description = '' 76 Domain name for all advertisements. 77 ''; 78 }; 79 80 browseDomains = mkOption { 81 type = types.listOf types.str; 82 default = [ ]; 83 example = [ "0pointer.de" "zeroconf.org" ]; 84 description = '' 85 List of non-local DNS domains to be browsed. 86 ''; 87 }; 88 89 ipv4 = mkOption { 90 type = types.bool; 91 default = true; 92 description = "Whether to use IPv4."; 93 }; 94 95 ipv6 = mkOption { 96 type = types.bool; 97 default = false; 98 description = "Whether to use IPv6."; 99 }; 100 101 allowInterfaces = mkOption { 102 type = types.nullOr (types.listOf types.str); 103 default = null; 104 description = '' 105 List of network interfaces that should be used by the {command}`avahi-daemon`. 106 Other interfaces will be ignored. If `null`, all local interfaces 107 except loopback and point-to-point will be used. 108 ''; 109 }; 110 111 denyInterfaces = mkOption { 112 type = types.nullOr (types.listOf types.str); 113 default = null; 114 description = '' 115 List of network interfaces that should be ignored by the 116 {command}`avahi-daemon`. Other unspecified interfaces will be used, 117 unless {option}`allowInterfaces` is set. This option takes precedence 118 over {option}`allowInterfaces`. 119 ''; 120 }; 121 122 openFirewall = mkOption { 123 type = types.bool; 124 default = true; 125 description = '' 126 Whether to open the firewall for UDP port 5353. 127 Disabling this setting also disables discovering of network devices. 128 ''; 129 }; 130 131 allowPointToPoint = mkOption { 132 type = types.bool; 133 default = false; 134 description = '' 135 Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large 136 latencies with such links and opens a potential security hole by allowing mDNS access from Internet 137 connections. 138 ''; 139 }; 140 141 wideArea = mkOption { 142 type = types.bool; 143 default = true; 144 description = "Whether to enable wide-area service discovery."; 145 }; 146 147 reflector = mkOption { 148 type = types.bool; 149 default = false; 150 description = "Reflect incoming mDNS requests to all allowed network interfaces."; 151 }; 152 153 extraServiceFiles = mkOption { 154 type = with types; attrsOf (either str path); 155 default = { }; 156 example = literalExpression '' 157 { 158 ssh = "''${pkgs.avahi}/etc/avahi/services/ssh.service"; 159 smb = ''' 160 <?xml version="1.0" standalone='no'?><!--*-nxml-*--> 161 <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> 162 <service-group> 163 <name replace-wildcards="yes">%h</name> 164 <service> 165 <type>_smb._tcp</type> 166 <port>445</port> 167 </service> 168 </service-group> 169 '''; 170 } 171 ''; 172 description = '' 173 Specify custom service definitions which are placed in the avahi service directory. 174 See the {manpage}`avahi.service(5)` manpage for detailed information. 175 ''; 176 }; 177 178 publish = { 179 enable = mkOption { 180 type = types.bool; 181 default = false; 182 description = "Whether to allow publishing in general."; 183 }; 184 185 userServices = mkOption { 186 type = types.bool; 187 default = false; 188 description = "Whether to publish user services. Will set `addresses=true`."; 189 }; 190 191 addresses = mkOption { 192 type = types.bool; 193 default = false; 194 description = "Whether to register mDNS address records for all local IP addresses."; 195 }; 196 197 hinfo = mkOption { 198 type = types.bool; 199 default = false; 200 description = '' 201 Whether to register a mDNS HINFO record which contains information about the 202 local operating system and CPU. 203 ''; 204 }; 205 206 workstation = mkOption { 207 type = types.bool; 208 default = false; 209 description = '' 210 Whether to register a service of type "_workstation._tcp" on the local LAN. 211 ''; 212 }; 213 214 domain = mkOption { 215 type = types.bool; 216 default = false; 217 description = "Whether to announce the locally used domain name for browsing by other hosts."; 218 }; 219 }; 220 221 nssmdns4 = mkOption { 222 type = types.bool; 223 default = false; 224 description = '' 225 Whether to enable the mDNS NSS (Name Service Switch) plug-in for IPv4. 226 Enabling it allows applications to resolve names in the `.local` 227 domain by transparently querying the Avahi daemon. 228 ''; 229 }; 230 231 nssmdns6 = mkOption { 232 type = types.bool; 233 default = false; 234 description = '' 235 Whether to enable the mDNS NSS (Name Service Switch) plug-in for IPv6. 236 Enabling it allows applications to resolve names in the `.local` 237 domain by transparently querying the Avahi daemon. 238 239 ::: {.note} 240 Due to the fact that most mDNS responders only register local IPv4 addresses, 241 most user want to leave this option disabled to avoid long timeouts when applications first resolve the none existing IPv6 address. 242 ::: 243 ''; 244 }; 245 246 cacheEntriesMax = mkOption { 247 type = types.nullOr types.int; 248 default = null; 249 description = '' 250 Number of resource records to be cached per interface. Use 0 to 251 disable caching. Avahi daemon defaults to 4096 if not set. 252 ''; 253 }; 254 255 extraConfig = mkOption { 256 type = types.lines; 257 default = ""; 258 description = '' 259 Extra config to append to avahi-daemon.conf. 260 ''; 261 }; 262 }; 263 264 config = mkIf cfg.enable { 265 users.users.avahi = { 266 description = "avahi-daemon privilege separation user"; 267 home = "/var/empty"; 268 group = "avahi"; 269 isSystemUser = true; 270 }; 271 272 users.groups.avahi = { }; 273 274 system.nssModules = optional (cfg.nssmdns4 || cfg.nssmdns6) pkgs.nssmdns; 275 system.nssDatabases.hosts = let 276 mdns = if (cfg.nssmdns4 && cfg.nssmdns6) then 277 "mdns" 278 else if (!cfg.nssmdns4 && cfg.nssmdns6) then 279 "mdns6" 280 else if (cfg.nssmdns4 && !cfg.nssmdns6) then 281 "mdns4" 282 else 283 ""; 284 in optionals (cfg.nssmdns4 || cfg.nssmdns6) (mkMerge [ 285 (mkBefore [ "${mdns}_minimal [NOTFOUND=return]" ]) # before resolve 286 (mkAfter [ "${mdns}" ]) # after dns 287 ]); 288 289 environment.systemPackages = [ cfg.package ]; 290 291 environment.etc = (mapAttrs' 292 (n: v: nameValuePair 293 "avahi/services/${n}.service" 294 { ${if types.path.check v then "source" else "text"} = v; } 295 ) 296 cfg.extraServiceFiles); 297 298 systemd.sockets.avahi-daemon = { 299 description = "Avahi mDNS/DNS-SD Stack Activation Socket"; 300 listenStreams = [ "/run/avahi-daemon/socket" ]; 301 wantedBy = [ "sockets.target" ]; 302 }; 303 304 systemd.tmpfiles.rules = [ "d /run/avahi-daemon - avahi avahi -" ]; 305 306 systemd.services.avahi-daemon = { 307 description = "Avahi mDNS/DNS-SD Stack"; 308 wantedBy = [ "multi-user.target" ]; 309 requires = [ "avahi-daemon.socket" ]; 310 311 # Make NSS modules visible so that `avahi_nss_support ()' can 312 # return a sensible value. 313 environment.LD_LIBRARY_PATH = config.system.nssModules.path; 314 315 path = [ pkgs.coreutils cfg.package ]; 316 317 serviceConfig = { 318 NotifyAccess = "main"; 319 BusName = "org.freedesktop.Avahi"; 320 Type = "dbus"; 321 ExecStart = "${cfg.package}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}"; 322 ConfigurationDirectory = "avahi/services"; 323 }; 324 }; 325 326 services.dbus.enable = true; 327 services.dbus.packages = [ cfg.package ]; 328 329 networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 5353 ]; 330 }; 331}