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