1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 inherit (pkgs) cups cups_filters;
8
9 cfg = config.services.printing;
10
11 additionalBackends = pkgs.runCommand "additional-cups-backends" { }
12 ''
13 mkdir -p $out
14 if [ ! -e ${cups}/lib/cups/backend/smb ]; then
15 mkdir -p $out/lib/cups/backend
16 ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb
17 fi
18
19 # Provide support for printing via HTTPS.
20 if [ ! -e ${cups}/lib/cups/backend/https ]; then
21 mkdir -p $out/lib/cups/backend
22 ln -sv ${cups}/lib/cups/backend/ipp $out/lib/cups/backend/https
23 fi
24 '';
25
26 # Here we can enable additional backends, filters, etc. that are not
27 # part of CUPS itself, e.g. the SMB backend is part of Samba. Since
28 # we can't update ${cups}/lib/cups itself, we create a symlink tree
29 # here and add the additional programs. The ServerBin directive in
30 # cupsd.conf tells cupsd to use this tree.
31 bindir = pkgs.buildEnv {
32 name = "cups-progs";
33 paths = cfg.drivers;
34 pathsToLink = [ "/lib/cups" "/share/cups" "/bin" "/etc/cups" ];
35 postBuild = cfg.bindirCmds;
36 ignoreCollisions = true;
37 };
38
39in
40
41{
42
43 ###### interface
44
45 options = {
46 services.printing = {
47
48 enable = mkOption {
49 type = types.bool;
50 default = false;
51 description = ''
52 Whether to enable printing support through the CUPS daemon.
53 '';
54 };
55
56 listenAddresses = mkOption {
57 type = types.listOf types.str;
58 default = [ "127.0.0.1:631" ];
59 example = [ "*:631" ];
60 description = ''
61 A list of addresses and ports on which to listen.
62 '';
63 };
64
65 bindirCmds = mkOption {
66 type = types.lines;
67 internal = true;
68 default = "";
69 description = ''
70 Additional commands executed while creating the directory
71 containing the CUPS server binaries.
72 '';
73 };
74
75 defaultShared = mkOption {
76 type = types.bool;
77 default = false;
78 description = ''
79 Specifies whether local printers are shared by default.
80 '';
81 };
82
83 browsing = mkOption {
84 type = types.bool;
85 default = false;
86 description = ''
87 Specifies whether shared printers are advertised.
88 '';
89 };
90
91 webInterface = mkOption {
92 type = types.bool;
93 default = true;
94 description = ''
95 Specifies whether the web interface is enabled.
96 '';
97 };
98
99 cupsdConf = mkOption {
100 type = types.lines;
101 default = "";
102 example =
103 ''
104 BrowsePoll cups.example.com
105 LogLevel debug
106 '';
107 description = ''
108 The contents of the configuration file of the CUPS daemon
109 (<filename>cupsd.conf</filename>).
110 '';
111 };
112
113 cupsFilesConf = mkOption {
114 type = types.lines;
115 default = "";
116 description = ''
117 The contents of the configuration file of the CUPS daemon
118 (<filename>cups-files.conf</filename>).
119 '';
120 };
121
122 extraConf = mkOption {
123 type = types.lines;
124 default = "";
125 example =
126 ''
127 BrowsePoll cups.example.com
128 LogLevel debug
129 '';
130 description = ''
131 Extra contents of the configuration file of the CUPS daemon
132 (<filename>cupsd.conf</filename>).
133 '';
134 };
135
136 clientConf = mkOption {
137 type = types.lines;
138 default = "";
139 example =
140 ''
141 ServerName server.example.com
142 Encryption Never
143 '';
144 description = ''
145 The contents of the client configuration.
146 (<filename>client.conf</filename>)
147 '';
148 };
149
150 browsedConf = mkOption {
151 type = types.lines;
152 default = "";
153 example =
154 ''
155 BrowsePoll cups.example.com
156 '';
157 description = ''
158 The contents of the configuration. file of the CUPS Browsed daemon
159 (<filename>cups-browsed.conf</filename>)
160 '';
161 };
162
163 snmpConf = mkOption {
164 type = types.lines;
165 default = ''
166 Address @LOCAL
167 '';
168 description = ''
169 The contents of <filename>/etc/cups/snmp.conf</filename>. See "man
170 cups-snmp.conf" for a complete description.
171 '';
172 };
173
174 drivers = mkOption {
175 type = types.listOf types.path;
176 example = literalExample "[ pkgs.splix ]";
177 description = ''
178 CUPS drivers to use. Drivers provided by CUPS, cups-filters, Ghostscript
179 and Samba are added unconditionally.
180 '';
181 };
182
183 tempDir = mkOption {
184 type = types.path;
185 default = "/tmp";
186 example = "/tmp/cups";
187 description = ''
188 CUPSd temporary directory.
189 '';
190 };
191 };
192
193 };
194
195
196 ###### implementation
197
198 config = mkIf config.services.printing.enable {
199
200 users.extraUsers = singleton
201 { name = "cups";
202 uid = config.ids.uids.cups;
203 group = "lp";
204 description = "CUPS printing services";
205 };
206
207 environment.systemPackages = [ cups ];
208
209 environment.etc."cups/client.conf".text = cfg.clientConf;
210 environment.etc."cups/cups-files.conf".text = cfg.cupsFilesConf;
211 environment.etc."cups/cupsd.conf".text = cfg.cupsdConf;
212 environment.etc."cups/cups-browsed.conf".text = cfg.browsedConf;
213 environment.etc."cups/snmp.conf".text = cfg.snmpConf;
214
215 services.dbus.packages = [ cups ];
216
217 # Cups uses libusb to talk to printers, and does not use the
218 # linux kernel driver. If the driver is not in a black list, it
219 # gets loaded, and then cups cannot access the printers.
220 boot.blacklistedKernelModules = [ "usblp" ];
221
222 systemd.packages = [ cups ];
223
224 systemd.services.cups =
225 { wantedBy = [ "multi-user.target" ];
226 wants = [ "network.target" ];
227 after = [ "network.target" ];
228
229 path = [ cups ];
230
231 preStart =
232 ''
233 mkdir -m 0755 -p /etc/cups
234 mkdir -m 0700 -p /var/cache/cups
235 mkdir -m 0700 -p /var/spool/cups
236 mkdir -m 0755 -p ${cfg.tempDir}
237 '';
238
239 restartTriggers =
240 [ config.environment.etc."cups/cups-files.conf".source
241 config.environment.etc."cups/cupsd.conf".source
242 ];
243 };
244
245 systemd.services.cups-browsed = mkIf config.services.avahi.enable
246 { description = "CUPS Remote Printer Discovery";
247
248 wantedBy = [ "multi-user.target" ];
249 wants = [ "cups.service" "avahi-daemon.service" ];
250 after = [ "cups.service" "avahi-daemon.service" ];
251
252 path = [ cups ];
253
254 serviceConfig.ExecStart = "${cups_filters}/bin/cups-browsed";
255
256 restartTriggers =
257 [ config.environment.etc."cups/cups-browsed.conf".source
258 ];
259 };
260
261 services.printing.drivers =
262 [ cups pkgs.ghostscript pkgs.cups_filters additionalBackends
263 pkgs.perl pkgs.coreutils pkgs.gnused pkgs.bc pkgs.gawk pkgs.gnugrep
264 ];
265
266 services.printing.cupsFilesConf =
267 ''
268 SystemGroup root wheel
269
270 ServerBin ${bindir}/lib/cups
271 DataDir ${bindir}/share/cups
272
273 AccessLog syslog
274 ErrorLog syslog
275 PageLog syslog
276
277 TempDir ${cfg.tempDir}
278
279 # User and group used to run external programs, including
280 # those that actually send the job to the printer. Note that
281 # Udev sets the group of printer devices to `lp', so we want
282 # these programs to run as `lp' as well.
283 User cups
284 Group lp
285 '';
286
287 services.printing.cupsdConf =
288 ''
289 LogLevel info
290
291 ${concatMapStrings (addr: ''
292 Listen ${addr}
293 '') cfg.listenAddresses}
294 Listen /var/run/cups/cups.sock
295
296 SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin:${bindir}/sbin
297
298 DefaultShared ${if cfg.defaultShared then "Yes" else "No"}
299
300 Browsing ${if cfg.browsing then "Yes" else "No"}
301
302 WebInterface ${if cfg.webInterface then "Yes" else "No"}
303
304 DefaultAuthType Basic
305
306 <Location />
307 Order allow,deny
308 Allow localhost
309 </Location>
310
311 <Location /admin>
312 Order allow,deny
313 Allow localhost
314 </Location>
315
316 <Location /admin/conf>
317 AuthType Basic
318 Require user @SYSTEM
319 Order allow,deny
320 Allow localhost
321 </Location>
322
323 <Policy default>
324 <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job>
325 Require user @OWNER @SYSTEM
326 Order deny,allow
327 </Limit>
328
329 <Limit Pause-Printer Resume-Printer Set-Printer-Attributes Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Add-Printer CUPS-Delete-Printer CUPS-Add-Class CUPS-Delete-Class CUPS-Accept-Jobs CUPS-Reject-Jobs CUPS-Set-Default>
330 AuthType Basic
331 Require user @SYSTEM
332 Order deny,allow
333 </Limit>
334
335 <Limit Cancel-Job CUPS-Authenticate-Job>
336 Require user @OWNER @SYSTEM
337 Order deny,allow
338 </Limit>
339
340 <Limit All>
341 Order deny,allow
342 </Limit>
343 </Policy>
344
345 ${cfg.extraConf}
346 '';
347
348 security.pam.services.cups = {};
349
350 };
351}