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