nixos/httpd: code cleanup

Changed files
+101 -107
nixos
modules
+1 -1
nixos/modules/services/monitoring/nagios.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{ hostName = "example.org";
adminAddr = "webmaster@example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{ hostName = "example.org";
adminAddr = "webmaster@example.org";
+1 -1
nixos/modules/services/web-apps/limesurvey.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{
hostName = "survey.example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "survey.example.org";
+1 -1
nixos/modules/services/web-apps/mediawiki.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{
hostName = "mediawiki.example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "mediawiki.example.org";
+1 -1
nixos/modules/services/web-apps/moodle.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{
hostName = "moodle.example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "moodle.example.org";
+1 -1
nixos/modules/services/web-apps/wordpress.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{
adminAddr = "webmaster@example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
adminAddr = "webmaster@example.org";
+1 -1
nixos/modules/services/web-apps/zabbix.nix
···
};
virtualHost = mkOption {
-
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
example = literalExample ''
{
hostName = "zabbix.example.org";
···
};
virtualHost = mkOption {
+
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "zabbix.example.org";
+95 -101
nixos/modules/services/web-servers/apache-httpd/default.nix
···
let
-
mainCfg = config.services.httpd;
runtimeDir = "/run/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; };
-
vhosts = attrValues mainCfg.virtualHosts;
mkListenInfo = hostOpts:
if hostOpts.listen != [] then hostOpts.listen
···
"mime" "autoindex" "negotiation" "dir"
"alias" "rewrite"
"unixd" "slotmem_shm" "socache_shmcb"
-
"mpm_${mainCfg.multiProcessingModule}"
]
-
++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
++ optional enableSSL "ssl"
++ optional enableUserDir "userdir"
-
++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
-
++ optional mainCfg.enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
-
++ optional mainCfg.enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
-
++ mainCfg.extraModules;
-
-
allDenied = "Require all denied";
-
allGranted = "Require all granted";
-
-
-
loggingConf = (if mainCfg.logFormat != "none" then ''
-
ErrorLog ${mainCfg.logDir}/error.log
LogLevel notice
···
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
-
CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
'' else ''
ErrorLog /dev/null
'');
···
sslConf = ''
-
SSLSessionCache shmcb:${runtimeDir}/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>
'';
mkVHostConf = hostOpts:
let
-
adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else mainCfg.adminAddr;
listen = filter (listen: !listen.ssl) (mkListenInfo hostOpts);
listenSSL = filter (listen: listen.ssl) (mkListenInfo hostOpts);
···
'') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
in
''
-
${optionalString mainCfg.logPerVirtualHost ''
-
ErrorLog ${mainCfg.logDir}/error-${hostOpts.hostName}.log
-
CustomLog ${mainCfg.logDir}/access-${hostOpts.hostName}.log ${hostOpts.logFormat}
''}
${optionalString (hostOpts.robotsEntries != "") ''
···
<Directory "${documentRoot}">
Options Indexes FollowSymLinks
AllowOverride None
-
${allGranted}
</Directory>
${optionalString hostOpts.enableUserDir ''
···
Alias ${elem.urlPath} ${elem.dir}/
<Directory ${elem.dir}>
Options +Indexes
-
${allGranted}
AllowOverride All
</Directory>
'';
···
confFile = pkgs.writeText "httpd.conf" ''
-
ServerRoot ${httpd}
ServerName ${config.networking.hostName}
DefaultRuntimeDir ${runtimeDir}/runtime
PidFile ${runtimeDir}/httpd.pid
-
${optionalString (mainCfg.multiProcessingModule != "prefork") ''
# mod_cgid requires this.
ScriptSock ${runtimeDir}/cgisock
''}
<IfModule prefork.c>
-
MaxClients ${toString mainCfg.maxClients}
-
MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
</IfModule>
${let
···
in concatStringsSep "\n" uniqueListen
}
-
User ${mainCfg.user}
-
Group ${mainCfg.group}
${let
mkModule = module:
-
if isString module then { name = module; path = "${httpd}/modules/mod_${module}.so"; }
else if isAttrs module then { inherit (module) name path; }
else throw "Expecting either a string or attribute set including a name and path.";
in
···
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>
-
${mainCfg.extraConfig}
${concatMapStringsSep "\n" mkVHostConf vhosts}
'';
···
# Generate the PHP configuration file. Should probably be factored
# out into a separate module.
phpIni = pkgs.runCommand "php.ini"
-
{ options = mainCfg.phpOptions;
preferLocalBuild = true;
}
''
···
(mkRemovedOptionModule [ "services" "httpd" "sslServerKey" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
];
-
###### 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 = "";
description = ''
Configuration lines appended to the generated Apache
-
configuration file. Note that this mechanism may not work
when <option>configFile</option> is overridden.
'';
};
···
]
'';
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
···
type = types.str;
default = "wwwrun";
description = ''
-
User account under which httpd runs. The account is created
-
automatically if it doesn't exist.
'';
};
···
type = types.str;
default = "wwwrun";
description = ''
-
Group under which httpd runs. The account is created
-
automatically if it doesn't exist.
'';
};
···
type = types.path;
default = "/var/log/httpd";
description = ''
-
Directory for Apache's log files. It is created automatically.
'';
};
virtualHosts = mkOption {
-
type = with types; attrsOf (submodule (import ./per-server-options.nix));
default = {
localhost = {
-
documentRoot = "${httpd}/htdocs";
};
};
example = literalExample ''
···
''
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
···
type = types.int;
default = 0;
example = 500;
-
description =
-
"Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
};
sslCiphers = mkOption {
···
};
-
###### implementation
-
-
config = mkIf config.services.httpd.enable {
assertions = [
{
···
warnings =
mapAttrsToList (name: hostOpts: ''
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.
-
'') (filterAttrs (name: hostOpts: hostOpts.servedFiles != []) mainCfg.virtualHosts);
-
users.users = optionalAttrs (mainCfg.user == "wwwrun") {
wwwrun = {
-
group = mainCfg.group;
description = "Apache httpd user";
uid = config.ids.uids.wwwrun;
};
};
-
users.groups = optionalAttrs (mainCfg.group == "wwwrun") {
wwwrun.gid = config.ids.gids.wwwrun;
};
security.acme.certs = mapAttrs (name: hostOpts: {
-
user = mainCfg.user;
-
group = mkDefault mainCfg.group;
-
email = if hostOpts.adminAddr != null then hostOpts.adminAddr else mainCfg.adminAddr;
webroot = hostOpts.acmeRoot;
extraDomains = genAttrs hostOpts.serverAliases (alias: null);
postRun = "systemctl reload httpd.service";
-
}) (filterAttrs (name: hostOpts: hostOpts.enableACME) mainCfg.virtualHosts);
-
environment.systemPackages = [httpd];
# required for "apachectl configtest"
environment.etc."httpd/httpd.conf".source = httpdConf;
···
after = [ "network.target" "fs.target" ] ++ map (hostOpts: "acme-selfsigned-${hostOpts.hostName}.service") vhostsACME;
path =
-
[ httpd pkgs.coreutils pkgs.gnugrep ]
-
++ optional mainCfg.enablePHP pkgs.system-sendmail; # Needed for PHP's mail() function.
environment =
-
optionalAttrs mainCfg.enablePHP { PHPRC = phpIni; }
-
// optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; };
preStart =
''
-
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
'';
-
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.Group = mainCfg.group;
-
serviceConfig.Type = "forking";
-
serviceConfig.PIDFile = "${runtimeDir}/httpd.pid";
-
serviceConfig.Restart = "always";
-
serviceConfig.RestartSec = "5s";
-
serviceConfig.RuntimeDirectory = "httpd httpd/runtime";
-
serviceConfig.RuntimeDirectoryMode = "0750";
};
};
···
let
+
cfg = config.services.httpd;
runtimeDir = "/run/httpd";
+
pkg = cfg.package.out;
+
httpdConf = cfg.configFile;
+
php = cfg.phpPackage.override { apacheHttpd = pkg.dev; /* otherwise it only gets .out */ };
phpMajorVersion = lib.versions.major (lib.getVersion php);
+
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = pkg; };
+
vhosts = attrValues cfg.virtualHosts;
mkListenInfo = hostOpts:
if hostOpts.listen != [] then hostOpts.listen
···
"mime" "autoindex" "negotiation" "dir"
"alias" "rewrite"
"unixd" "slotmem_shm" "socache_shmcb"
+
"mpm_${cfg.multiProcessingModule}"
]
+
++ (if cfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
++ optional enableSSL "ssl"
++ optional enableUserDir "userdir"
+
++ optional cfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
+
++ optional cfg.enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
+
++ optional cfg.enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
+
++ cfg.extraModules;
+
loggingConf = (if cfg.logFormat != "none" then ''
+
ErrorLog ${cfg.logDir}/error.log
LogLevel notice
···
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
+
CustomLog ${cfg.logDir}/access.log ${cfg.logFormat}
'' else ''
ErrorLog /dev/null
'');
···
sslConf = ''
+
<IfModule mod_ssl.c>
+
SSLSessionCache shmcb:${runtimeDir}/ssl_scache(512000)
+
Mutex posixsem
+
SSLRandomSeed startup builtin
+
SSLRandomSeed connect builtin
+
SSLProtocol ${cfg.sslProtocols}
+
SSLCipherSuite ${cfg.sslCiphers}
+
SSLHonorCipherOrder on
+
</IfModule>
'';
mimeConf = ''
+
TypesConfig ${pkg}/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 ${pkg}/conf/magic
</IfModule>
'';
mkVHostConf = hostOpts:
let
+
adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else cfg.adminAddr;
listen = filter (listen: !listen.ssl) (mkListenInfo hostOpts);
listenSSL = filter (listen: listen.ssl) (mkListenInfo hostOpts);
···
'') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
in
''
+
${optionalString cfg.logPerVirtualHost ''
+
ErrorLog ${cfg.logDir}/error-${hostOpts.hostName}.log
+
CustomLog ${cfg.logDir}/access-${hostOpts.hostName}.log ${hostOpts.logFormat}
''}
${optionalString (hostOpts.robotsEntries != "") ''
···
<Directory "${documentRoot}">
Options Indexes FollowSymLinks
AllowOverride None
+
Require all granted
</Directory>
${optionalString hostOpts.enableUserDir ''
···
Alias ${elem.urlPath} ${elem.dir}/
<Directory ${elem.dir}>
Options +Indexes
+
Require all granted
AllowOverride All
</Directory>
'';
···
confFile = pkgs.writeText "httpd.conf" ''
+
ServerRoot ${pkg}
ServerName ${config.networking.hostName}
DefaultRuntimeDir ${runtimeDir}/runtime
PidFile ${runtimeDir}/httpd.pid
+
${optionalString (cfg.multiProcessingModule != "prefork") ''
# mod_cgid requires this.
ScriptSock ${runtimeDir}/cgisock
''}
<IfModule prefork.c>
+
MaxClients ${toString cfg.maxClients}
+
MaxRequestsPerChild ${toString cfg.maxRequestsPerChild}
</IfModule>
${let
···
in concatStringsSep "\n" uniqueListen
}
+
User ${cfg.user}
+
Group ${cfg.group}
${let
mkModule = module:
+
if isString module then { name = module; path = "${pkg}/modules/mod_${module}.so"; }
else if isAttrs module then { inherit (module) name path; }
else throw "Expecting either a string or attribute set including a name and path.";
in
···
AddHandler type-map var
<Files ~ "^\.ht">
+
Require all denied
</Files>
${mimeConf}
${loggingConf}
${browserHacks}
+
Include ${pkg}/conf/extra/httpd-default.conf
+
Include ${pkg}/conf/extra/httpd-autoindex.conf
+
Include ${pkg}/conf/extra/httpd-multilang-errordoc.conf
+
Include ${pkg}/conf/extra/httpd-languages.conf
TraceEnable off
+
${sslConf}
# Fascist default - deny access to everything.
<Directory />
Options FollowSymLinks
AllowOverride None
+
Require all denied
</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>
+
Require all granted
</Directory>
+
${cfg.extraConfig}
${concatMapStringsSep "\n" mkVHostConf vhosts}
'';
···
# Generate the PHP configuration file. Should probably be factored
# out into a separate module.
phpIni = pkgs.runCommand "php.ini"
+
{ options = cfg.phpOptions;
preferLocalBuild = true;
}
''
···
(mkRemovedOptionModule [ "services" "httpd" "sslServerKey" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
];
+
# interface
options = {
services.httpd = {
+
enable = mkEnableOption "the Apache HTTP Server";
package = mkOption {
type = types.package;
···
default = "";
description = ''
Configuration lines appended to the generated Apache
+
configuration file. Note that this mechanism will not work
when <option>configFile</option> is overridden.
'';
};
···
]
'';
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
···
type = types.str;
default = "wwwrun";
description = ''
+
User account under which httpd runs.
'';
};
···
type = types.str;
default = "wwwrun";
description = ''
+
Group under which httpd runs.
'';
};
···
type = types.path;
default = "/var/log/httpd";
description = ''
+
Directory for Apache's log files. It is created automatically.
'';
};
virtualHosts = mkOption {
+
type = with types; attrsOf (submodule (import ./vhost-options.nix));
default = {
localhost = {
+
documentRoot = "${pkg}/htdocs";
};
};
example = literalExample ''
···
''
date.timezone = "CET"
'';
+
description = ''
+
Options appended to the PHP configuration file <filename>php.ini</filename>.
+
'';
};
multiProcessingModule = mkOption {
+
type = types.enum [ "event" "prefork" "worker" ];
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
···
type = types.int;
default = 0;
example = 500;
+
description = ''
+
Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited.
+
'';
};
sslCiphers = mkOption {
···
};
+
# implementation
+
config = mkIf cfg.enable {
assertions = [
{
···
warnings =
mapAttrsToList (name: hostOpts: ''
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.
+
'') (filterAttrs (name: hostOpts: hostOpts.servedFiles != []) cfg.virtualHosts);
+
users.users = optionalAttrs (cfg.user == "wwwrun") {
wwwrun = {
+
group = cfg.group;
description = "Apache httpd user";
uid = config.ids.uids.wwwrun;
};
};
+
users.groups = optionalAttrs (cfg.group == "wwwrun") {
wwwrun.gid = config.ids.gids.wwwrun;
};
security.acme.certs = mapAttrs (name: hostOpts: {
+
user = cfg.user;
+
group = mkDefault cfg.group;
+
email = if hostOpts.adminAddr != null then hostOpts.adminAddr else cfg.adminAddr;
webroot = hostOpts.acmeRoot;
extraDomains = genAttrs hostOpts.serverAliases (alias: null);
postRun = "systemctl reload httpd.service";
+
}) (filterAttrs (name: hostOpts: hostOpts.enableACME) cfg.virtualHosts);
+
environment.systemPackages = [ pkg ];
# required for "apachectl configtest"
environment.etc."httpd/httpd.conf".source = httpdConf;
···
after = [ "network.target" "fs.target" ] ++ map (hostOpts: "acme-selfsigned-${hostOpts.hostName}.service") vhostsACME;
path =
+
[ pkg pkgs.coreutils pkgs.gnugrep ]
+
++ optional cfg.enablePHP pkgs.system-sendmail; # Needed for PHP's mail() function.
environment =
+
optionalAttrs cfg.enablePHP { PHPRC = phpIni; }
+
// optionalAttrs cfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; };
preStart =
''
+
mkdir -m 0700 -p ${cfg.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 ' ${cfg.user} ' | cut -f2 -d ' '); do
${pkgs.utillinux}/bin/ipcrm -s $i
done
'';
+
serviceConfig = {
+
ExecStart = "@${pkg}/bin/httpd httpd -f ${httpdConf}";
+
ExecStop = "${pkg}/bin/httpd -f ${httpdConf} -k graceful-stop";
+
ExecReload = "${pkg}/bin/httpd -f ${httpdConf} -k graceful";
+
Group = cfg.group;
+
Type = "forking";
+
PIDFile = "${runtimeDir}/httpd.pid";
+
Restart = "always";
+
RestartSec = "5s";
+
RuntimeDirectory = "httpd httpd/runtime";
+
RuntimeDirectoryMode = "0750";
+
};
};
};
nixos/modules/services/web-servers/apache-httpd/per-server-options.nix nixos/modules/services/web-servers/apache-httpd/vhost-options.nix