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