]>
Commit | Line | Data |
---|---|---|
1 | { config, lib, pkgs, ... }: | |
2 | ||
3 | with lib; | |
4 | ||
5 | let | |
6 | ||
7 | mainCfg = config.services.httpdProd; | |
8 | ||
9 | httpd = mainCfg.package.out; | |
10 | ||
11 | version24 = !versionOlder httpd.version "2.4"; | |
12 | ||
13 | httpdConf = mainCfg.configFile; | |
14 | ||
15 | php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ }; | |
16 | ||
17 | phpMajorVersion = head (splitString "." php.version); | |
18 | ||
19 | mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; }; | |
20 | ||
21 | defaultListen = cfg: if cfg.enableSSL | |
22 | then [{ip = "*"; port = 443;}] | |
23 | else [{ip = "*"; port = 80;}]; | |
24 | ||
25 | getListen = cfg: | |
26 | let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen; | |
27 | in if list == [] | |
28 | then defaultListen cfg | |
29 | else list; | |
30 | ||
31 | listenToString = l: "${l.ip}:${toString l.port}"; | |
32 | ||
33 | extraModules = attrByPath ["extraModules"] [] mainCfg; | |
34 | extraForeignModules = filter isAttrs extraModules; | |
35 | extraApacheModules = filter isString extraModules; | |
36 | ||
37 | ||
38 | makeServerInfo = cfg: { | |
39 | # Canonical name must not include a trailing slash. | |
40 | canonicalNames = | |
41 | let defaultPort = (head (defaultListen cfg)).port; in | |
42 | map (port: | |
43 | (if cfg.enableSSL then "https" else "http") + "://" + | |
44 | cfg.hostName + | |
45 | (if port != defaultPort then ":${toString port}" else "") | |
46 | ) (map (x: x.port) (getListen cfg)); | |
47 | ||
48 | # Admin address: inherit from the main server if not specified for | |
49 | # a virtual host. | |
50 | adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr; | |
51 | ||
52 | vhostConfig = cfg; | |
53 | serverConfig = mainCfg; | |
54 | fullConfig = config; # machine config | |
55 | }; | |
56 | ||
57 | ||
58 | allHosts = [mainCfg] ++ mainCfg.virtualHosts; | |
59 | ||
60 | ||
61 | callSubservices = serverInfo: defs: | |
62 | let f = svc: | |
63 | let | |
64 | svcFunction = | |
65 | if svc ? function then svc.function | |
66 | # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix; | |
67 | else if svc ? serviceExpression then import (toString svc.serviceExpression) | |
68 | else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix"); | |
69 | config = (evalModules | |
70 | { modules = [ { options = res.options; config = svc.config or svc; } ]; | |
71 | check = false; | |
72 | }).config; | |
73 | defaults = { | |
74 | extraConfig = ""; | |
75 | extraModules = []; | |
76 | extraModulesPre = []; | |
77 | extraPath = []; | |
78 | extraServerPath = []; | |
79 | globalEnvVars = []; | |
80 | robotsEntries = ""; | |
81 | startupScript = ""; | |
82 | enablePHP = false; | |
83 | enablePerl = false; | |
84 | phpOptions = ""; | |
85 | options = {}; | |
86 | documentRoot = null; | |
87 | }; | |
88 | res = defaults // svcFunction { inherit config lib pkgs serverInfo php; }; | |
89 | in res; | |
90 | in map f defs; | |
91 | ||
92 | ||
93 | # !!! callSubservices is expensive | |
94 | subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices; | |
95 | ||
96 | mainSubservices = subservicesFor mainCfg; | |
97 | ||
98 | allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts; | |
99 | ||
100 | ||
101 | enableSSL = any (vhost: vhost.enableSSL) allHosts; | |
102 | ||
103 | ||
104 | # Names of modules from ${httpd}/modules that we want to load. | |
105 | apacheModules = | |
106 | [ # HTTP authentication mechanisms: basic and digest. | |
107 | "auth_basic" "auth_digest" | |
108 | ||
109 | # Authentication: is the user who he claims to be? | |
110 | "authn_file" "authn_dbm" "authn_anon" | |
111 | (if version24 then "authn_core" else "authn_alias") | |
112 | ||
113 | # Authorization: is the user allowed access? | |
114 | "authz_user" "authz_groupfile" "authz_host" | |
115 | ||
116 | # Other modules. | |
117 | "ext_filter" "include" "log_config" "env" "mime_magic" | |
118 | "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif" | |
119 | "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs" | |
120 | "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling" | |
121 | "userdir" "alias" "rewrite" "proxy" "proxy_http" | |
122 | ] | |
123 | ++ optionals version24 [ | |
124 | "mpm_${mainCfg.multiProcessingModule}" | |
125 | "authz_core" | |
126 | "unixd" | |
127 | "cache" "cache_disk" | |
128 | "slotmem_shm" | |
129 | "socache_shmcb" | |
130 | # For compatibility with old configurations, the new module mod_access_compat is provided. | |
131 | "access_compat" | |
132 | ] | |
133 | ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ]) | |
134 | ++ optional enableSSL "ssl" | |
135 | ++ extraApacheModules; | |
136 | ||
137 | ||
138 | allDenied = if version24 then '' | |
139 | Require all denied | |
140 | '' else '' | |
141 | Order deny,allow | |
142 | Deny from all | |
143 | ''; | |
144 | ||
145 | allGranted = if version24 then '' | |
146 | Require all granted | |
147 | '' else '' | |
148 | Order allow,deny | |
149 | Allow from all | |
150 | ''; | |
151 | ||
152 | ||
153 | loggingConf = (if mainCfg.logFormat != "none" then '' | |
154 | ErrorLog ${mainCfg.logDir}/error_log | |
155 | ||
156 | LogLevel notice | |
157 | ||
158 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined | |
159 | LogFormat "%h %l %u %t \"%r\" %>s %b" common | |
160 | LogFormat "%{Referer}i -> %U" referer | |
161 | LogFormat "%{User-agent}i" agent | |
162 | ||
163 | CustomLog ${mainCfg.logDir}/access_log ${mainCfg.logFormat} | |
164 | '' else '' | |
165 | ErrorLog /dev/null | |
166 | ''); | |
167 | ||
168 | ||
169 | browserHacks = '' | |
170 | BrowserMatch "Mozilla/2" nokeepalive | |
171 | BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 | |
172 | BrowserMatch "RealPlayer 4\.0" force-response-1.0 | |
173 | BrowserMatch "Java/1\.0" force-response-1.0 | |
174 | BrowserMatch "JDK/1\.0" force-response-1.0 | |
175 | BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully | |
176 | BrowserMatch "^WebDrive" redirect-carefully | |
177 | BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully | |
178 | BrowserMatch "^gnome-vfs" redirect-carefully | |
179 | ''; | |
180 | ||
181 | ||
182 | sslConf = '' | |
183 | SSLSessionCache ${if version24 then "shmcb" else "shm"}:${mainCfg.stateDir}/ssl_scache(512000) | |
184 | ||
185 | ${if version24 then "Mutex" else "SSLMutex"} posixsem | |
186 | ||
187 | SSLRandomSeed startup builtin | |
188 | SSLRandomSeed connect builtin | |
189 | ||
190 | SSLProtocol All -SSLv2 -SSLv3 | |
191 | SSLCipherSuite HIGH:!aNULL:!MD5:!EXP | |
192 | SSLHonorCipherOrder on | |
193 | ''; | |
194 | ||
195 | ||
196 | mimeConf = '' | |
197 | TypesConfig ${httpd}/conf/mime.types | |
198 | ||
199 | AddType application/x-x509-ca-cert .crt | |
200 | AddType application/x-pkcs7-crl .crl | |
201 | AddType application/x-httpd-php .php .phtml | |
202 | ||
203 | <IfModule mod_mime_magic.c> | |
204 | MIMEMagicFile ${httpd}/conf/magic | |
205 | </IfModule> | |
206 | ''; | |
207 | ||
208 | ||
209 | perServerConf = isMainServer: cfg: let | |
210 | ||
211 | serverInfo = makeServerInfo cfg; | |
212 | ||
213 | subservices = callSubservices serverInfo cfg.extraSubservices; | |
214 | ||
215 | maybeDocumentRoot = fold (svc: acc: | |
216 | if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc | |
217 | ) null ([ cfg ] ++ subservices); | |
218 | ||
219 | documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else | |
220 | pkgs.runCommand "empty" {} "mkdir -p $out"; | |
221 | ||
222 | documentRootConf = '' | |
223 | DocumentRoot "${documentRoot}" | |
224 | ||
225 | <Directory "${documentRoot}"> | |
226 | Options Indexes FollowSymLinks | |
227 | AllowOverride None | |
228 | ${allGranted} | |
229 | </Directory> | |
230 | ''; | |
231 | ||
232 | robotsTxt = | |
233 | concatStringsSep "\n" (filter (x: x != "") ( | |
234 | # If this is a vhost, the include the entries for the main server as well. | |
235 | (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices) | |
236 | ++ [cfg.robotsEntries] | |
237 | ++ (map (svc: svc.robotsEntries) subservices))); | |
238 | ||
239 | in '' | |
240 | ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)} | |
241 | ||
242 | ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases} | |
243 | ||
244 | ${if cfg.sslServerCert != null then '' | |
245 | SSLCertificateFile ${cfg.sslServerCert} | |
246 | SSLCertificateKeyFile ${cfg.sslServerKey} | |
247 | ${if cfg.sslServerChain != null then '' | |
248 | SSLCertificateChainFile ${cfg.sslServerChain} | |
249 | '' else ""} | |
250 | '' else ""} | |
251 | ||
252 | ${if cfg.enableSSL then '' | |
253 | SSLEngine on | |
254 | '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */ | |
255 | '' | |
256 | SSLEngine off | |
257 | '' else ""} | |
258 | ||
259 | ${if isMainServer || cfg.adminAddr != null then '' | |
260 | ServerAdmin ${cfg.adminAddr} | |
261 | '' else ""} | |
262 | ||
263 | ${if !isMainServer && mainCfg.logPerVirtualHost then '' | |
264 | ErrorLog ${mainCfg.logDir}/error_log-${cfg.hostName} | |
265 | CustomLog ${mainCfg.logDir}/access_log-${cfg.hostName} ${cfg.logFormat} | |
266 | '' else ""} | |
267 | ||
268 | ${optionalString (robotsTxt != "") '' | |
269 | Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt} | |
270 | ''} | |
271 | ||
272 | ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""} | |
273 | ||
274 | ${if cfg.enableUserDir then '' | |
275 | ||
276 | UserDir public_html | |
277 | UserDir disabled root | |
278 | ||
279 | <Directory "/home/*/public_html"> | |
280 | AllowOverride FileInfo AuthConfig Limit Indexes | |
281 | Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec | |
282 | <Limit GET POST OPTIONS> | |
283 | ${allGranted} | |
284 | </Limit> | |
285 | <LimitExcept GET POST OPTIONS> | |
286 | ${allDenied} | |
287 | </LimitExcept> | |
288 | </Directory> | |
289 | ||
290 | '' else ""} | |
291 | ||
292 | ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then '' | |
293 | RedirectPermanent / ${cfg.globalRedirect} | |
294 | '' else ""} | |
295 | ||
296 | ${ | |
297 | let makeFileConf = elem: '' | |
298 | Alias ${elem.urlPath} ${elem.file} | |
299 | ''; | |
300 | in concatMapStrings makeFileConf cfg.servedFiles | |
301 | } | |
302 | ||
303 | ${ | |
304 | let makeDirConf = elem: '' | |
305 | Alias ${elem.urlPath} ${elem.dir}/ | |
306 | <Directory ${elem.dir}> | |
307 | Options +Indexes | |
308 | ${allGranted} | |
309 | AllowOverride All | |
310 | </Directory> | |
311 | ''; | |
312 | in concatMapStrings makeDirConf cfg.servedDirs | |
313 | } | |
314 | ||
315 | ${concatMapStrings (svc: svc.extraConfig) subservices} | |
316 | ||
317 | ${cfg.extraConfig} | |
318 | ''; | |
319 | ||
320 | ||
321 | confFile = pkgs.writeText "httpd.conf" '' | |
322 | ||
323 | ServerRoot ${httpd} | |
324 | ||
325 | ${optionalString version24 '' | |
326 | DefaultRuntimeDir ${mainCfg.stateDir}/runtime | |
327 | ''} | |
328 | ||
329 | PidFile ${mainCfg.stateDir}/httpd.pid | |
330 | ||
331 | ${optionalString (mainCfg.multiProcessingModule != "prefork") '' | |
332 | # mod_cgid requires this. | |
333 | ScriptSock ${mainCfg.stateDir}/cgisock | |
334 | ''} | |
335 | ||
336 | <IfModule prefork.c> | |
337 | MaxClients ${toString mainCfg.maxClients} | |
338 | MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild} | |
339 | </IfModule> | |
340 | ||
341 | ${let | |
342 | listen = concatMap getListen allHosts; | |
343 | toStr = listen: "Listen ${listenToString listen}\n"; | |
344 | uniqueListen = uniqList {inputList = map toStr listen;}; | |
345 | in concatStrings uniqueListen | |
346 | } | |
347 | ||
348 | User ${mainCfg.user} | |
349 | Group ${mainCfg.group} | |
350 | ||
351 | ${let | |
352 | load = {name, path}: "LoadModule ${name}_module ${path}\n"; | |
353 | allModules = | |
354 | concatMap (svc: svc.extraModulesPre) allSubservices | |
355 | ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules | |
356 | ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; } | |
357 | ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; } | |
358 | ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; } | |
359 | ++ concatMap (svc: svc.extraModules) allSubservices | |
360 | ++ extraForeignModules; | |
361 | in concatMapStrings load allModules | |
362 | } | |
363 | ||
364 | AddHandler type-map var | |
365 | ||
366 | <Files ~ "^\.ht"> | |
367 | ${allDenied} | |
368 | </Files> | |
369 | ||
370 | ${mimeConf} | |
371 | ${loggingConf} | |
372 | ${browserHacks} | |
373 | ||
374 | Include ${httpd}/conf/extra/httpd-default.conf | |
375 | Include ${httpd}/conf/extra/httpd-autoindex.conf | |
376 | Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf | |
377 | Include ${httpd}/conf/extra/httpd-languages.conf | |
378 | ||
379 | ${if enableSSL then sslConf else ""} | |
380 | ||
381 | # Fascist default - deny access to everything. | |
382 | <Directory /> | |
383 | Options FollowSymLinks | |
384 | AllowOverride None | |
385 | ${allDenied} | |
386 | </Directory> | |
387 | ||
388 | # Generate directives for the main server. | |
389 | ${perServerConf true mainCfg} | |
390 | ||
391 | # Always enable virtual hosts; it doesn't seem to hurt. | |
392 | ${let | |
393 | listen = concatMap getListen allHosts; | |
394 | uniqueListen = uniqList {inputList = listen;}; | |
395 | directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen; | |
396 | in optionalString (!version24) directives | |
397 | } | |
398 | ||
399 | ${let | |
400 | makeVirtualHost = vhost: '' | |
401 | <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}> | |
402 | ${perServerConf false vhost} | |
403 | </VirtualHost> | |
404 | ''; | |
405 | in concatMapStrings makeVirtualHost mainCfg.virtualHosts | |
406 | } | |
407 | ''; | |
408 | ||
409 | ||
410 | enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices; | |
411 | ||
412 | enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices; | |
413 | ||
414 | ||
415 | # Generate the PHP configuration file. Should probably be factored | |
416 | # out into a separate module. | |
417 | phpIni = pkgs.runCommand "php.ini" | |
418 | { options = concatStringsSep "\n" | |
419 | ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices)); | |
420 | } | |
421 | '' | |
422 | cat ${php}/etc/php.ini > $out | |
423 | echo "$options" >> $out | |
424 | ''; | |
425 | ||
426 | in | |
427 | ||
428 | ||
429 | { | |
430 | ||
431 | ###### interface | |
432 | ||
433 | options = { | |
434 | ||
435 | services.httpdProd = { | |
436 | ||
437 | enable = mkOption { | |
438 | type = types.bool; | |
439 | default = false; | |
440 | description = "Whether to enable the Apache HTTP Server."; | |
441 | }; | |
442 | ||
443 | package = mkOption { | |
444 | type = types.package; | |
445 | default = pkgs.apacheHttpd; | |
446 | defaultText = "pkgs.apacheHttpd"; | |
447 | description = '' | |
448 | Overridable attribute of the Apache HTTP Server package to use. | |
449 | ''; | |
450 | }; | |
451 | ||
452 | configFile = mkOption { | |
453 | type = types.path; | |
454 | default = confFile; | |
455 | defaultText = "confFile"; | |
456 | example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."''; | |
457 | description = '' | |
458 | Override the configuration file used by Apache. By default, | |
459 | NixOS generates one automatically. | |
460 | ''; | |
461 | }; | |
462 | ||
463 | extraConfig = mkOption { | |
464 | type = types.lines; | |
465 | default = ""; | |
466 | description = '' | |
467 | Cnfiguration lines appended to the generated Apache | |
468 | configuration file. Note that this mechanism may not work | |
469 | when <option>configFile</option> is overridden. | |
470 | ''; | |
471 | }; | |
472 | ||
473 | extraModules = mkOption { | |
474 | type = types.listOf types.unspecified; | |
475 | default = []; | |
476 | example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]''; | |
477 | description = '' | |
478 | Additional Apache modules to be used. These can be | |
479 | specified as a string in the case of modules distributed | |
480 | with Apache, or as an attribute set specifying the | |
481 | <varname>name</varname> and <varname>path</varname> of the | |
482 | module. | |
483 | ''; | |
484 | }; | |
485 | ||
486 | logPerVirtualHost = mkOption { | |
487 | type = types.bool; | |
488 | default = false; | |
489 | description = '' | |
490 | If enabled, each virtual host gets its own | |
491 | <filename>access_log</filename> and | |
492 | <filename>error_log</filename>, namely suffixed by the | |
493 | <option>hostName</option> of the virtual host. | |
494 | ''; | |
495 | }; | |
496 | ||
497 | user = mkOption { | |
498 | type = types.str; | |
499 | default = "wwwrun"; | |
500 | description = '' | |
501 | User account under which httpd runs. The account is created | |
502 | automatically if it doesn't exist. | |
503 | ''; | |
504 | }; | |
505 | ||
506 | group = mkOption { | |
507 | type = types.str; | |
508 | default = "wwwrun"; | |
509 | description = '' | |
510 | Group under which httpd runs. The account is created | |
511 | automatically if it doesn't exist. | |
512 | ''; | |
513 | }; | |
514 | ||
515 | logDir = mkOption { | |
516 | type = types.path; | |
517 | default = "/var/log/httpd"; | |
518 | description = '' | |
519 | Directory for Apache's log files. It is created automatically. | |
520 | ''; | |
521 | }; | |
522 | ||
523 | stateDir = mkOption { | |
524 | type = types.path; | |
525 | default = "/run/httpd"; | |
526 | description = '' | |
527 | Directory for Apache's transient runtime state (such as PID | |
528 | files). It is created automatically. Note that the default, | |
529 | <filename>/run/httpd</filename>, is deleted at boot time. | |
530 | ''; | |
531 | }; | |
532 | ||
533 | virtualHosts = mkOption { | |
534 | type = types.listOf (types.submodule ( | |
535 | { options = import ./per-server-options.nix { | |
536 | inherit lib; | |
537 | forMainServer = false; | |
538 | }; | |
539 | })); | |
540 | default = []; | |
541 | example = [ | |
542 | { hostName = "foo"; | |
543 | documentRoot = "/data/webroot-foo"; | |
544 | } | |
545 | { hostName = "bar"; | |
546 | documentRoot = "/data/webroot-bar"; | |
547 | } | |
548 | ]; | |
549 | description = '' | |
550 | Specification of the virtual hosts served by Apache. Each | |
551 | element should be an attribute set specifying the | |
552 | configuration of the virtual host. The available options | |
553 | are the non-global options permissible for the main host. | |
554 | ''; | |
555 | }; | |
556 | ||
557 | enableMellon = mkOption { | |
558 | type = types.bool; | |
559 | default = false; | |
560 | description = "Whether to enable the mod_auth_mellon module."; | |
561 | }; | |
562 | ||
563 | enablePHP = mkOption { | |
564 | type = types.bool; | |
565 | default = false; | |
566 | description = "Whether to enable the PHP module."; | |
567 | }; | |
568 | ||
569 | phpPackage = mkOption { | |
570 | type = types.package; | |
571 | default = pkgs.php; | |
572 | defaultText = "pkgs.php"; | |
573 | description = '' | |
574 | Overridable attribute of the PHP package to use. | |
575 | ''; | |
576 | }; | |
577 | ||
578 | enablePerl = mkOption { | |
579 | type = types.bool; | |
580 | default = false; | |
581 | description = "Whether to enable the Perl module (mod_perl)."; | |
582 | }; | |
583 | ||
584 | phpOptions = mkOption { | |
585 | type = types.lines; | |
586 | default = ""; | |
587 | example = | |
588 | '' | |
589 | date.timezone = "CET" | |
590 | ''; | |
591 | description = | |
592 | "Options appended to the PHP configuration file <filename>php.ini</filename>."; | |
593 | }; | |
594 | ||
595 | multiProcessingModule = mkOption { | |
596 | type = types.str; | |
597 | default = "prefork"; | |
598 | example = "worker"; | |
599 | description = | |
600 | '' | |
601 | Multi-processing module to be used by Apache. Available | |
602 | modules are <literal>prefork</literal> (the default; | |
603 | handles each request in a separate child process), | |
604 | <literal>worker</literal> (hybrid approach that starts a | |
605 | number of child processes each running a number of | |
606 | threads) and <literal>event</literal> (a recent variant of | |
607 | <literal>worker</literal> that handles persistent | |
608 | connections more efficiently). | |
609 | ''; | |
610 | }; | |
611 | ||
612 | maxClients = mkOption { | |
613 | type = types.int; | |
614 | default = 150; | |
615 | example = 8; | |
616 | description = "Maximum number of httpd processes (prefork)"; | |
617 | }; | |
618 | ||
619 | maxRequestsPerChild = mkOption { | |
620 | type = types.int; | |
621 | default = 0; | |
622 | example = 500; | |
623 | description = | |
624 | "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited"; | |
625 | }; | |
626 | } | |
627 | ||
628 | # Include the options shared between the main server and virtual hosts. | |
629 | // (import ./per-server-options.nix { | |
630 | inherit lib; | |
631 | forMainServer = true; | |
632 | }); | |
633 | ||
634 | }; | |
635 | ||
636 | ||
637 | ###### implementation | |
638 | ||
639 | config = mkIf config.services.httpdProd.enable { | |
640 | ||
641 | assertions = [ { assertion = mainCfg.enableSSL == true | |
642 | -> mainCfg.sslServerCert != null | |
643 | && mainCfg.sslServerKey != null; | |
644 | message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } | |
645 | ]; | |
646 | ||
647 | warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts); | |
648 | ||
649 | environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; | |
650 | ||
651 | services.httpdProd.phpOptions = | |
652 | '' | |
653 | ; Needed for PHP's mail() function. | |
654 | sendmail_path = sendmail -t -i | |
655 | '' + optionalString (!isNull config.time.timeZone) '' | |
656 | ||
657 | ; Apparently PHP doesn't use $TZ. | |
658 | date.timezone = "${config.time.timeZone}" | |
659 | ''; | |
660 | ||
661 | systemd.services.httpdProd = | |
662 | { description = "Apache HTTPD"; | |
663 | ||
664 | wantedBy = [ "multi-user.target" ]; | |
665 | wants = [ "keys.target" ]; | |
666 | after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ]; | |
667 | ||
668 | path = | |
669 | [ httpd pkgs.coreutils pkgs.gnugrep ] | |
670 | ++ # Needed for PHP's mail() function. !!! Probably the | |
671 | # ssmtp module should export the path to sendmail in | |
672 | # some way. | |
673 | optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp | |
674 | ++ concatMap (svc: svc.extraServerPath) allSubservices; | |
675 | ||
676 | environment = | |
677 | optionalAttrs enablePHP { PHPRC = phpIni; } | |
678 | // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; } | |
679 | // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices)); | |
680 | ||
681 | preStart = | |
682 | '' | |
683 | mkdir -m 0750 -p ${mainCfg.stateDir} | |
684 | [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir} | |
685 | ${optionalString version24 '' | |
686 | mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" | |
687 | [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" | |
688 | ''} | |
689 | mkdir -m 0700 -p ${mainCfg.logDir} | |
690 | ||
691 | # Get rid of old semaphores. These tend to accumulate across | |
692 | # server restarts, eventually preventing it from restarting | |
693 | # successfully. | |
694 | for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do | |
695 | ${pkgs.utillinux}/bin/ipcrm -s $i | |
696 | done | |
697 | ||
698 | # Run the startup hooks for the subservices. | |
699 | for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do | |
700 | echo Running Apache startup hook $i... | |
701 | $i | |
702 | done | |
703 | ''; | |
704 | ||
705 | serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}"; | |
706 | serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop"; | |
707 | serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful"; | |
708 | serviceConfig.Type = "forking"; | |
709 | serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid"; | |
710 | serviceConfig.Restart = "always"; | |
711 | serviceConfig.RestartSec = "5s"; | |
712 | }; | |
713 | ||
714 | }; | |
715 | } |