1# Avahi daemon.
2{ config, lib, utils, pkgs, ... }:
3
4with lib;
5
6let
7
8 cfg = config.services.avahi;
9
10 yesNo = yes : if yes then "yes" else "no";
11
12 avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" ''
13 [server]
14 ${# Users can set `networking.hostName' to the empty string, when getting
15 # a host name from DHCP. In that case, let Avahi take whatever the
16 # current host name is; setting `host-name' to the empty string in
17 # `avahi-daemon.conf' would be invalid.
18 optionalString (hostName != "") "host-name=${hostName}"}
19 browse-domains=${concatStringsSep ", " browseDomains}
20 use-ipv4=${yesNo ipv4}
21 use-ipv6=${yesNo ipv6}
22 ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"}
23 ${optionalString (domainName!=null) "domain-name=${domainName}"}
24 allow-point-to-point=${yesNo allowPointToPoint}
25 ${optionalString (cacheEntriesMax!=null) "cache-entries-max=${toString cacheEntriesMax}"}
26
27 [wide-area]
28 enable-wide-area=${yesNo wideArea}
29
30 [publish]
31 disable-publishing=${yesNo (!publish.enable)}
32 disable-user-service-publishing=${yesNo (!publish.userServices)}
33 publish-addresses=${yesNo (publish.userServices || publish.addresses)}
34 publish-hinfo=${yesNo publish.hinfo}
35 publish-workstation=${yesNo publish.workstation}
36 publish-domain=${yesNo publish.domain}
37
38 [reflector]
39 enable-reflector=${yesNo reflector}
40 '';
41
42in
43
44{
45
46 ###### interface
47
48 options = {
49
50 services.avahi = {
51
52 enable = mkOption {
53 default = false;
54 description = ''
55 Whether to run the Avahi daemon, which allows Avahi clients
56 to use Avahi's service discovery facilities and also allows
57 the local machine to advertise its presence and services
58 (through the mDNS responder implemented by `avahi-daemon').
59 '';
60 };
61
62 hostName = mkOption {
63 type = types.str;
64 description = ''
65 Host name advertised on the LAN. If not set, avahi will use the value
66 of config.networking.hostName.
67 '';
68 };
69
70 domainName = mkOption {
71 type = types.str;
72 default = "local";
73 description = ''
74 Domain name for all advertisements.
75 '';
76 };
77
78 browseDomains = mkOption {
79 default = [ ];
80 example = [ "0pointer.de" "zeroconf.org" ];
81 description = ''
82 List of non-local DNS domains to be browsed.
83 '';
84 };
85
86 ipv4 = mkOption {
87 default = true;
88 description = ''Whether to use IPv4'';
89 };
90
91 ipv6 = mkOption {
92 default = false;
93 description = ''Whether to use IPv6'';
94 };
95
96 interfaces = mkOption {
97 type = types.nullOr (types.listOf types.str);
98 default = null;
99 description = ''
100 List of network interfaces that should be used by the <command>avahi-daemon</command>.
101 Other interfaces will be ignored. If <literal>null</literal> all local interfaces
102 except loopback and point-to-point will be used.
103 '';
104 };
105
106 allowPointToPoint = mkOption {
107 default = false;
108 description= ''
109 Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large
110 latencies with such links and opens a potential security hole by allowing mDNS access from Internet
111 connections. Use with care and YMMV!
112 '';
113 };
114
115 wideArea = mkOption {
116 default = true;
117 description = ''Whether to enable wide-area service discovery.'';
118 };
119
120 reflector = mkOption {
121 default = false;
122 description = ''Reflect incoming mDNS requests to all allowed network interfaces.'';
123 };
124
125 publish = {
126 enable = mkOption {
127 default = false;
128 description = ''Whether to allow publishing in general.'';
129 };
130
131 userServices = mkOption {
132 default = false;
133 description = ''Whether to publish user services. Will set <literal>addresses=true</literal>.'';
134 };
135
136 addresses = mkOption {
137 default = false;
138 description = ''Whether to register mDNS address records for all local IP addresses.'';
139 };
140
141 hinfo = mkOption {
142 default = false;
143 description = ''
144 Whether to register an mDNS HINFO record which contains information about the
145 local operating system and CPU.
146 '';
147 };
148
149 workstation = mkOption {
150 default = false;
151 description = ''Whether to register a service of type "_workstation._tcp" on the local LAN.'';
152 };
153
154 domain = mkOption {
155 default = false;
156 description = ''Whether to announce the locally used domain name for browsing by other hosts.'';
157 };
158
159 };
160
161 nssmdns = mkOption {
162 default = false;
163 description = ''
164 Whether to enable the mDNS NSS (Name Service Switch) plug-in.
165 Enabling it allows applications to resolve names in the `.local'
166 domain by transparently querying the Avahi daemon.
167 '';
168 };
169
170 cacheEntriesMax = mkOption {
171 default = null;
172 type = types.nullOr types.int;
173 description = ''
174 Number of resource records to be cached per interface. Use 0 to
175 disable caching. Avahi daemon defaults to 4096 if not set.
176 '';
177 };
178
179 };
180
181 };
182
183
184 ###### implementation
185
186 config = mkIf cfg.enable {
187
188 services.avahi.hostName = mkDefault config.networking.hostName;
189
190 users.extraUsers = singleton
191 { name = "avahi";
192 uid = config.ids.uids.avahi;
193 description = "`avahi-daemon' privilege separation user";
194 home = "/var/empty";
195 };
196
197 users.extraGroups = singleton
198 { name = "avahi";
199 gid = config.ids.gids.avahi;
200 };
201
202 system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
203
204 environment.systemPackages = [ pkgs.avahi ];
205
206 systemd.sockets.avahi-daemon =
207 { description = "Avahi mDNS/DNS-SD Stack Activation Socket";
208 listenStreams = [ "/var/run/avahi-daemon/socket" ];
209 wantedBy = [ "sockets.target" ];
210 };
211
212 systemd.services.avahi-daemon =
213 { description = "Avahi mDNS/DNS-SD Stack";
214 wantedBy = [ "multi-user.target" ];
215 requires = [ "avahi-daemon.socket" ];
216
217 serviceConfig."NotifyAccess" = "main";
218 serviceConfig."BusName" = "org.freedesktop.Avahi";
219 serviceConfig."Type" = "dbus";
220
221 path = [ pkgs.coreutils pkgs.avahi ];
222
223 preStart = "mkdir -p /var/run/avahi-daemon";
224
225 script =
226 ''
227 # Make NSS modules visible so that `avahi_nss_support ()' can
228 # return a sensible value.
229 export LD_LIBRARY_PATH="${config.system.nssModules.path}"
230
231 exec ${pkgs.avahi}/sbin/avahi-daemon --syslog -f "${avahiDaemonConf}"
232 '';
233 };
234
235 services.dbus.enable = true;
236 services.dbus.packages = [ pkgs.avahi ];
237
238 # Enabling Avahi without exposing it in the firewall doesn't make
239 # sense.
240 networking.firewall.allowedUDPPorts = [ 5353 ];
241
242 };
243
244}