1{ config, lib, pkgs, ...}:
2
3with lib;
4
5let
6 cfg = config.services.mosquitto;
7
8 listenerConf = optionalString cfg.ssl.enable ''
9 listener ${toString cfg.ssl.port} ${cfg.ssl.host}
10 cafile ${cfg.ssl.cafile}
11 certfile ${cfg.ssl.certfile}
12 keyfile ${cfg.ssl.keyfile}
13 '';
14
15 mosquittoConf = pkgs.writeText "mosquitto.conf" ''
16 pid_file /run/mosquitto/pid
17 acl_file ${aclFile}
18 persistence true
19 allow_anonymous ${if cfg.allowAnonymous then "true" else "false"}
20 bind_address ${cfg.host}
21 port ${toString cfg.port}
22 ${listenerConf}
23 ${cfg.extraConf}
24 '';
25
26 userAcl = (concatStringsSep "\n\n" (mapAttrsToList (n: c:
27 "user ${n}\n" + (concatStringsSep "\n" c.acl)) cfg.users
28 ));
29
30 aclFile = pkgs.writeText "mosquitto.acl" ''
31 ${cfg.aclExtraConf}
32 ${userAcl}
33 '';
34
35in
36
37{
38
39 ###### Interface
40
41 options = {
42 services.mosquitto = {
43 enable = mkEnableOption "Enable the MQTT Mosquitto broker.";
44
45 host = mkOption {
46 default = "127.0.0.1";
47 example = "0.0.0.0";
48 type = types.string;
49 description = ''
50 Host to listen on without SSL.
51 '';
52 };
53
54 port = mkOption {
55 default = 1883;
56 example = 1883;
57 type = types.int;
58 description = ''
59 Port on which to listen without SSL.
60 '';
61 };
62
63 ssl = {
64 enable = mkEnableOption "Enable SSL listener.";
65
66 cafile = mkOption {
67 type = types.nullOr types.path;
68 default = null;
69 description = "Path to PEM encoded CA certificates.";
70 };
71
72 certfile = mkOption {
73 type = types.nullOr types.path;
74 default = null;
75 description = "Path to PEM encoded server certificate.";
76 };
77
78 keyfile = mkOption {
79 type = types.nullOr types.path;
80 default = null;
81 description = "Path to PEM encoded server key.";
82 };
83
84 host = mkOption {
85 default = "0.0.0.0";
86 example = "localhost";
87 type = types.string;
88 description = ''
89 Host to listen on with SSL.
90 '';
91 };
92
93 port = mkOption {
94 default = 8883;
95 example = 8883;
96 type = types.int;
97 description = ''
98 Port on which to listen with SSL.
99 '';
100 };
101 };
102
103 dataDir = mkOption {
104 default = "/var/lib/mosquitto";
105 type = types.path;
106 description = ''
107 The data directory.
108 '';
109 };
110
111 users = mkOption {
112 type = types.attrsOf (types.submodule {
113 options = {
114 password = mkOption {
115 type = with types; uniq (nullOr str);
116 default = null;
117 description = ''
118 Specifies the (clear text) password for the MQTT User.
119 '';
120 };
121
122 hashedPassword = mkOption {
123 type = with types; uniq (nullOr str);
124 default = null;
125 description = ''
126 Specifies the hashed password for the MQTT User.
127 <option>hashedPassword</option> overrides <option>password</option>.
128 To generate hashed password install <literal>mkpasswd</literal>
129 package and run <literal>mkpasswd -m sha-512</literal>.
130 '';
131 };
132
133 acl = mkOption {
134 type = types.listOf types.string;
135 example = [ "topic read A/B" "topic A/#" ];
136 description = ''
137 Control client access to topics on the broker.
138 '';
139 };
140 };
141 });
142 example = { john = { password = "123456"; acl = [ "topic readwrite john/#" ]; }; };
143 description = ''
144 A set of users and their passwords and ACLs.
145 '';
146 };
147
148 allowAnonymous = mkOption {
149 default = false;
150 example = true;
151 type = types.bool;
152 description = ''
153 Allow clients to connect without authentication.
154 '';
155 };
156
157 extraConf = mkOption {
158 default = "";
159 type = types.lines;
160 description = ''
161 Extra config to append to `mosquitto.conf` file.
162 '';
163 };
164
165 aclExtraConf = mkOption {
166 default = "";
167 type = types.lines;
168 description = ''
169 Extra config to prepend to the ACL file.
170 '';
171 };
172
173 };
174 };
175
176
177 ###### Implementation
178
179 config = mkIf cfg.enable {
180
181 systemd.services.mosquitto = {
182 description = "Mosquitto MQTT Broker Daemon";
183 wantedBy = [ "multi-user.target" ];
184 after = [ "network.target" ];
185 serviceConfig = {
186 Type = "forking";
187 User = "mosquitto";
188 Group = "mosquitto";
189 RuntimeDirectory = "mosquitto";
190 WorkingDirectory = cfg.dataDir;
191 Restart = "on-failure";
192 ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf} -d";
193 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
194 PIDFile = "/run/mosquitto/pid";
195 };
196 preStart = ''
197 rm -f ${cfg.dataDir}/passwd
198 touch ${cfg.dataDir}/passwd
199 '' + concatStringsSep "\n" (
200 mapAttrsToList (n: c:
201 if c.hashedPassword != null then
202 "echo '${n}:${c.hashedPassword}' > ${cfg.dataDir}/passwd"
203 else optionalString (c.password != null)
204 "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} ${c.password}"
205 ) cfg.users);
206 };
207
208 users.extraUsers.mosquitto = {
209 description = "Mosquitto MQTT Broker Daemon owner";
210 group = "mosquitto";
211 uid = config.ids.uids.mosquitto;
212 home = cfg.dataDir;
213 createHome = true;
214 };
215
216 users.extraGroups.mosquitto.gid = config.ids.gids.mosquitto;
217
218 };
219}