diff options
Diffstat (limited to 'modules/websites/httpd-service-builder.nix')
-rw-r--r-- | modules/websites/httpd-service-builder.nix | 735 |
1 files changed, 0 insertions, 735 deletions
diff --git a/modules/websites/httpd-service-builder.nix b/modules/websites/httpd-service-builder.nix deleted file mode 100644 index 1f7488d..0000000 --- a/modules/websites/httpd-service-builder.nix +++ /dev/null | |||
@@ -1,735 +0,0 @@ | |||
1 | # to help backporting this builder should stay as close as possible to | ||
2 | # nixos/modules/services/web-servers/apache-httpd/default.nix | ||
3 | { httpdName, withUsers ? true }: | ||
4 | { config, lib, pkgs, ... }: | ||
5 | |||
6 | with lib; | ||
7 | |||
8 | let | ||
9 | |||
10 | cfg = config.services.httpd."${httpdName}"; | ||
11 | |||
12 | runtimeDir = "/run/httpd_${httpdName}"; | ||
13 | |||
14 | pkg = cfg.package.out; | ||
15 | |||
16 | httpdConf = cfg.configFile; | ||
17 | |||
18 | php = cfg.phpPackage.override { apacheHttpd = pkg.dev; /* otherwise it only gets .out */ }; | ||
19 | |||
20 | phpMajorVersion = lib.versions.major (lib.getVersion php); | ||
21 | |||
22 | mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = pkg; }; | ||
23 | |||
24 | vhosts = attrValues cfg.virtualHosts; | ||
25 | |||
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 | ); | ||
32 | |||
33 | listenInfo = unique (concatMap mkListenInfo vhosts); | ||
34 | |||
35 | enableHttp2 = any (vhost: vhost.http2) vhosts; | ||
36 | enableSSL = any (listen: listen.ssl) listenInfo; | ||
37 | enableUserDir = any (vhost: vhost.enableUserDir) vhosts; | ||
38 | |||
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}" | ||
48 | ] | ||
49 | ++ (if cfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ]) | ||
50 | ++ optional enableHttp2 "http2" | ||
51 | ++ optional enableSSL "ssl" | ||
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; | ||
57 | |||
58 | loggingConf = (if cfg.logFormat != "none" then '' | ||
59 | ErrorLog ${cfg.logDir}/error.log | ||
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 | |||
68 | CustomLog ${cfg.logDir}/access.log ${cfg.logFormat} | ||
69 | '' else '' | ||
70 | ErrorLog /dev/null | ||
71 | ''); | ||
72 | |||
73 | |||
74 | browserHacks = '' | ||
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> | ||
86 | ''; | ||
87 | |||
88 | |||
89 | sslConf = '' | ||
90 | <IfModule mod_ssl.c> | ||
91 | SSLSessionCache shmcb:${runtimeDir}/ssl_scache(512000) | ||
92 | |||
93 | Mutex posixsem | ||
94 | |||
95 | SSLRandomSeed startup builtin | ||
96 | SSLRandomSeed connect builtin | ||
97 | |||
98 | SSLProtocol ${cfg.sslProtocols} | ||
99 | SSLCipherSuite ${cfg.sslCiphers} | ||
100 | SSLHonorCipherOrder on | ||
101 | </IfModule> | ||
102 | ''; | ||
103 | |||
104 | |||
105 | mimeConf = '' | ||
106 | TypesConfig ${pkg}/conf/mime.types | ||
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> | ||
113 | MIMEMagicFile ${pkg}/conf/magic | ||
114 | </IfModule> | ||
115 | ''; | ||
116 | |||
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} | ||
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 | ; | ||
259 | |||
260 | |||
261 | confFile = pkgs.writeText "httpd.conf" '' | ||
262 | |||
263 | ServerRoot ${pkg} | ||
264 | ServerName ${config.networking.hostName} | ||
265 | DefaultRuntimeDir ${runtimeDir}/runtime | ||
266 | |||
267 | PidFile ${runtimeDir}/httpd.pid | ||
268 | |||
269 | ${optionalString (cfg.multiProcessingModule != "prefork") '' | ||
270 | # mod_cgid requires this. | ||
271 | ScriptSock ${runtimeDir}/cgisock | ||
272 | ''} | ||
273 | |||
274 | <IfModule prefork.c> | ||
275 | MaxClients ${toString cfg.maxClients} | ||
276 | MaxRequestsPerChild ${toString cfg.maxRequestsPerChild} | ||
277 | </IfModule> | ||
278 | |||
279 | ${let | ||
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 | ||
283 | } | ||
284 | |||
285 | User ${cfg.user} | ||
286 | Group ${cfg.group} | ||
287 | |||
288 | ${let | ||
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)) | ||
295 | } | ||
296 | |||
297 | AddHandler type-map var | ||
298 | |||
299 | <Files ~ "^\.ht"> | ||
300 | Require all denied | ||
301 | </Files> | ||
302 | |||
303 | ${mimeConf} | ||
304 | ${loggingConf} | ||
305 | ${browserHacks} | ||
306 | |||
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 | ||
311 | |||
312 | TraceEnable off | ||
313 | |||
314 | ${sslConf} | ||
315 | |||
316 | # Fascist default - deny access to everything. | ||
317 | <Directory /> | ||
318 | Options FollowSymLinks | ||
319 | AllowOverride None | ||
320 | Require all denied | ||
321 | </Directory> | ||
322 | |||
323 | ${cfg.extraConfig} | ||
324 | |||
325 | ${concatMapStringsSep "\n" mkVHostConf vhosts} | ||
326 | ''; | ||
327 | |||
328 | # Generate the PHP configuration file. Should probably be factored | ||
329 | # out into a separate module. | ||
330 | phpIni = pkgs.runCommand "php.ini" | ||
331 | { options = cfg.phpOptions; | ||
332 | preferLocalBuild = true; | ||
333 | } | ||
334 | '' | ||
335 | cat ${php}/etc/php.ini > $out | ||
336 | echo "$options" >> $out | ||
337 | ''; | ||
338 | |||
339 | in | ||
340 | |||
341 | |||
342 | { | ||
343 | |||
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 | ||
365 | |||
366 | options = { | ||
367 | |||
368 | services.httpd."${httpdName}" = { | ||
369 | |||
370 | enable = mkEnableOption "the Apache HTTP Server"; | ||
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 = '' | ||
396 | Configuration lines appended to the generated Apache | ||
397 | configuration file. Note that this mechanism will not work | ||
398 | when <option>configFile</option> is overridden. | ||
399 | ''; | ||
400 | }; | ||
401 | |||
402 | extraModules = mkOption { | ||
403 | type = types.listOf types.unspecified; | ||
404 | default = []; | ||
405 | example = literalExample '' | ||
406 | [ | ||
407 | "proxy_connect" | ||
408 | { name = "jk"; path = "''${pkgs.tomcat_connectors}/modules/mod_jk.so"; } | ||
409 | ] | ||
410 | ''; | ||
411 | description = '' | ||
412 | Additional Apache modules to be used. These can be | ||
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 | |||
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 | |||
436 | logPerVirtualHost = mkOption { | ||
437 | type = types.bool; | ||
438 | default = true; | ||
439 | description = '' | ||
440 | If enabled, each virtual host gets its own | ||
441 | <filename>access.log</filename> and | ||
442 | <filename>error.log</filename>, namely suffixed by the | ||
443 | <option>hostName</option> of the virtual host. | ||
444 | ''; | ||
445 | }; | ||
446 | |||
447 | user = mkOption { | ||
448 | type = types.str; | ||
449 | default = "wwwrun"; | ||
450 | description = '' | ||
451 | User account under which httpd runs. | ||
452 | ''; | ||
453 | }; | ||
454 | |||
455 | group = mkOption { | ||
456 | type = types.str; | ||
457 | default = "wwwrun"; | ||
458 | description = '' | ||
459 | Group under which httpd runs. | ||
460 | ''; | ||
461 | }; | ||
462 | |||
463 | logDir = mkOption { | ||
464 | type = types.path; | ||
465 | default = "/var/log/httpd"; | ||
466 | description = '' | ||
467 | Directory for Apache's log files. It is created automatically. | ||
468 | ''; | ||
469 | }; | ||
470 | |||
471 | virtualHosts = mkOption { | ||
472 | type = with types; attrsOf (submodule (import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix>)); | ||
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"; | ||
487 | }; | ||
488 | } | ||
489 | ''; | ||
490 | description = '' | ||
491 | Specification of the virtual hosts served by Apache. Each | ||
492 | element should be an attribute set specifying the | ||
493 | configuration of the virtual host. | ||
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 | ''; | ||
531 | description = '' | ||
532 | Options appended to the PHP configuration file <filename>php.ini</filename>. | ||
533 | ''; | ||
534 | }; | ||
535 | |||
536 | multiProcessingModule = mkOption { | ||
537 | type = types.enum [ "event" "prefork" "worker" ]; | ||
538 | default = "prefork"; | ||
539 | example = "worker"; | ||
540 | description = | ||
541 | '' | ||
542 | Multi-processing module to be used by Apache. Available | ||
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; | ||
564 | description = '' | ||
565 | Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited. | ||
566 | ''; | ||
567 | }; | ||
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; | ||
577 | default = "All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1"; | ||
578 | example = "All -SSLv2 -SSLv3"; | ||
579 | description = "Allowed SSL/TLS protocol versions."; | ||
580 | }; | ||
581 | }; | ||
582 | |||
583 | }; | ||
584 | |||
585 | # implementation | ||
586 | |||
587 | config = mkIf cfg.enable { | ||
588 | |||
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 | ]; | ||
614 | |||
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); | ||
619 | |||
620 | users.users = optionalAttrs (withUsers && cfg.user == "wwwrun") { | ||
621 | wwwrun = { | ||
622 | group = cfg.group; | ||
623 | description = "Apache httpd user"; | ||
624 | uid = config.ids.uids.wwwrun; | ||
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); | ||
640 | |||
641 | environment.systemPackages = [ pkg ]; | ||
642 | |||
643 | # required for "apachectl configtest" | ||
644 | environment.etc."httpd/httpd_${httpdName}.conf".source = httpdConf; | ||
645 | |||
646 | services.httpd."${httpdName}" = { phpOptions = | ||
647 | '' | ||
648 | ; Needed for PHP's mail() function. | ||
649 | sendmail_path = sendmail -t -i | ||
650 | |||
651 | ; Don't advertise PHP | ||
652 | expose_php = off | ||
653 | '' + optionalString (config.time.timeZone != null) '' | ||
654 | |||
655 | ; Apparently PHP doesn't use $TZ. | ||
656 | date.timezone = "${config.time.timeZone}" | ||
657 | ''; | ||
658 | |||
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 | |||
691 | systemd.services."httpd${httpdName}" = | ||
692 | let | ||
693 | vhostsACME = filter (hostOpts: hostOpts.enableACME) vhosts; | ||
694 | in | ||
695 | { description = "Apache HTTPD"; | ||
696 | |||
697 | wantedBy = [ "multi-user.target" ]; | ||
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; | ||
700 | |||
701 | path = | ||
702 | [ pkg pkgs.coreutils pkgs.gnugrep ] | ||
703 | ++ optional cfg.enablePHP pkgs.system-sendmail; # Needed for PHP's mail() function. | ||
704 | |||
705 | environment = | ||
706 | optionalAttrs cfg.enablePHP { PHPRC = phpIni; } | ||
707 | // optionalAttrs cfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; }; | ||
708 | |||
709 | preStart = | ||
710 | '' | ||
711 | # Get rid of old semaphores. These tend to accumulate across | ||
712 | # server restarts, eventually preventing it from restarting | ||
713 | # successfully. | ||
714 | for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${cfg.user} ' | cut -f2 -d ' '); do | ||
715 | ${pkgs.utillinux}/bin/ipcrm -s $i | ||
716 | done | ||
717 | ''; | ||
718 | |||
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 | }; | ||
732 | }; | ||
733 | |||
734 | }; | ||
735 | } | ||