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