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