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