aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/default.nix2
-rw-r--r--modules/private/default.nix6
-rw-r--r--modules/private/httpd-service-builder.nix (renamed from nixops/modules/websites/apache/httpd_tools.nix)19
-rw-r--r--nixops/modules/websites/apache/httpd_inte.nix731
-rw-r--r--nixops/modules/websites/apache/httpd_prod.nix731
-rw-r--r--nixops/modules/websites/default.nix8
6 files changed, 18 insertions, 1479 deletions
diff --git a/modules/default.nix b/modules/default.nix
index 2c993c5..6c49160 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -8,4 +8,4 @@
8 mastodon = ./webapps/mastodon.nix; 8 mastodon = ./webapps/mastodon.nix;
9 mediagoblin = ./webapps/mediagoblin.nix; 9 mediagoblin = ./webapps/mediagoblin.nix;
10 peertube = ./webapps/peertube.nix; 10 peertube = ./webapps/peertube.nix;
11} 11} // (if builtins.pathExists ./private then import ./private else {})
diff --git a/modules/private/default.nix b/modules/private/default.nix
new file mode 100644
index 0000000..ba46374
--- /dev/null
+++ b/modules/private/default.nix
@@ -0,0 +1,6 @@
1{
2 # adatped from nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
3 httpdProd = import ./httpd-service-builder.nix { httpdName = "Prod"; withUsers = false; };
4 httpdInte = import ./httpd-service-builder.nix { httpdName = "Inte"; withUsers = false; };
5 httpdTools = import ./httpd-service-builder.nix { httpdName = "Tools"; withUsers = true; };
6}
diff --git a/nixops/modules/websites/apache/httpd_tools.nix b/modules/private/httpd-service-builder.nix
index c48d0d2..0f0fe22 100644
--- a/nixops/modules/websites/apache/httpd_tools.nix
+++ b/modules/private/httpd-service-builder.nix
@@ -1,10 +1,13 @@
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 }:
1{ config, lib, pkgs, ... }: 4{ config, lib, pkgs, ... }:
2 5
3with lib; 6with lib;
4 7
5let 8let
6 9
7 mainCfg = config.services.httpdTools; 10 mainCfg = config.services."httpd${httpdName}";
8 11
9 httpd = mainCfg.package.out; 12 httpd = mainCfg.package.out;
10 13
@@ -435,7 +438,7 @@ in
435 438
436 options = { 439 options = {
437 440
438 services.httpdTools = { 441 services."httpd${httpdName}" = {
439 442
440 enable = mkOption { 443 enable = mkOption {
441 type = types.bool; 444 type = types.bool;
@@ -642,7 +645,7 @@ in
642 } 645 }
643 646
644 # Include the options shared between the main server and virtual hosts. 647 # Include the options shared between the main server and virtual hosts.
645 // (import ./per-server-options.nix { 648 // (import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix> {
646 inherit lib; 649 inherit lib;
647 forMainServer = true; 650 forMainServer = true;
648 }); 651 });
@@ -652,7 +655,7 @@ in
652 655
653 ###### implementation 656 ###### implementation
654 657
655 config = mkIf config.services.httpdTools.enable { 658 config = mkIf config.services."httpd${httpdName}".enable {
656 659
657 assertions = [ { assertion = mainCfg.enableSSL == true 660 assertions = [ { assertion = mainCfg.enableSSL == true
658 -> mainCfg.sslServerCert != null 661 -> mainCfg.sslServerCert != null
@@ -662,21 +665,21 @@ in
662 665
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); 666 warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts);
664 667
665 users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton 668 users.users = optionalAttrs (withUsers && mainCfg.user == "wwwrun") (singleton
666 { name = "wwwrun"; 669 { name = "wwwrun";
667 group = mainCfg.group; 670 group = mainCfg.group;
668 description = "Apache httpd user"; 671 description = "Apache httpd user";
669 uid = config.ids.uids.wwwrun; 672 uid = config.ids.uids.wwwrun;
670 }); 673 });
671 674
672 users.groups = optionalAttrs (mainCfg.group == "wwwrun") (singleton 675 users.groups = optionalAttrs (withUsers && mainCfg.group == "wwwrun") (singleton
673 { name = "wwwrun"; 676 { name = "wwwrun";
674 gid = config.ids.gids.wwwrun; 677 gid = config.ids.gids.wwwrun;
675 }); 678 });
676 679
677 environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; 680 environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
678 681
679 services.httpdTools.phpOptions = 682 services."httpd${httpdName}".phpOptions =
680 '' 683 ''
681 ; Needed for PHP's mail() function. 684 ; Needed for PHP's mail() function.
682 sendmail_path = sendmail -t -i 685 sendmail_path = sendmail -t -i
@@ -689,7 +692,7 @@ in
689 date.timezone = "${config.time.timeZone}" 692 date.timezone = "${config.time.timeZone}"
690 ''; 693 '';
691 694
692 systemd.services.httpdTools = 695 systemd.services."httpd${httpdName}" =
693 { description = "Apache HTTPD"; 696 { description = "Apache HTTPD";
694 697
695 wantedBy = [ "multi-user.target" ]; 698 wantedBy = [ "multi-user.target" ];
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
3with lib;
4
5let
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
429in
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
3with lib;
4
5let
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
429in
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/default.nix b/nixops/modules/websites/default.nix
index 8bbb344..627d01a 100644
--- a/nixops/modules/websites/default.nix
+++ b/nixops/modules/websites/default.nix
@@ -131,14 +131,6 @@ in
131 ./tools/diaspora.nix 131 ./tools/diaspora.nix
132 ./tools/ether.nix 132 ./tools/ether.nix
133 ./tools/peertube.nix 133 ./tools/peertube.nix
134 # built using:
135 # sed -e "s/services\.httpd/services\.httpdProd/g" .nix-defexpr/channels/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
136 # Removed allGranted
137 # And removed users / groups
138 ./apache/httpd_prod.nix
139 ./apache/httpd_inte.nix
140 # except for this one for users/groups
141 ./apache/httpd_tools.nix
142 # Adapted from base phpfpm 134 # Adapted from base phpfpm
143 ./phpfpm 135 ./phpfpm
144 ]; 136 ];