]>
Commit | Line | Data |
---|---|---|
581c499c IB |
1 | # to help backporting this builder should stay as close as possible to |
2 | # nixos/modules/services/web-servers/apache-httpd/default.nix | |
3 | { httpdName, withUsers ? true }: | |
273e2c61 IB |
4 | { config, lib, pkgs, ... }: |
5 | ||
6 | with lib; | |
7 | ||
8 | let | |
9 | ||
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 | ||
339 | in | |
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 | } |