]>
Commit | Line | Data |
---|---|---|
1 | { config, lib, pkgs, ... }: | |
2 | ||
3 | with lib; | |
4 | ||
5 | let | |
6 | ||
7 | mainCfg = config.services.httpdInte; | |
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 | # But do allow access to files in the store so that we don't have | |
389 | # to generate <Directory> clauses for every generated file that we | |
390 | # want to serve. | |
391 | <Directory /nix/store> | |
392 | ${allGranted} | |
393 | </Directory> | |
394 | ||
395 | # Generate directives for the main server. | |
396 | ${perServerConf true mainCfg} | |
397 | ||
398 | # Always enable virtual hosts; it doesn't seem to hurt. | |
399 | ${let | |
400 | listen = concatMap getListen allHosts; | |
401 | uniqueListen = uniqList {inputList = listen;}; | |
402 | directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen; | |
403 | in optionalString (!version24) directives | |
404 | } | |
405 | ||
406 | ${let | |
407 | makeVirtualHost = vhost: '' | |
408 | <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}> | |
409 | ${perServerConf false vhost} | |
410 | </VirtualHost> | |
411 | ''; | |
412 | in concatMapStrings makeVirtualHost mainCfg.virtualHosts | |
413 | } | |
414 | ''; | |
415 | ||
416 | ||
417 | enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices; | |
418 | ||
419 | enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices; | |
420 | ||
421 | ||
422 | # Generate the PHP configuration file. Should probably be factored | |
423 | # out into a separate module. | |
424 | phpIni = pkgs.runCommand "php.ini" | |
425 | { options = concatStringsSep "\n" | |
426 | ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices)); | |
427 | } | |
428 | '' | |
429 | cat ${php}/etc/php.ini > $out | |
430 | echo "$options" >> $out | |
431 | ''; | |
432 | ||
433 | in | |
434 | ||
435 | ||
436 | { | |
437 | ||
438 | ###### interface | |
439 | ||
440 | options = { | |
441 | ||
442 | services.httpdInte = { | |
443 | ||
444 | enable = mkOption { | |
445 | type = types.bool; | |
446 | default = false; | |
447 | description = "Whether to enable the Apache HTTP Server."; | |
448 | }; | |
449 | ||
450 | package = mkOption { | |
451 | type = types.package; | |
452 | default = pkgs.apacheHttpd; | |
453 | defaultText = "pkgs.apacheHttpd"; | |
454 | description = '' | |
455 | Overridable attribute of the Apache HTTP Server package to use. | |
456 | ''; | |
457 | }; | |
458 | ||
459 | configFile = mkOption { | |
460 | type = types.path; | |
461 | default = confFile; | |
462 | defaultText = "confFile"; | |
463 | example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."''; | |
464 | description = '' | |
465 | Override the configuration file used by Apache. By default, | |
466 | NixOS generates one automatically. | |
467 | ''; | |
468 | }; | |
469 | ||
470 | extraConfig = mkOption { | |
471 | type = types.lines; | |
472 | default = ""; | |
473 | description = '' | |
474 | Cnfiguration lines appended to the generated Apache | |
475 | configuration file. Note that this mechanism may not work | |
476 | when <option>configFile</option> is overridden. | |
477 | ''; | |
478 | }; | |
479 | ||
480 | extraModules = mkOption { | |
481 | type = types.listOf types.unspecified; | |
482 | default = []; | |
483 | example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]''; | |
484 | description = '' | |
485 | Additional Apache modules to be used. These can be | |
486 | specified as a string in the case of modules distributed | |
487 | with Apache, or as an attribute set specifying the | |
488 | <varname>name</varname> and <varname>path</varname> of the | |
489 | module. | |
490 | ''; | |
491 | }; | |
492 | ||
493 | logPerVirtualHost = mkOption { | |
494 | type = types.bool; | |
495 | default = false; | |
496 | description = '' | |
497 | If enabled, each virtual host gets its own | |
498 | <filename>access_log</filename> and | |
499 | <filename>error_log</filename>, namely suffixed by the | |
500 | <option>hostName</option> of the virtual host. | |
501 | ''; | |
502 | }; | |
503 | ||
504 | user = mkOption { | |
505 | type = types.str; | |
506 | default = "wwwrun"; | |
507 | description = '' | |
508 | User account under which httpd runs. The account is created | |
509 | automatically if it doesn't exist. | |
510 | ''; | |
511 | }; | |
512 | ||
513 | group = mkOption { | |
514 | type = types.str; | |
515 | default = "wwwrun"; | |
516 | description = '' | |
517 | Group under which httpd runs. The account is created | |
518 | automatically if it doesn't exist. | |
519 | ''; | |
520 | }; | |
521 | ||
522 | logDir = mkOption { | |
523 | type = types.path; | |
524 | default = "/var/log/httpd"; | |
525 | description = '' | |
526 | Directory for Apache's log files. It is created automatically. | |
527 | ''; | |
528 | }; | |
529 | ||
530 | stateDir = mkOption { | |
531 | type = types.path; | |
532 | default = "/run/httpd"; | |
533 | description = '' | |
534 | Directory for Apache's transient runtime state (such as PID | |
535 | files). It is created automatically. Note that the default, | |
536 | <filename>/run/httpd</filename>, is deleted at boot time. | |
537 | ''; | |
538 | }; | |
539 | ||
540 | virtualHosts = mkOption { | |
541 | type = types.listOf (types.submodule ( | |
542 | { options = import ./per-server-options.nix { | |
543 | inherit lib; | |
544 | forMainServer = false; | |
545 | }; | |
546 | })); | |
547 | default = []; | |
548 | example = [ | |
549 | { hostName = "foo"; | |
550 | documentRoot = "/data/webroot-foo"; | |
551 | } | |
552 | { hostName = "bar"; | |
553 | documentRoot = "/data/webroot-bar"; | |
554 | } | |
555 | ]; | |
556 | description = '' | |
557 | Specification of the virtual hosts served by Apache. Each | |
558 | element should be an attribute set specifying the | |
559 | configuration of the virtual host. The available options | |
560 | are the non-global options permissible for the main host. | |
561 | ''; | |
562 | }; | |
563 | ||
564 | enableMellon = mkOption { | |
565 | type = types.bool; | |
566 | default = false; | |
567 | description = "Whether to enable the mod_auth_mellon module."; | |
568 | }; | |
569 | ||
570 | enablePHP = mkOption { | |
571 | type = types.bool; | |
572 | default = false; | |
573 | description = "Whether to enable the PHP module."; | |
574 | }; | |
575 | ||
576 | phpPackage = mkOption { | |
577 | type = types.package; | |
578 | default = pkgs.php; | |
579 | defaultText = "pkgs.php"; | |
580 | description = '' | |
581 | Overridable attribute of the PHP package to use. | |
582 | ''; | |
583 | }; | |
584 | ||
585 | enablePerl = mkOption { | |
586 | type = types.bool; | |
587 | default = false; | |
588 | description = "Whether to enable the Perl module (mod_perl)."; | |
589 | }; | |
590 | ||
591 | phpOptions = mkOption { | |
592 | type = types.lines; | |
593 | default = ""; | |
594 | example = | |
595 | '' | |
596 | date.timezone = "CET" | |
597 | ''; | |
598 | description = | |
599 | "Options appended to the PHP configuration file <filename>php.ini</filename>."; | |
600 | }; | |
601 | ||
602 | multiProcessingModule = mkOption { | |
603 | type = types.str; | |
604 | default = "prefork"; | |
605 | example = "worker"; | |
606 | description = | |
607 | '' | |
608 | Multi-processing module to be used by Apache. Available | |
609 | modules are <literal>prefork</literal> (the default; | |
610 | handles each request in a separate child process), | |
611 | <literal>worker</literal> (hybrid approach that starts a | |
612 | number of child processes each running a number of | |
613 | threads) and <literal>event</literal> (a recent variant of | |
614 | <literal>worker</literal> that handles persistent | |
615 | connections more efficiently). | |
616 | ''; | |
617 | }; | |
618 | ||
619 | maxClients = mkOption { | |
620 | type = types.int; | |
621 | default = 150; | |
622 | example = 8; | |
623 | description = "Maximum number of httpd processes (prefork)"; | |
624 | }; | |
625 | ||
626 | maxRequestsPerChild = mkOption { | |
627 | type = types.int; | |
628 | default = 0; | |
629 | example = 500; | |
630 | description = | |
631 | "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited"; | |
632 | }; | |
633 | } | |
634 | ||
635 | # Include the options shared between the main server and virtual hosts. | |
636 | // (import ./per-server-options.nix { | |
637 | inherit lib; | |
638 | forMainServer = true; | |
639 | }); | |
640 | ||
641 | }; | |
642 | ||
643 | ||
644 | ###### implementation | |
645 | ||
646 | config = mkIf config.services.httpdInte.enable { | |
647 | ||
648 | assertions = [ { assertion = mainCfg.enableSSL == true | |
649 | -> mainCfg.sslServerCert != null | |
650 | && mainCfg.sslServerKey != null; | |
651 | message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } | |
652 | ]; | |
653 | ||
654 | 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); | |
655 | ||
656 | environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; | |
657 | ||
658 | services.httpdInte.phpOptions = | |
659 | '' | |
660 | ; Needed for PHP's mail() function. | |
661 | sendmail_path = sendmail -t -i | |
662 | '' + optionalString (!isNull config.time.timeZone) '' | |
663 | ||
664 | ; Apparently PHP doesn't use $TZ. | |
665 | date.timezone = "${config.time.timeZone}" | |
666 | ''; | |
667 | ||
668 | systemd.services.httpdInte = | |
669 | { description = "Apache HTTPD"; | |
670 | ||
671 | wantedBy = [ "multi-user.target" ]; | |
672 | wants = [ "keys.target" ]; | |
673 | after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ]; | |
674 | ||
675 | path = | |
676 | [ httpd pkgs.coreutils pkgs.gnugrep ] | |
677 | ++ # Needed for PHP's mail() function. !!! Probably the | |
678 | # ssmtp module should export the path to sendmail in | |
679 | # some way. | |
680 | optional config.networking.defaultMailServer.directDelivery pkgs.ssmtp | |
681 | ++ concatMap (svc: svc.extraServerPath) allSubservices; | |
682 | ||
683 | environment = | |
684 | optionalAttrs enablePHP { PHPRC = phpIni; } | |
685 | // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; } | |
686 | // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices)); | |
687 | ||
688 | preStart = | |
689 | '' | |
690 | mkdir -m 0750 -p ${mainCfg.stateDir} | |
691 | [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir} | |
692 | ${optionalString version24 '' | |
693 | mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" | |
694 | [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" | |
695 | ''} | |
696 | mkdir -m 0700 -p ${mainCfg.logDir} | |
697 | ||
698 | # Get rid of old semaphores. These tend to accumulate across | |
699 | # server restarts, eventually preventing it from restarting | |
700 | # successfully. | |
701 | for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do | |
702 | ${pkgs.utillinux}/bin/ipcrm -s $i | |
703 | done | |
704 | ||
705 | # Run the startup hooks for the subservices. | |
706 | for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do | |
707 | echo Running Apache startup hook $i... | |
708 | $i | |
709 | done | |
710 | ''; | |
711 | ||
712 | serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}"; | |
713 | serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop"; | |
714 | serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful"; | |
715 | serviceConfig.Type = "forking"; | |
716 | serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid"; | |
717 | serviceConfig.Restart = "always"; | |
718 | serviceConfig.RestartSec = "5s"; | |
719 | }; | |
720 | ||
721 | }; | |
722 | } |