1{ config, lib, pkgs, ...}:
2
3with lib;
4
5let
6 cfg = config.services.znc;
7
8 defaultUser = "znc"; # Default user to own process.
9
10 # Default user and pass:
11 # un=znc
12 # pw=nixospass
13
14 defaultUserName = "znc";
15 defaultPassBlock = "
16 <Pass password>
17 Method = sha256
18 Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
19 Salt = l5Xryew4g*!oa(ECfX2o
20 </Pass>
21 ";
22
23 modules = pkgs.buildEnv {
24 name = "znc-modules";
25 paths = cfg.modulePackages;
26 };
27
28 # Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
29 mkZncConf = confOpts: ''
30 // Also check http://en.znc.in/wiki/Configuration
31
32 AnonIPLimit = 10
33 ConnectDelay = 5
34 # Add `LoadModule = x` for each module...
35 ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules}
36 MaxBufferSize = 500
37 ProtectWebSessions = true
38 SSLCertFile = ${cfg.dataDir}/znc.pem
39 ServerThrottle = 30
40 Skin = dark-clouds
41 StatusPrefix = *
42 Version = 1.2
43
44 <Listener listener0>
45 AllowIRC = true
46 AllowWeb = true
47 IPv4 = true
48 IPv6 = false
49 Port = ${if confOpts.useSSL then "+" else ""}${toString confOpts.port}
50 SSL = ${if confOpts.useSSL then "true" else "false"}
51 </Listener>
52
53 <User ${confOpts.userName}>
54 Admin = true
55 Allow = *
56 AltNick = ${confOpts.nick}_
57 AppendTimestamp = false
58 AutoClearChanBuffer = false
59 Buffer = 150
60 ChanModes = +stn
61 DenyLoadMod = false
62 DenySetBindHost = false
63 Ident = ident
64 JoinTries = 10
65 MaxJoins = 0
66 MaxNetworks = 1
67 MultiClients = true
68 Nick = ${confOpts.nick}
69 PrependTimestamp = true
70 QuitMsg = Quit
71 RealName = ${confOpts.nick}
72 TimestampFormat = [%H:%M:%S]
73 ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.userModules}
74
75 ${confOpts.passBlock}
76 </User>
77 ${confOpts.extraZncConf}
78 '';
79
80 zncConfFile = pkgs.writeTextFile {
81 name = "znc.conf";
82 text = if cfg.zncConf != ""
83 then cfg.zncConf
84 else mkZncConf cfg.confOptions;
85 };
86
87in
88
89{
90
91 ###### Interface
92
93 options = {
94 services.znc = {
95 enable = mkOption {
96 default = false;
97 example = true;
98 type = types.bool;
99 description = ''
100 Enable a ZNC service for a user.
101 '';
102 };
103
104 user = mkOption {
105 default = "znc";
106 example = "john";
107 type = types.string;
108 description = ''
109 The name of an existing user account to use to own the ZNC server process.
110 If not specified, a default user will be created to own the process.
111 '';
112 };
113
114 dataDir = mkOption {
115 default = "/var/lib/znc/";
116 example = "/home/john/.znc/";
117 type = types.path;
118 description = ''
119 The data directory. Used for configuration files and modules.
120 '';
121 };
122
123 zncConf = mkOption {
124 default = "";
125 example = "See: http://wiki.znc.in/Configuration";
126 type = types.lines;
127 description = ''
128 The contents of the `znc.conf` file to use when creating it.
129 If specified, `confOptions` will be ignored, and this value, as-is, will be used.
130 If left empty, a conf file with default values will be used.
131 Recommended to generate with `znc --makeconf` command.
132 '';
133 };
134
135 /* TODO: add to the documentation of the current module:
136
137 Values to use when creating a `znc.conf` file.
138
139 confOptions = {
140 modules = [ "log" ];
141 userName = "john";
142 nick = "johntron";
143 };
144 */
145 confOptions = {
146 modules = mkOption {
147 type = types.listOf types.str;
148 default = [ "partyline" "webadmin" "adminlog" "log" ];
149 example = [ "partyline" "webadmin" "adminlog" "log" ];
150 description = ''
151 A list of modules to include in the `znc.conf` file.
152 '';
153 };
154
155 userModules = mkOption {
156 type = types.listOf types.str;
157 default = [ ];
158 example = [ "fish" "push" ];
159 description = ''
160 A list of user modules to include in the `znc.conf` file.
161 '';
162 };
163
164 userName = mkOption {
165 default = defaultUserName;
166 example = "johntron";
167 type = types.string;
168 description = ''
169 The user name to use when generating the `znc.conf` file.
170 This is the user name used by the user logging into the ZNC web admin.
171 '';
172 };
173
174 nick = mkOption {
175 default = "znc-user";
176 example = "john";
177 type = types.string;
178 description = ''
179 The IRC nick to use when generating the `znc.conf` file.
180 '';
181 };
182
183 passBlock = mkOption {
184 default = defaultPassBlock;
185 example = "Must be the block generated by the `znc --makepass` command.";
186 type = types.string;
187 description = ''
188 The pass block to use when generating the `znc.conf` file.
189 This is the password used by the user logging into the ZNC web admin.
190 This is the block generated by the `znc --makepass` command.
191 !!! If not specified, please change this after starting the service. !!!
192 '';
193 };
194
195 port = mkOption {
196 default = 5000;
197 example = 5000;
198 type = types.int;
199 description = ''
200 Specifies the port on which to listen.
201 '';
202 };
203
204 useSSL = mkOption {
205 default = true;
206 example = true;
207 type = types.bool;
208 description = ''
209 Indicates whether the ZNC server should use SSL when listening on the specified port.
210 '';
211 };
212
213 extraZncConf = mkOption {
214 default = "";
215 type = types.lines;
216 description = ''
217 Extra config to `znc.conf` file
218 '';
219 };
220 };
221
222 modulePackages = mkOption {
223 type = types.listOf types.package;
224 default = [ ];
225 example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
226 description = ''
227 A list of global znc module packages to add to znc.
228 '';
229 };
230
231 mutable = mkOption {
232 default = false;
233 example = true;
234 type = types.bool;
235 description = ''
236 Indicates whether to allow the contents of the `dataDir` directory to be changed
237 by the user at run-time.
238 If true, modifications to the ZNC configuration after its initial creation are not
239 overwritten by a NixOS system rebuild.
240 If false, the ZNC configuration is rebuilt by every system rebuild.
241 If the user wants to manage the ZNC service using the web admin interface, this value
242 should be set to true.
243 '';
244 };
245
246 extraFlags = mkOption {
247 default = [ ];
248 example = [ "--debug" ];
249 type = types.listOf types.str;
250 description = ''
251 Extra flags to use when executing znc command.
252 '';
253 };
254 };
255 };
256
257
258 ###### Implementation
259
260 config = mkIf cfg.enable {
261
262 systemd.services.znc = {
263 description = "ZNC Server";
264 wantedBy = [ "multi-user.target" ];
265 after = [ "network.service" ];
266 serviceConfig = {
267 User = cfg.user;
268 Restart = "always";
269 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
270 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
271 };
272 preStart = ''
273 ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/configs
274
275 # If mutable, regenerate conf file every time.
276 ${optionalString (!cfg.mutable) ''
277 ${pkgs.coreutils}/bin/echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
278 ${pkgs.coreutils}/bin/rm -f ${cfg.dataDir}/configs/znc.conf
279 ''}
280
281 # Ensure essential files exist.
282 if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
283 ${pkgs.coreutils}/bin/echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
284 ${pkgs.coreutils}/bin/cp --no-clobber ${zncConfFile} ${cfg.dataDir}/configs/znc.conf
285 ${pkgs.coreutils}/bin/chmod u+rw ${cfg.dataDir}/configs/znc.conf
286 ${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
287 fi
288
289 if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
290 ${pkgs.coreutils}/bin/echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
291 ${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
292 fi
293
294 # Symlink modules
295 rm ${cfg.dataDir}/modules || true
296 ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
297 '';
298 script = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${toString cfg.extraFlags}";
299 };
300
301 users.extraUsers = optional (cfg.user == defaultUser)
302 { name = defaultUser;
303 description = "ZNC server daemon owner";
304 group = defaultUser;
305 uid = config.ids.uids.znc;
306 home = cfg.dataDir;
307 createHome = true;
308 };
309
310 users.extraGroups = optional (cfg.user == defaultUser)
311 { name = defaultUser;
312 gid = config.ids.gids.znc;
313 members = [ defaultUser ];
314 };
315
316 };
317}