]> git.immae.eu Git - perso/Immae/Config/Nix/NUR.git/blob - modules/websites/httpd-service-builder.nix
Upgrade nixos
[perso/Immae/Config/Nix/NUR.git] / modules / websites / httpd-service-builder.nix
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 mainCfg = config.services.httpd."${httpdName}";
11
12 httpd = mainCfg.package.out;
13
14 httpdConf = mainCfg.configFile;
15
16 php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
17
18 phpMajorVersion = head (splitString "." php.version);
19
20 mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
21
22 defaultListen = cfg: if cfg.enableSSL
23 then [{ip = "*"; port = 443;}]
24 else [{ip = "*"; port = 80;}];
25
26 getListen = cfg:
27 if cfg.listen == []
28 then defaultListen cfg
29 else cfg.listen;
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" "authn_core"
111
112 # Authorization: is the user allowed access?
113 "authz_user" "authz_groupfile" "authz_host" "authz_core"
114
115 # Other modules.
116 "ext_filter" "include" "log_config" "env" "mime_magic"
117 "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
118 "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
119 "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
120 "userdir" "alias" "rewrite" "proxy" "proxy_http"
121 "unixd" "cache" "cache_disk" "slotmem_shm" "socache_shmcb"
122 "mpm_${mainCfg.multiProcessingModule}"
123
124 # For compatibility with old configurations, the new module mod_access_compat is provided.
125 "access_compat"
126 ]
127 ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
128 ++ optional enableSSL "ssl"
129 ++ extraApacheModules;
130
131
132 allDenied = "Require all denied";
133 allGranted = "Require all granted";
134
135
136 loggingConf = (if mainCfg.logFormat != "none" then ''
137 ErrorLog ${mainCfg.logDir}/error.log
138
139 LogLevel notice
140
141 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
142 LogFormat "%h %l %u %t \"%r\" %>s %b" common
143 LogFormat "%{Referer}i -> %U" referer
144 LogFormat "%{User-agent}i" agent
145
146 CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
147 '' else ''
148 ErrorLog /dev/null
149 '');
150
151
152 browserHacks = ''
153 BrowserMatch "Mozilla/2" nokeepalive
154 BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
155 BrowserMatch "RealPlayer 4\.0" force-response-1.0
156 BrowserMatch "Java/1\.0" force-response-1.0
157 BrowserMatch "JDK/1\.0" force-response-1.0
158 BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
159 BrowserMatch "^WebDrive" redirect-carefully
160 BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
161 BrowserMatch "^gnome-vfs" redirect-carefully
162 '';
163
164
165 sslConf = ''
166 SSLSessionCache shmcb:${mainCfg.stateDir}/ssl_scache(512000)
167
168 Mutex posixsem
169
170 SSLRandomSeed startup builtin
171 SSLRandomSeed connect builtin
172
173 SSLProtocol ${mainCfg.sslProtocols}
174 SSLCipherSuite ${mainCfg.sslCiphers}
175 SSLHonorCipherOrder on
176 '';
177
178
179 mimeConf = ''
180 TypesConfig ${httpd}/conf/mime.types
181
182 AddType application/x-x509-ca-cert .crt
183 AddType application/x-pkcs7-crl .crl
184 AddType application/x-httpd-php .php .phtml
185
186 <IfModule mod_mime_magic.c>
187 MIMEMagicFile ${httpd}/conf/magic
188 </IfModule>
189 '';
190
191
192 perServerConf = isMainServer: cfg: let
193
194 serverInfo = makeServerInfo cfg;
195
196 subservices = callSubservices serverInfo cfg.extraSubservices;
197
198 maybeDocumentRoot = fold (svc: acc:
199 if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
200 ) null ([ cfg ] ++ subservices);
201
202 documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
203 pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
204
205 documentRootConf = ''
206 DocumentRoot "${documentRoot}"
207
208 <Directory "${documentRoot}">
209 Options Indexes FollowSymLinks
210 AllowOverride None
211 ${allGranted}
212 </Directory>
213 '';
214
215 robotsTxt =
216 concatStringsSep "\n" (filter (x: x != "") (
217 # If this is a vhost, the include the entries for the main server as well.
218 (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices)
219 ++ [cfg.robotsEntries]
220 ++ (map (svc: svc.robotsEntries) subservices)));
221
222 in ''
223 ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
224
225 ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
226
227 ${if cfg.sslServerCert != null then ''
228 SSLCertificateFile ${cfg.sslServerCert}
229 SSLCertificateKeyFile ${cfg.sslServerKey}
230 ${if cfg.sslServerChain != null then ''
231 SSLCertificateChainFile ${cfg.sslServerChain}
232 '' else ""}
233 '' else ""}
234
235 ${if cfg.enableSSL then ''
236 SSLEngine on
237 '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
238 ''
239 SSLEngine off
240 '' else ""}
241
242 ${if isMainServer || cfg.adminAddr != null then ''
243 ServerAdmin ${cfg.adminAddr}
244 '' else ""}
245
246 ${if !isMainServer && mainCfg.logPerVirtualHost then ''
247 ErrorLog ${mainCfg.logDir}/error-${cfg.hostName}.log
248 CustomLog ${mainCfg.logDir}/access-${cfg.hostName}.log ${cfg.logFormat}
249 '' else ""}
250
251 ${optionalString (robotsTxt != "") ''
252 Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt}
253 ''}
254
255 ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
256
257 ${if cfg.enableUserDir then ''
258
259 UserDir public_html
260 UserDir disabled root
261
262 <Directory "/home/*/public_html">
263 AllowOverride FileInfo AuthConfig Limit Indexes
264 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
265 <Limit GET POST OPTIONS>
266 ${allGranted}
267 </Limit>
268 <LimitExcept GET POST OPTIONS>
269 ${allDenied}
270 </LimitExcept>
271 </Directory>
272
273 '' else ""}
274
275 ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
276 RedirectPermanent / ${cfg.globalRedirect}
277 '' else ""}
278
279 ${
280 let makeFileConf = elem: ''
281 Alias ${elem.urlPath} ${elem.file}
282 '';
283 in concatMapStrings makeFileConf cfg.servedFiles
284 }
285
286 ${
287 let makeDirConf = elem: ''
288 Alias ${elem.urlPath} ${elem.dir}/
289 <Directory ${elem.dir}>
290 Options +Indexes
291 ${allGranted}
292 AllowOverride All
293 </Directory>
294 '';
295 in concatMapStrings makeDirConf cfg.servedDirs
296 }
297
298 ${concatMapStrings (svc: svc.extraConfig) subservices}
299
300 ${cfg.extraConfig}
301 '';
302
303
304 confFile = pkgs.writeText "httpd.conf" ''
305
306 ServerRoot ${httpd}
307
308 DefaultRuntimeDir ${mainCfg.stateDir}/runtime
309
310 PidFile ${mainCfg.stateDir}/httpd.pid
311
312 ${optionalString (mainCfg.multiProcessingModule != "prefork") ''
313 # mod_cgid requires this.
314 ScriptSock ${mainCfg.stateDir}/cgisock
315 ''}
316
317 <IfModule prefork.c>
318 MaxClients ${toString mainCfg.maxClients}
319 MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
320 </IfModule>
321
322 ${let
323 listen = concatMap getListen allHosts;
324 toStr = listen: "Listen ${listenToString listen}\n";
325 uniqueListen = uniqList {inputList = map toStr listen;};
326 in concatStrings uniqueListen
327 }
328
329 User ${mainCfg.user}
330 Group ${mainCfg.group}
331
332 ${let
333 load = {name, path}: "LoadModule ${name}_module ${path}\n";
334 allModules =
335 concatMap (svc: svc.extraModulesPre) allSubservices
336 ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
337 ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
338 ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
339 ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
340 ++ concatMap (svc: svc.extraModules) allSubservices
341 ++ extraForeignModules;
342 in concatMapStrings load (unique allModules)
343 }
344
345 AddHandler type-map var
346
347 <Files ~ "^\.ht">
348 ${allDenied}
349 </Files>
350
351 ${mimeConf}
352 ${loggingConf}
353 ${browserHacks}
354
355 Include ${httpd}/conf/extra/httpd-default.conf
356 Include ${httpd}/conf/extra/httpd-autoindex.conf
357 Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
358 Include ${httpd}/conf/extra/httpd-languages.conf
359
360 TraceEnable off
361
362 ${if enableSSL then sslConf else ""}
363
364 # Fascist default - deny access to everything.
365 <Directory />
366 Options FollowSymLinks
367 AllowOverride None
368 ${allDenied}
369 </Directory>
370
371 # Generate directives for the main server.
372 ${perServerConf true mainCfg}
373
374 ${let
375 makeVirtualHost = vhost: ''
376 <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
377 ${perServerConf false vhost}
378 </VirtualHost>
379 '';
380 in concatMapStrings makeVirtualHost mainCfg.virtualHosts
381 }
382 '';
383
384
385 enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
386
387 enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
388
389
390 # Generate the PHP configuration file. Should probably be factored
391 # out into a separate module.
392 phpIni = pkgs.runCommand "php.ini"
393 { options = concatStringsSep "\n"
394 ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
395 preferLocalBuild = true;
396 }
397 ''
398 cat ${php}/etc/php.ini > $out
399 echo "$options" >> $out
400 '';
401
402 in
403
404
405 {
406
407 ###### interface
408
409 options = {
410
411 services.httpd."${httpdName}" = {
412
413 enable = mkOption {
414 type = types.bool;
415 default = false;
416 description = "Whether to enable the Apache HTTP Server.";
417 };
418
419 package = mkOption {
420 type = types.package;
421 default = pkgs.apacheHttpd;
422 defaultText = "pkgs.apacheHttpd";
423 description = ''
424 Overridable attribute of the Apache HTTP Server package to use.
425 '';
426 };
427
428 configFile = mkOption {
429 type = types.path;
430 default = confFile;
431 defaultText = "confFile";
432 example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
433 description = ''
434 Override the configuration file used by Apache. By default,
435 NixOS generates one automatically.
436 '';
437 };
438
439 extraConfig = mkOption {
440 type = types.lines;
441 default = "";
442 description = ''
443 Cnfiguration lines appended to the generated Apache
444 configuration file. Note that this mechanism may not work
445 when <option>configFile</option> is overridden.
446 '';
447 };
448
449 extraModules = mkOption {
450 type = types.listOf types.unspecified;
451 default = [];
452 example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
453 description = ''
454 Additional Apache modules to be used. These can be
455 specified as a string in the case of modules distributed
456 with Apache, or as an attribute set specifying the
457 <varname>name</varname> and <varname>path</varname> of the
458 module.
459 '';
460 };
461
462 logPerVirtualHost = mkOption {
463 type = types.bool;
464 default = false;
465 description = ''
466 If enabled, each virtual host gets its own
467 <filename>access.log</filename> and
468 <filename>error.log</filename>, namely suffixed by the
469 <option>hostName</option> of the virtual host.
470 '';
471 };
472
473 user = mkOption {
474 type = types.str;
475 default = "wwwrun";
476 description = ''
477 User account under which httpd runs. The account is created
478 automatically if it doesn't exist.
479 '';
480 };
481
482 group = mkOption {
483 type = types.str;
484 default = "wwwrun";
485 description = ''
486 Group under which httpd runs. The account is created
487 automatically if it doesn't exist.
488 '';
489 };
490
491 logDir = mkOption {
492 type = types.path;
493 default = "/var/log/httpd";
494 description = ''
495 Directory for Apache's log files. It is created automatically.
496 '';
497 };
498
499 stateDir = mkOption {
500 type = types.path;
501 default = "/run/httpd";
502 description = ''
503 Directory for Apache's transient runtime state (such as PID
504 files). It is created automatically. Note that the default,
505 <filename>/run/httpd</filename>, is deleted at boot time.
506 '';
507 };
508
509 virtualHosts = mkOption {
510 type = types.listOf (types.submodule (
511 { options = import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix> {
512 inherit lib;
513 forMainServer = false;
514 };
515 }));
516 default = [];
517 example = [
518 { hostName = "foo";
519 documentRoot = "/data/webroot-foo";
520 }
521 { hostName = "bar";
522 documentRoot = "/data/webroot-bar";
523 }
524 ];
525 description = ''
526 Specification of the virtual hosts served by Apache. Each
527 element should be an attribute set specifying the
528 configuration of the virtual host. The available options
529 are the non-global options permissible for the main host.
530 '';
531 };
532
533 enableMellon = mkOption {
534 type = types.bool;
535 default = false;
536 description = "Whether to enable the mod_auth_mellon module.";
537 };
538
539 enablePHP = mkOption {
540 type = types.bool;
541 default = false;
542 description = "Whether to enable the PHP module.";
543 };
544
545 phpPackage = mkOption {
546 type = types.package;
547 default = pkgs.php;
548 defaultText = "pkgs.php";
549 description = ''
550 Overridable attribute of the PHP package to use.
551 '';
552 };
553
554 enablePerl = mkOption {
555 type = types.bool;
556 default = false;
557 description = "Whether to enable the Perl module (mod_perl).";
558 };
559
560 phpOptions = mkOption {
561 type = types.lines;
562 default = "";
563 example =
564 ''
565 date.timezone = "CET"
566 '';
567 description =
568 "Options appended to the PHP configuration file <filename>php.ini</filename>.";
569 };
570
571 multiProcessingModule = mkOption {
572 type = types.str;
573 default = "prefork";
574 example = "worker";
575 description =
576 ''
577 Multi-processing module to be used by Apache. Available
578 modules are <literal>prefork</literal> (the default;
579 handles each request in a separate child process),
580 <literal>worker</literal> (hybrid approach that starts a
581 number of child processes each running a number of
582 threads) and <literal>event</literal> (a recent variant of
583 <literal>worker</literal> that handles persistent
584 connections more efficiently).
585 '';
586 };
587
588 maxClients = mkOption {
589 type = types.int;
590 default = 150;
591 example = 8;
592 description = "Maximum number of httpd processes (prefork)";
593 };
594
595 maxRequestsPerChild = mkOption {
596 type = types.int;
597 default = 0;
598 example = 500;
599 description =
600 "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
601 };
602
603 sslCiphers = mkOption {
604 type = types.str;
605 default = "HIGH:!aNULL:!MD5:!EXP";
606 description = "Cipher Suite available for negotiation in SSL proxy handshake.";
607 };
608
609 sslProtocols = mkOption {
610 type = types.str;
611 default = "All -SSLv2 -SSLv3 -TLSv1";
612 example = "All -SSLv2 -SSLv3";
613 description = "Allowed SSL/TLS protocol versions.";
614 };
615 }
616
617 # Include the options shared between the main server and virtual hosts.
618 // (import <nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix> {
619 inherit lib;
620 forMainServer = true;
621 });
622
623 };
624
625
626 ###### implementation
627
628 config = mkIf config.services.httpd."${httpdName}".enable {
629
630 assertions = [ { assertion = mainCfg.enableSSL == true
631 -> mainCfg.sslServerCert != null
632 && mainCfg.sslServerKey != null;
633 message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
634 ];
635
636 warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
637
638 users.users = optionalAttrs (withUsers && mainCfg.user == "wwwrun") (singleton
639 { name = "wwwrun";
640 group = mainCfg.group;
641 description = "Apache httpd user";
642 uid = config.ids.uids.wwwrun;
643 });
644
645 users.groups = optionalAttrs (withUsers && mainCfg.group == "wwwrun") (singleton
646 { name = "wwwrun";
647 gid = config.ids.gids.wwwrun;
648 });
649
650 environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
651
652 services.httpd."${httpdName}".phpOptions =
653 ''
654 ; Needed for PHP's mail() function.
655 sendmail_path = sendmail -t -i
656
657 ; Don't advertise PHP
658 expose_php = off
659 '' + optionalString (config.time.timeZone != null) ''
660
661 ; Apparently PHP doesn't use $TZ.
662 date.timezone = "${config.time.timeZone}"
663 '';
664
665 systemd.services."httpd${httpdName}" =
666 { description = "Apache HTTPD";
667
668 wantedBy = [ "multi-user.target" ];
669 wants = [ "keys.target" ];
670 after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
671
672 path =
673 [ httpd pkgs.coreutils pkgs.gnugrep ]
674 ++ optional enablePHP pkgs.system-sendmail # Needed for PHP's mail() function.
675 ++ concatMap (svc: svc.extraServerPath) allSubservices;
676
677 environment =
678 optionalAttrs enablePHP { PHPRC = phpIni; }
679 // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; }
680 // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
681
682 preStart =
683 ''
684 mkdir -m 0750 -p ${mainCfg.stateDir}
685 [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
686
687 mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
688 [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
689
690 mkdir -m 0700 -p ${mainCfg.logDir}
691
692 # Get rid of old semaphores. These tend to accumulate across
693 # server restarts, eventually preventing it from restarting
694 # successfully.
695 for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
696 ${pkgs.utillinux}/bin/ipcrm -s $i
697 done
698
699 # Run the startup hooks for the subservices.
700 for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
701 echo Running Apache startup hook $i...
702 $i
703 done
704 '';
705
706 serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
707 serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
708 serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful";
709 serviceConfig.Type = "forking";
710 serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid";
711 serviceConfig.Restart = "always";
712 serviceConfig.RestartSec = "5s";
713 };
714
715 };
716 }