nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix

241 lines
6.6 KiB
Nix
Raw Normal View History

2013-03-03 14:24:44 +00:00
# NixOS module for lighttpd web server
{ config, lib, pkgs, ... }:
2013-03-03 14:24:44 +00:00
with lib;
2013-03-03 14:24:44 +00:00
let
cfg = config.services.lighttpd;
# List of known lighttpd modules, ordered by how the lighttpd documentation
# recommends them being imported:
# http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
#
# Some modules are always imported and should not appear in the config:
# disallowedModules = [ "mod_indexfile" "mod_dirlisting" "mod_staticfile" ];
#
# Get full module list: "ls -1 $lighttpd/lib/*.so"
allKnownModules = [
"mod_rewrite"
"mod_redirect"
"mod_alias"
"mod_access"
"mod_auth"
"mod_status"
"mod_simple_vhost"
"mod_evhost"
"mod_userdir"
"mod_secdownload"
"mod_fastcgi"
"mod_proxy"
"mod_cgi"
"mod_ssi"
"mod_compress"
"mod_usertrack"
"mod_expire"
"mod_rrdtool"
"mod_accesslog"
# Remaining list of modules, order assumed to be unimportant.
"mod_cml"
"mod_dirlisting"
"mod_evasive"
"mod_extforward"
"mod_flv_streaming"
"mod_magnet"
"mod_mysql_vhost"
"mod_scgi"
"mod_setenv"
"mod_trigger_b4_dl"
"mod_webdav"
];
maybeModuleString = moduleName:
if elem moduleName cfg.enableModules then ''"${moduleName}"'' else "";
modulesIncludeString = concatStringsSep ",\n"
(filter (x: x != "") (map maybeModuleString allKnownModules));
lighttpd: improve module handling lighttpd doesn't support loading a module more than once. If you attempt to load a module again, lighttpd prints an error message: (plugin.c.131) Cannot load plugin mod_cgi more than once, please fix your config (we may not accept such configs in future releases And it's not just the error message. The module isn't loaded (or is messed up somehow) so that neither sub-service will work properly after this. This is bad news for the current approach to sub-services, where each sub-service lists the needed modules in a server.modules += (...) block. When two sub-services need the same module we get the above issue. (And, AFAIK, there is no way to check if a module is already loaded either.) First I thought about an approach where each sub-service specifies the list of plugins it needs, and that a common server.modules = (...) list is built from the union of those lists. That would loosly couple the sub-services with the main lighttpd nixos module expression. But I think this is a bad idea because lighttpd module loading order matters[1], and the module order in the global server.modules = (...) list would be somewhat cumbersome to control. Here is an example: Sub-service A needs mod_fastcgi. Sub-service B needs mod_auth and mod_fastcgi. Note that mod_auth must be loaded *before* mod_fastcgi to take effect. The union of those modules may either be ["mod_auth" "mod_fastcgi"] or ["mod_fastcgi" "mod_auth"] depending on the evaluation order. The first order will work, the latter will not. So instead of the above, this commit moves the modules from service.modules += (...) snippets in each sub-service to a global server.modules = (...) list in the main lighttpd module expression. The module loading order is fixed and each module is included only if any of the sub-services that needs it is enabled. The downside to this approach is that sub-services need a (tiny) bit of change to the main lighttpd nixos module expression. But I think it is the only sane way to do it (as long as lighttpd is written the way it is). References: [1] http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails [2] http://redmine.lighttpd.net/issues/2337
2013-06-14 20:01:12 +01:00
configFile = if cfg.configText != "" then
pkgs.writeText "lighttpd.conf" ''
${cfg.configText}
''
else
pkgs.writeText "lighttpd.conf" ''
server.document-root = "${cfg.document-root}"
server.port = ${toString cfg.port}
server.username = "lighttpd"
server.groupname = "lighttpd"
lighttpd: improve module handling lighttpd doesn't support loading a module more than once. If you attempt to load a module again, lighttpd prints an error message: (plugin.c.131) Cannot load plugin mod_cgi more than once, please fix your config (we may not accept such configs in future releases And it's not just the error message. The module isn't loaded (or is messed up somehow) so that neither sub-service will work properly after this. This is bad news for the current approach to sub-services, where each sub-service lists the needed modules in a server.modules += (...) block. When two sub-services need the same module we get the above issue. (And, AFAIK, there is no way to check if a module is already loaded either.) First I thought about an approach where each sub-service specifies the list of plugins it needs, and that a common server.modules = (...) list is built from the union of those lists. That would loosly couple the sub-services with the main lighttpd nixos module expression. But I think this is a bad idea because lighttpd module loading order matters[1], and the module order in the global server.modules = (...) list would be somewhat cumbersome to control. Here is an example: Sub-service A needs mod_fastcgi. Sub-service B needs mod_auth and mod_fastcgi. Note that mod_auth must be loaded *before* mod_fastcgi to take effect. The union of those modules may either be ["mod_auth" "mod_fastcgi"] or ["mod_fastcgi" "mod_auth"] depending on the evaluation order. The first order will work, the latter will not. So instead of the above, this commit moves the modules from service.modules += (...) snippets in each sub-service to a global server.modules = (...) list in the main lighttpd module expression. The module loading order is fixed and each module is included only if any of the sub-services that needs it is enabled. The downside to this approach is that sub-services need a (tiny) bit of change to the main lighttpd nixos module expression. But I think it is the only sane way to do it (as long as lighttpd is written the way it is). References: [1] http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails [2] http://redmine.lighttpd.net/issues/2337
2013-06-14 20:01:12 +01:00
# As for why all modules are loaded here, instead of having small
# server.modules += () entries in each sub-service extraConfig snippet,
# read this:
#
# http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
# http://redmine.lighttpd.net/issues/2337
#
# Basically, lighttpd doesn't want to load (or even silently ignore) a
# module for a second time, and there is no way to check if a module has
# been loaded already. So if two services were to put the same module in
# server.modules += (), that would break the lighttpd configuration.
server.modules = (
${modulesIncludeString}
lighttpd: improve module handling lighttpd doesn't support loading a module more than once. If you attempt to load a module again, lighttpd prints an error message: (plugin.c.131) Cannot load plugin mod_cgi more than once, please fix your config (we may not accept such configs in future releases And it's not just the error message. The module isn't loaded (or is messed up somehow) so that neither sub-service will work properly after this. This is bad news for the current approach to sub-services, where each sub-service lists the needed modules in a server.modules += (...) block. When two sub-services need the same module we get the above issue. (And, AFAIK, there is no way to check if a module is already loaded either.) First I thought about an approach where each sub-service specifies the list of plugins it needs, and that a common server.modules = (...) list is built from the union of those lists. That would loosly couple the sub-services with the main lighttpd nixos module expression. But I think this is a bad idea because lighttpd module loading order matters[1], and the module order in the global server.modules = (...) list would be somewhat cumbersome to control. Here is an example: Sub-service A needs mod_fastcgi. Sub-service B needs mod_auth and mod_fastcgi. Note that mod_auth must be loaded *before* mod_fastcgi to take effect. The union of those modules may either be ["mod_auth" "mod_fastcgi"] or ["mod_fastcgi" "mod_auth"] depending on the evaluation order. The first order will work, the latter will not. So instead of the above, this commit moves the modules from service.modules += (...) snippets in each sub-service to a global server.modules = (...) list in the main lighttpd module expression. The module loading order is fixed and each module is included only if any of the sub-services that needs it is enabled. The downside to this approach is that sub-services need a (tiny) bit of change to the main lighttpd nixos module expression. But I think it is the only sane way to do it (as long as lighttpd is written the way it is). References: [1] http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails [2] http://redmine.lighttpd.net/issues/2337
2013-06-14 20:01:12 +01:00
)
# Logging (logs end up in systemd journal)
accesslog.use-syslog = "enable"
server.errorlog-use-syslog = "enable"
mimetype.assign = (
".html" => "text/html",
".htm" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png",
".css" => "text/css"
)
static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
index-file.names = ( "index.html" )
${if cfg.mod_userdir then ''
userdir.path = "public_html"
'' else ""}
${if cfg.mod_status then ''
status.status-url = "/server-status"
status.statistics-url = "/server-statistics"
status.config-url = "/server-config"
'' else ""}
${cfg.extraConfig}
'';
2013-03-03 14:24:44 +00:00
in
{
options = {
services.lighttpd = {
enable = mkOption {
default = false;
2015-06-15 17:10:26 +01:00
type = types.bool;
2013-03-03 14:24:44 +00:00
description = ''
Enable the lighttpd web server.
'';
};
port = mkOption {
default = 80;
type = types.int;
description = ''
TCP port number for lighttpd to bind to.
'';
};
document-root = mkOption {
default = "/srv/www";
type = types.path;
description = ''
Document-root of the web server. Must be readable by the "lighttpd" user.
2013-03-03 14:24:44 +00:00
'';
};
mod_userdir = mkOption {
default = false;
2015-06-15 17:10:26 +01:00
type = types.bool;
description = ''
If true, requests in the form /~user/page.html are rewritten to take
the file public_html/page.html from the home directory of the user.
'';
};
enableModules = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "mod_cgi" "mod_status" ];
description = ''
List of lighttpd modules to enable. Sub-services take care of
enabling modules as needed, so this option is mainly for when you
want to add custom stuff to
<option>services.lighttpd.extraConfig</option> that depends on a
certain module.
'';
};
mod_status = mkOption {
default = false;
2015-06-15 17:10:26 +01:00
type = types.bool;
description = ''
Show server status overview at /server-status, statistics at
/server-statistics and list of loaded modules at /server-config.
'';
};
2013-03-03 14:24:44 +00:00
configText = mkOption {
default = "";
type = types.lines;
example = ''...verbatim config file contents...'';
description = ''
Overridable config file contents to use for lighttpd. By default, use
the contents automatically generated by NixOS.
2013-03-03 14:24:44 +00:00
'';
};
extraConfig = mkOption {
default = "";
type = types.lines;
2013-03-03 14:24:44 +00:00
description = ''
These configuration lines will be appended to the generated lighttpd
config file. Note that this mechanism does not work when the manual
<option>configText</option> option is used.
2013-03-03 14:24:44 +00:00
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{ assertion = all (x: elem x allKnownModules) cfg.enableModules;
message = ''
One (or more) modules in services.lighttpd.enableModules are
unrecognized.
Known modules: ${toString allKnownModules}
services.lighttpd.enableModules: ${toString cfg.enableModules}
'';
}
];
services.lighttpd.enableModules = mkMerge
[ (mkIf cfg.mod_status [ "mod_status" ])
(mkIf cfg.mod_userdir [ "mod_userdir" ])
# always load mod_accesslog so that we can log to the journal
[ "mod_accesslog" ]
];
2013-03-03 14:24:44 +00:00
systemd.services.lighttpd = {
description = "Lighttpd Web Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
2013-03-03 14:24:44 +00:00
# SIGINT => graceful shutdown
serviceConfig.KillSignal = "SIGINT";
};
users.extraUsers.lighttpd = {
group = "lighttpd";
description = "lighttpd web server privilege separation user";
uid = config.ids.uids.lighttpd;
2013-03-03 14:24:44 +00:00
};
users.extraGroups.lighttpd.gid = config.ids.gids.lighttpd;
2013-03-03 14:24:44 +00:00
};
}