
Quoting from the splitString docstring: NOTE: this function is not performant and should never be used. This replaces trivial uses of splitString for splitting version strings with the (potentially builtin) splitVersion.
720 lines
22 KiB
Nix
720 lines
22 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
mainCfg = config.services.httpd;
|
|
|
|
httpd = mainCfg.package.out;
|
|
|
|
httpdConf = mainCfg.configFile;
|
|
|
|
php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
|
|
|
|
phpMajorVersion = lib.versions.major (lib.getVersion php);
|
|
|
|
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
|
|
|
|
defaultListen = cfg: if cfg.enableSSL
|
|
then [{ip = "*"; port = 443;}]
|
|
else [{ip = "*"; port = 80;}];
|
|
|
|
getListen = cfg:
|
|
if cfg.listen == []
|
|
then defaultListen cfg
|
|
else cfg.listen;
|
|
|
|
listenToString = l: "${l.ip}:${toString l.port}";
|
|
|
|
extraModules = attrByPath ["extraModules"] [] mainCfg;
|
|
extraForeignModules = filter isAttrs extraModules;
|
|
extraApacheModules = filter isString extraModules;
|
|
|
|
|
|
makeServerInfo = cfg: {
|
|
# Canonical name must not include a trailing slash.
|
|
canonicalNames =
|
|
let defaultPort = (head (defaultListen cfg)).port; in
|
|
map (port:
|
|
(if cfg.enableSSL then "https" else "http") + "://" +
|
|
cfg.hostName +
|
|
(if port != defaultPort then ":${toString port}" else "")
|
|
) (map (x: x.port) (getListen cfg));
|
|
|
|
# Admin address: inherit from the main server if not specified for
|
|
# a virtual host.
|
|
adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr;
|
|
|
|
vhostConfig = cfg;
|
|
serverConfig = mainCfg;
|
|
fullConfig = config; # machine config
|
|
};
|
|
|
|
|
|
allHosts = [mainCfg] ++ mainCfg.virtualHosts;
|
|
|
|
|
|
callSubservices = serverInfo: defs:
|
|
let f = svc:
|
|
let
|
|
svcFunction =
|
|
if svc ? function then svc.function
|
|
# instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix;
|
|
else if svc ? serviceExpression then import (toString svc.serviceExpression)
|
|
else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
|
|
config = (evalModules
|
|
{ modules = [ { options = res.options; config = svc.config or svc; } ];
|
|
check = false;
|
|
}).config;
|
|
defaults = {
|
|
extraConfig = "";
|
|
extraModules = [];
|
|
extraModulesPre = [];
|
|
extraPath = [];
|
|
extraServerPath = [];
|
|
globalEnvVars = [];
|
|
robotsEntries = "";
|
|
startupScript = "";
|
|
enablePHP = false;
|
|
enablePerl = false;
|
|
phpOptions = "";
|
|
options = {};
|
|
documentRoot = null;
|
|
};
|
|
res = defaults // svcFunction { inherit config lib pkgs serverInfo php; };
|
|
in res;
|
|
in map f defs;
|
|
|
|
|
|
# !!! callSubservices is expensive
|
|
subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
|
|
|
|
mainSubservices = subservicesFor mainCfg;
|
|
|
|
allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts;
|
|
|
|
|
|
enableSSL = any (vhost: vhost.enableSSL) allHosts;
|
|
|
|
|
|
# Names of modules from ${httpd}/modules that we want to load.
|
|
apacheModules =
|
|
[ # HTTP authentication mechanisms: basic and digest.
|
|
"auth_basic" "auth_digest"
|
|
|
|
# Authentication: is the user who he claims to be?
|
|
"authn_file" "authn_dbm" "authn_anon" "authn_core"
|
|
|
|
# Authorization: is the user allowed access?
|
|
"authz_user" "authz_groupfile" "authz_host" "authz_core"
|
|
|
|
# Other modules.
|
|
"ext_filter" "include" "log_config" "env" "mime_magic"
|
|
"cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
|
|
"mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
|
|
"vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
|
|
"userdir" "alias" "rewrite" "proxy" "proxy_http"
|
|
"unixd" "cache" "cache_disk" "slotmem_shm" "socache_shmcb"
|
|
"mpm_${mainCfg.multiProcessingModule}"
|
|
|
|
# For compatibility with old configurations, the new module mod_access_compat is provided.
|
|
"access_compat"
|
|
]
|
|
++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
|
|
++ optional enableSSL "ssl"
|
|
++ extraApacheModules;
|
|
|
|
|
|
allDenied = "Require all denied";
|
|
allGranted = "Require all granted";
|
|
|
|
|
|
loggingConf = (if mainCfg.logFormat != "none" then ''
|
|
ErrorLog ${mainCfg.logDir}/error.log
|
|
|
|
LogLevel notice
|
|
|
|
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
|
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
|
LogFormat "%{Referer}i -> %U" referer
|
|
LogFormat "%{User-agent}i" agent
|
|
|
|
CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
|
|
'' else ''
|
|
ErrorLog /dev/null
|
|
'');
|
|
|
|
|
|
browserHacks = ''
|
|
BrowserMatch "Mozilla/2" nokeepalive
|
|
BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
|
|
BrowserMatch "RealPlayer 4\.0" force-response-1.0
|
|
BrowserMatch "Java/1\.0" force-response-1.0
|
|
BrowserMatch "JDK/1\.0" force-response-1.0
|
|
BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
|
|
BrowserMatch "^WebDrive" redirect-carefully
|
|
BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
|
|
BrowserMatch "^gnome-vfs" redirect-carefully
|
|
'';
|
|
|
|
|
|
sslConf = ''
|
|
SSLSessionCache shmcb:${mainCfg.stateDir}/ssl_scache(512000)
|
|
|
|
Mutex posixsem
|
|
|
|
SSLRandomSeed startup builtin
|
|
SSLRandomSeed connect builtin
|
|
|
|
SSLProtocol ${mainCfg.sslProtocols}
|
|
SSLCipherSuite ${mainCfg.sslCiphers}
|
|
SSLHonorCipherOrder on
|
|
'';
|
|
|
|
|
|
mimeConf = ''
|
|
TypesConfig ${httpd}/conf/mime.types
|
|
|
|
AddType application/x-x509-ca-cert .crt
|
|
AddType application/x-pkcs7-crl .crl
|
|
AddType application/x-httpd-php .php .phtml
|
|
|
|
<IfModule mod_mime_magic.c>
|
|
MIMEMagicFile ${httpd}/conf/magic
|
|
</IfModule>
|
|
'';
|
|
|
|
|
|
perServerConf = isMainServer: cfg: let
|
|
|
|
serverInfo = makeServerInfo cfg;
|
|
|
|
subservices = callSubservices serverInfo cfg.extraSubservices;
|
|
|
|
maybeDocumentRoot = fold (svc: acc:
|
|
if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
|
|
) null ([ cfg ] ++ subservices);
|
|
|
|
documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
|
|
pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
|
|
|
|
documentRootConf = ''
|
|
DocumentRoot "${documentRoot}"
|
|
|
|
<Directory "${documentRoot}">
|
|
Options Indexes FollowSymLinks
|
|
AllowOverride None
|
|
${allGranted}
|
|
</Directory>
|
|
'';
|
|
|
|
robotsTxt =
|
|
concatStringsSep "\n" (filter (x: x != "") (
|
|
# If this is a vhost, the include the entries for the main server as well.
|
|
(if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices)
|
|
++ [cfg.robotsEntries]
|
|
++ (map (svc: svc.robotsEntries) subservices)));
|
|
|
|
in ''
|
|
${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
|
|
|
|
${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
|
|
|
|
${if cfg.sslServerCert != null then ''
|
|
SSLCertificateFile ${cfg.sslServerCert}
|
|
SSLCertificateKeyFile ${cfg.sslServerKey}
|
|
${if cfg.sslServerChain != null then ''
|
|
SSLCertificateChainFile ${cfg.sslServerChain}
|
|
'' else ""}
|
|
'' else ""}
|
|
|
|
${if cfg.enableSSL then ''
|
|
SSLEngine on
|
|
'' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
|
|
''
|
|
SSLEngine off
|
|
'' else ""}
|
|
|
|
${if isMainServer || cfg.adminAddr != null then ''
|
|
ServerAdmin ${cfg.adminAddr}
|
|
'' else ""}
|
|
|
|
${if !isMainServer && mainCfg.logPerVirtualHost then ''
|
|
ErrorLog ${mainCfg.logDir}/error-${cfg.hostName}.log
|
|
CustomLog ${mainCfg.logDir}/access-${cfg.hostName}.log ${cfg.logFormat}
|
|
'' else ""}
|
|
|
|
${optionalString (robotsTxt != "") ''
|
|
Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt}
|
|
''}
|
|
|
|
${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
|
|
|
|
${if cfg.enableUserDir then ''
|
|
|
|
UserDir public_html
|
|
UserDir disabled root
|
|
|
|
<Directory "/home/*/public_html">
|
|
AllowOverride FileInfo AuthConfig Limit Indexes
|
|
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
|
|
<Limit GET POST OPTIONS>
|
|
${allGranted}
|
|
</Limit>
|
|
<LimitExcept GET POST OPTIONS>
|
|
${allDenied}
|
|
</LimitExcept>
|
|
</Directory>
|
|
|
|
'' else ""}
|
|
|
|
${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
|
|
RedirectPermanent / ${cfg.globalRedirect}
|
|
'' else ""}
|
|
|
|
${
|
|
let makeFileConf = elem: ''
|
|
Alias ${elem.urlPath} ${elem.file}
|
|
'';
|
|
in concatMapStrings makeFileConf cfg.servedFiles
|
|
}
|
|
|
|
${
|
|
let makeDirConf = elem: ''
|
|
Alias ${elem.urlPath} ${elem.dir}/
|
|
<Directory ${elem.dir}>
|
|
Options +Indexes
|
|
${allGranted}
|
|
AllowOverride All
|
|
</Directory>
|
|
'';
|
|
in concatMapStrings makeDirConf cfg.servedDirs
|
|
}
|
|
|
|
${concatMapStrings (svc: svc.extraConfig) subservices}
|
|
|
|
${cfg.extraConfig}
|
|
'';
|
|
|
|
|
|
confFile = pkgs.writeText "httpd.conf" ''
|
|
|
|
ServerRoot ${httpd}
|
|
|
|
DefaultRuntimeDir ${mainCfg.stateDir}/runtime
|
|
|
|
PidFile ${mainCfg.stateDir}/httpd.pid
|
|
|
|
${optionalString (mainCfg.multiProcessingModule != "prefork") ''
|
|
# mod_cgid requires this.
|
|
ScriptSock ${mainCfg.stateDir}/cgisock
|
|
''}
|
|
|
|
<IfModule prefork.c>
|
|
MaxClients ${toString mainCfg.maxClients}
|
|
MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
|
|
</IfModule>
|
|
|
|
${let
|
|
listen = concatMap getListen allHosts;
|
|
toStr = listen: "Listen ${listenToString listen}\n";
|
|
uniqueListen = uniqList {inputList = map toStr listen;};
|
|
in concatStrings uniqueListen
|
|
}
|
|
|
|
User ${mainCfg.user}
|
|
Group ${mainCfg.group}
|
|
|
|
${let
|
|
load = {name, path}: "LoadModule ${name}_module ${path}\n";
|
|
allModules =
|
|
concatMap (svc: svc.extraModulesPre) allSubservices
|
|
++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
|
|
++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
|
|
++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
|
|
++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
|
|
++ concatMap (svc: svc.extraModules) allSubservices
|
|
++ extraForeignModules;
|
|
in concatMapStrings load (unique allModules)
|
|
}
|
|
|
|
AddHandler type-map var
|
|
|
|
<Files ~ "^\.ht">
|
|
${allDenied}
|
|
</Files>
|
|
|
|
${mimeConf}
|
|
${loggingConf}
|
|
${browserHacks}
|
|
|
|
Include ${httpd}/conf/extra/httpd-default.conf
|
|
Include ${httpd}/conf/extra/httpd-autoindex.conf
|
|
Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
|
|
Include ${httpd}/conf/extra/httpd-languages.conf
|
|
|
|
TraceEnable off
|
|
|
|
${if enableSSL then sslConf else ""}
|
|
|
|
# Fascist default - deny access to everything.
|
|
<Directory />
|
|
Options FollowSymLinks
|
|
AllowOverride None
|
|
${allDenied}
|
|
</Directory>
|
|
|
|
# But do allow access to files in the store so that we don't have
|
|
# to generate <Directory> clauses for every generated file that we
|
|
# want to serve.
|
|
<Directory /nix/store>
|
|
${allGranted}
|
|
</Directory>
|
|
|
|
# Generate directives for the main server.
|
|
${perServerConf true mainCfg}
|
|
|
|
${let
|
|
makeVirtualHost = vhost: ''
|
|
<VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
|
|
${perServerConf false vhost}
|
|
</VirtualHost>
|
|
'';
|
|
in concatMapStrings makeVirtualHost mainCfg.virtualHosts
|
|
}
|
|
'';
|
|
|
|
|
|
enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
|
|
|
|
enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
|
|
|
|
|
|
# Generate the PHP configuration file. Should probably be factored
|
|
# out into a separate module.
|
|
phpIni = pkgs.runCommand "php.ini"
|
|
{ options = concatStringsSep "\n"
|
|
([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
|
|
preferLocalBuild = true;
|
|
}
|
|
''
|
|
cat ${php}/etc/php.ini > $out
|
|
echo "$options" >> $out
|
|
'';
|
|
|
|
in
|
|
|
|
|
|
{
|
|
|
|
###### interface
|
|
|
|
options = {
|
|
|
|
services.httpd = {
|
|
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to enable the Apache HTTP Server.";
|
|
};
|
|
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.apacheHttpd;
|
|
defaultText = "pkgs.apacheHttpd";
|
|
description = ''
|
|
Overridable attribute of the Apache HTTP Server package to use.
|
|
'';
|
|
};
|
|
|
|
configFile = mkOption {
|
|
type = types.path;
|
|
default = confFile;
|
|
defaultText = "confFile";
|
|
example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
|
|
description = ''
|
|
Override the configuration file used by Apache. By default,
|
|
NixOS generates one automatically.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Cnfiguration lines appended to the generated Apache
|
|
configuration file. Note that this mechanism may not work
|
|
when <option>configFile</option> is overridden.
|
|
'';
|
|
};
|
|
|
|
extraModules = mkOption {
|
|
type = types.listOf types.unspecified;
|
|
default = [];
|
|
example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
|
|
description = ''
|
|
Additional Apache modules to be used. These can be
|
|
specified as a string in the case of modules distributed
|
|
with Apache, or as an attribute set specifying the
|
|
<varname>name</varname> and <varname>path</varname> of the
|
|
module.
|
|
'';
|
|
};
|
|
|
|
logPerVirtualHost = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
If enabled, each virtual host gets its own
|
|
<filename>access.log</filename> and
|
|
<filename>error.log</filename>, namely suffixed by the
|
|
<option>hostName</option> of the virtual host.
|
|
'';
|
|
};
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = "wwwrun";
|
|
description = ''
|
|
User account under which httpd runs. The account is created
|
|
automatically if it doesn't exist.
|
|
'';
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
default = "wwwrun";
|
|
description = ''
|
|
Group under which httpd runs. The account is created
|
|
automatically if it doesn't exist.
|
|
'';
|
|
};
|
|
|
|
logDir = mkOption {
|
|
type = types.path;
|
|
default = "/var/log/httpd";
|
|
description = ''
|
|
Directory for Apache's log files. It is created automatically.
|
|
'';
|
|
};
|
|
|
|
stateDir = mkOption {
|
|
type = types.path;
|
|
default = "/run/httpd";
|
|
description = ''
|
|
Directory for Apache's transient runtime state (such as PID
|
|
files). It is created automatically. Note that the default,
|
|
<filename>/run/httpd</filename>, is deleted at boot time.
|
|
'';
|
|
};
|
|
|
|
virtualHosts = mkOption {
|
|
type = types.listOf (types.submodule (
|
|
{ options = import ./per-server-options.nix {
|
|
inherit lib;
|
|
forMainServer = false;
|
|
};
|
|
}));
|
|
default = [];
|
|
example = [
|
|
{ hostName = "foo";
|
|
documentRoot = "/data/webroot-foo";
|
|
}
|
|
{ hostName = "bar";
|
|
documentRoot = "/data/webroot-bar";
|
|
}
|
|
];
|
|
description = ''
|
|
Specification of the virtual hosts served by Apache. Each
|
|
element should be an attribute set specifying the
|
|
configuration of the virtual host. The available options
|
|
are the non-global options permissible for the main host.
|
|
'';
|
|
};
|
|
|
|
enableMellon = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to enable the mod_auth_mellon module.";
|
|
};
|
|
|
|
enablePHP = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to enable the PHP module.";
|
|
};
|
|
|
|
phpPackage = mkOption {
|
|
type = types.package;
|
|
default = pkgs.php;
|
|
defaultText = "pkgs.php";
|
|
description = ''
|
|
Overridable attribute of the PHP package to use.
|
|
'';
|
|
};
|
|
|
|
enablePerl = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to enable the Perl module (mod_perl).";
|
|
};
|
|
|
|
phpOptions = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
example =
|
|
''
|
|
date.timezone = "CET"
|
|
'';
|
|
description =
|
|
"Options appended to the PHP configuration file <filename>php.ini</filename>.";
|
|
};
|
|
|
|
multiProcessingModule = mkOption {
|
|
type = types.str;
|
|
default = "prefork";
|
|
example = "worker";
|
|
description =
|
|
''
|
|
Multi-processing module to be used by Apache. Available
|
|
modules are <literal>prefork</literal> (the default;
|
|
handles each request in a separate child process),
|
|
<literal>worker</literal> (hybrid approach that starts a
|
|
number of child processes each running a number of
|
|
threads) and <literal>event</literal> (a recent variant of
|
|
<literal>worker</literal> that handles persistent
|
|
connections more efficiently).
|
|
'';
|
|
};
|
|
|
|
maxClients = mkOption {
|
|
type = types.int;
|
|
default = 150;
|
|
example = 8;
|
|
description = "Maximum number of httpd processes (prefork)";
|
|
};
|
|
|
|
maxRequestsPerChild = mkOption {
|
|
type = types.int;
|
|
default = 0;
|
|
example = 500;
|
|
description =
|
|
"Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
|
|
};
|
|
|
|
sslCiphers = mkOption {
|
|
type = types.str;
|
|
default = "HIGH:!aNULL:!MD5:!EXP";
|
|
description = "Cipher Suite available for negotiation in SSL proxy handshake.";
|
|
};
|
|
|
|
sslProtocols = mkOption {
|
|
type = types.str;
|
|
default = "All -SSLv2 -SSLv3 -TLSv1";
|
|
example = "All -SSLv2 -SSLv3";
|
|
description = "Allowed SSL/TLS protocol versions.";
|
|
};
|
|
}
|
|
|
|
# Include the options shared between the main server and virtual hosts.
|
|
// (import ./per-server-options.nix {
|
|
inherit lib;
|
|
forMainServer = true;
|
|
});
|
|
|
|
};
|
|
|
|
|
|
###### implementation
|
|
|
|
config = mkIf config.services.httpd.enable {
|
|
|
|
assertions = [ { assertion = mainCfg.enableSSL == true
|
|
-> mainCfg.sslServerCert != null
|
|
&& mainCfg.sslServerKey != null;
|
|
message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
|
|
];
|
|
|
|
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);
|
|
|
|
users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
|
|
{ name = "wwwrun";
|
|
group = mainCfg.group;
|
|
description = "Apache httpd user";
|
|
uid = config.ids.uids.wwwrun;
|
|
});
|
|
|
|
users.groups = optionalAttrs (mainCfg.group == "wwwrun") (singleton
|
|
{ name = "wwwrun";
|
|
gid = config.ids.gids.wwwrun;
|
|
});
|
|
|
|
environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
|
|
|
|
services.httpd.phpOptions =
|
|
''
|
|
; Needed for PHP's mail() function.
|
|
sendmail_path = sendmail -t -i
|
|
|
|
; Don't advertise PHP
|
|
expose_php = off
|
|
'' + optionalString (config.time.timeZone != null) ''
|
|
|
|
; Apparently PHP doesn't use $TZ.
|
|
date.timezone = "${config.time.timeZone}"
|
|
'';
|
|
|
|
systemd.services.httpd =
|
|
{ description = "Apache HTTPD";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" "fs.target" ];
|
|
|
|
path =
|
|
[ httpd pkgs.coreutils pkgs.gnugrep ]
|
|
++ optional enablePHP pkgs.system-sendmail # Needed for PHP's mail() function.
|
|
++ concatMap (svc: svc.extraServerPath) allSubservices;
|
|
|
|
environment =
|
|
optionalAttrs enablePHP { PHPRC = phpIni; }
|
|
// optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; }
|
|
// (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
|
|
|
|
preStart =
|
|
''
|
|
mkdir -m 0750 -p ${mainCfg.stateDir}
|
|
[ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
|
|
|
|
mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
|
|
[ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
|
|
|
|
mkdir -m 0700 -p ${mainCfg.logDir}
|
|
|
|
# Get rid of old semaphores. These tend to accumulate across
|
|
# server restarts, eventually preventing it from restarting
|
|
# successfully.
|
|
for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
|
|
${pkgs.utillinux}/bin/ipcrm -s $i
|
|
done
|
|
|
|
# Run the startup hooks for the subservices.
|
|
for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
|
|
echo Running Apache startup hook $i...
|
|
$i
|
|
done
|
|
'';
|
|
|
|
serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
|
|
serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
|
|
serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful";
|
|
serviceConfig.Type = "forking";
|
|
serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid";
|
|
serviceConfig.Restart = "always";
|
|
serviceConfig.RestartSec = "5s";
|
|
};
|
|
|
|
};
|
|
}
|