nixos/httpd: code cleanup

This commit is contained in:
Aaron Andersen 2020-01-24 20:36:48 -05:00
parent 596e0fcb39
commit 01ccb67598
8 changed files with 101 additions and 107 deletions

View file

@ -154,7 +154,7 @@ in
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{ hostName = "example.org";
adminAddr = "webmaster@example.org";

View file

@ -100,7 +100,7 @@ in
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "survey.example.org";

View file

@ -290,7 +290,7 @@ in
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "mediawiki.example.org";

View file

@ -140,7 +140,7 @@ in
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "moodle.example.org";

View file

@ -209,7 +209,7 @@ let
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
adminAddr = "webmaster@example.org";

View file

@ -113,7 +113,7 @@ in
};
virtualHost = mkOption {
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
example = literalExample ''
{
hostName = "zabbix.example.org";

View file

@ -4,21 +4,21 @@ with lib;
let
mainCfg = config.services.httpd;
cfg = config.services.httpd;
runtimeDir = "/run/httpd";
httpd = mainCfg.package.out;
pkg = cfg.package.out;
httpdConf = mainCfg.configFile;
httpdConf = cfg.configFile;
php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
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 = httpd; };
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = pkg; };
vhosts = attrValues mainCfg.virtualHosts;
vhosts = attrValues cfg.virtualHosts;
mkListenInfo = hostOpts:
if hostOpts.listen != [] then hostOpts.listen
@ -41,23 +41,18 @@ let
"mime" "autoindex" "negotiation" "dir"
"alias" "rewrite"
"unixd" "slotmem_shm" "socache_shmcb"
"mpm_${mainCfg.multiProcessingModule}"
"mpm_${cfg.multiProcessingModule}"
]
++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
++ (if cfg.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;
++ 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;
allDenied = "Require all denied";
allGranted = "Require all granted";
loggingConf = (if mainCfg.logFormat != "none" then ''
ErrorLog ${mainCfg.logDir}/error.log
loggingConf = (if cfg.logFormat != "none" then ''
ErrorLog ${cfg.logDir}/error.log
LogLevel notice
@ -66,7 +61,7 @@ let
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
CustomLog ${cfg.logDir}/access.log ${cfg.logFormat}
'' else ''
ErrorLog /dev/null
'');
@ -88,34 +83,36 @@ let
sslConf = ''
SSLSessionCache shmcb:${runtimeDir}/ssl_scache(512000)
<IfModule mod_ssl.c>
SSLSessionCache shmcb:${runtimeDir}/ssl_scache(512000)
Mutex posixsem
Mutex posixsem
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLProtocol ${mainCfg.sslProtocols}
SSLCipherSuite ${mainCfg.sslCiphers}
SSLHonorCipherOrder on
SSLProtocol ${cfg.sslProtocols}
SSLCipherSuite ${cfg.sslCiphers}
SSLHonorCipherOrder on
</IfModule>
'';
mimeConf = ''
TypesConfig ${httpd}/conf/mime.types
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 ${httpd}/conf/magic
MIMEMagicFile ${pkg}/conf/magic
</IfModule>
'';
mkVHostConf = hostOpts:
let
adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else mainCfg.adminAddr;
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);
@ -203,9 +200,9 @@ let
'') (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 cfg.logPerVirtualHost ''
ErrorLog ${cfg.logDir}/error-${hostOpts.hostName}.log
CustomLog ${cfg.logDir}/access-${hostOpts.hostName}.log ${hostOpts.logFormat}
''}
${optionalString (hostOpts.robotsEntries != "") ''
@ -217,7 +214,7 @@ let
<Directory "${documentRoot}">
Options Indexes FollowSymLinks
AllowOverride None
${allGranted}
Require all granted
</Directory>
${optionalString hostOpts.enableUserDir ''
@ -244,7 +241,7 @@ let
Alias ${elem.urlPath} ${elem.dir}/
<Directory ${elem.dir}>
Options +Indexes
${allGranted}
Require all granted
AllowOverride All
</Directory>
'';
@ -259,20 +256,20 @@ let
confFile = pkgs.writeText "httpd.conf" ''
ServerRoot ${httpd}
ServerRoot ${pkg}
ServerName ${config.networking.hostName}
DefaultRuntimeDir ${runtimeDir}/runtime
PidFile ${runtimeDir}/httpd.pid
${optionalString (mainCfg.multiProcessingModule != "prefork") ''
${optionalString (cfg.multiProcessingModule != "prefork") ''
# mod_cgid requires this.
ScriptSock ${runtimeDir}/cgisock
''}
<IfModule prefork.c>
MaxClients ${toString mainCfg.maxClients}
MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
MaxClients ${toString cfg.maxClients}
MaxRequestsPerChild ${toString cfg.maxRequestsPerChild}
</IfModule>
${let
@ -281,12 +278,12 @@ let
in concatStringsSep "\n" uniqueListen
}
User ${mainCfg.user}
Group ${mainCfg.group}
User ${cfg.user}
Group ${cfg.group}
${let
mkModule = module:
if isString module then { name = module; path = "${httpd}/modules/mod_${module}.so"; }
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
@ -296,37 +293,37 @@ let
AddHandler type-map var
<Files ~ "^\.ht">
${allDenied}
Require all denied
</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
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
${if enableSSL then sslConf else ""}
${sslConf}
# Fascist default - deny access to everything.
<Directory />
Options FollowSymLinks
AllowOverride None
${allDenied}
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>
${allGranted}
Require all granted
</Directory>
${mainCfg.extraConfig}
${cfg.extraConfig}
${concatMapStringsSep "\n" mkVHostConf vhosts}
'';
@ -334,7 +331,7 @@ let
# Generate the PHP configuration file. Should probably be factored
# out into a separate module.
phpIni = pkgs.runCommand "php.ini"
{ options = mainCfg.phpOptions;
{ options = cfg.phpOptions;
preferLocalBuild = true;
}
''
@ -367,17 +364,13 @@ in
(mkRemovedOptionModule [ "services" "httpd" "sslServerKey" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
];
###### interface
# interface
options = {
services.httpd = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable the Apache HTTP Server.";
};
enable = mkEnableOption "the Apache HTTP Server";
package = mkOption {
type = types.package;
@ -404,7 +397,7 @@ in
default = "";
description = ''
Configuration lines appended to the generated Apache
configuration file. Note that this mechanism may not work
configuration file. Note that this mechanism will not work
when <option>configFile</option> is overridden.
'';
};
@ -419,7 +412,7 @@ in
]
'';
description = ''
Additional Apache modules to be used. These can be
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
@ -458,8 +451,7 @@ in
type = types.str;
default = "wwwrun";
description = ''
User account under which httpd runs. The account is created
automatically if it doesn't exist.
User account under which httpd runs.
'';
};
@ -467,8 +459,7 @@ in
type = types.str;
default = "wwwrun";
description = ''
Group under which httpd runs. The account is created
automatically if it doesn't exist.
Group under which httpd runs.
'';
};
@ -476,15 +467,15 @@ in
type = types.path;
default = "/var/log/httpd";
description = ''
Directory for Apache's log files. It is created automatically.
Directory for Apache's log files. It is created automatically.
'';
};
virtualHosts = mkOption {
type = with types; attrsOf (submodule (import ./per-server-options.nix));
type = with types; attrsOf (submodule (import ./vhost-options.nix));
default = {
localhost = {
documentRoot = "${httpd}/htdocs";
documentRoot = "${pkg}/htdocs";
};
};
example = literalExample ''
@ -540,17 +531,18 @@ in
''
date.timezone = "CET"
'';
description =
"Options appended to the PHP configuration file <filename>php.ini</filename>.";
description = ''
Options appended to the PHP configuration file <filename>php.ini</filename>.
'';
};
multiProcessingModule = mkOption {
type = types.str;
type = types.enum [ "event" "prefork" "worker" ];
default = "prefork";
example = "worker";
description =
''
Multi-processing module to be used by Apache. Available
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
@ -572,8 +564,9 @@ in
type = types.int;
default = 0;
example = 500;
description =
"Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
description = ''
Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited.
'';
};
sslCiphers = mkOption {
@ -592,10 +585,9 @@ in
};
# implementation
###### implementation
config = mkIf config.services.httpd.enable {
config = mkIf cfg.enable {
assertions = [
{
@ -626,30 +618,30 @@ in
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);
'') (filterAttrs (name: hostOpts: hostOpts.servedFiles != []) cfg.virtualHosts);
users.users = optionalAttrs (mainCfg.user == "wwwrun") {
users.users = optionalAttrs (cfg.user == "wwwrun") {
wwwrun = {
group = mainCfg.group;
group = cfg.group;
description = "Apache httpd user";
uid = config.ids.uids.wwwrun;
};
};
users.groups = optionalAttrs (mainCfg.group == "wwwrun") {
users.groups = optionalAttrs (cfg.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;
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) mainCfg.virtualHosts);
}) (filterAttrs (name: hostOpts: hostOpts.enableACME) cfg.virtualHosts);
environment.systemPackages = [httpd];
environment.systemPackages = [ pkg ];
# required for "apachectl configtest"
environment.etc."httpd/httpd.conf".source = httpdConf;
@ -700,35 +692,37 @@ in
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.
[ pkg pkgs.coreutils pkgs.gnugrep ]
++ optional cfg.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"; };
optionalAttrs cfg.enablePHP { PHPRC = phpIni; }
// optionalAttrs cfg.enableMellon { LD_LIBRARY_PATH = "${pkgs.xmlsec}/lib"; };
preStart =
''
mkdir -m 0700 -p ${mainCfg.logDir}
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 ' ${mainCfg.user} ' | cut -f2 -d ' '); do
for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${cfg.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";
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";
};
};
};