1# NixOS module for lighttpd web server
2
3{ config, lib, pkgs, ... }:
4
5with lib;
6
7let
8
9 cfg = config.services.lighttpd;
10
11 # List of known lighttpd modules, ordered by how the lighttpd documentation
12 # recommends them being imported:
13 # http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
14 #
15 # Some modules are always imported and should not appear in the config:
16 # disallowedModules = [ "mod_indexfile" "mod_dirlisting" "mod_staticfile" ];
17 #
18 # Get full module list: "ls -1 $lighttpd/lib/*.so"
19 allKnownModules = [
20 "mod_rewrite"
21 "mod_redirect"
22 "mod_alias"
23 "mod_access"
24 "mod_auth"
25 "mod_status"
26 "mod_simple_vhost"
27 "mod_evhost"
28 "mod_userdir"
29 "mod_secdownload"
30 "mod_fastcgi"
31 "mod_proxy"
32 "mod_cgi"
33 "mod_ssi"
34 "mod_compress"
35 "mod_usertrack"
36 "mod_expire"
37 "mod_rrdtool"
38 "mod_accesslog"
39 # Remaining list of modules, order assumed to be unimportant.
40 "mod_cml"
41 "mod_dirlisting"
42 "mod_evasive"
43 "mod_extforward"
44 "mod_flv_streaming"
45 "mod_magnet"
46 "mod_mysql_vhost"
47 "mod_scgi"
48 "mod_setenv"
49 "mod_trigger_b4_dl"
50 "mod_webdav"
51 ];
52
53 maybeModuleString = moduleName:
54 if elem moduleName cfg.enableModules then ''"${moduleName}"'' else "";
55
56 modulesIncludeString = concatStringsSep ",\n"
57 (filter (x: x != "") (map maybeModuleString allKnownModules));
58
59 configFile = if cfg.configText != "" then
60 pkgs.writeText "lighttpd.conf" ''
61 ${cfg.configText}
62 ''
63 else
64 pkgs.writeText "lighttpd.conf" ''
65 server.document-root = "${cfg.document-root}"
66 server.port = ${toString cfg.port}
67 server.username = "lighttpd"
68 server.groupname = "lighttpd"
69
70 # As for why all modules are loaded here, instead of having small
71 # server.modules += () entries in each sub-service extraConfig snippet,
72 # read this:
73 #
74 # http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
75 # http://redmine.lighttpd.net/issues/2337
76 #
77 # Basically, lighttpd doesn't want to load (or even silently ignore) a
78 # module for a second time, and there is no way to check if a module has
79 # been loaded already. So if two services were to put the same module in
80 # server.modules += (), that would break the lighttpd configuration.
81 server.modules = (
82 ${modulesIncludeString}
83 )
84
85 # Logging (logs end up in systemd journal)
86 accesslog.use-syslog = "enable"
87 server.errorlog-use-syslog = "enable"
88
89 mimetype.assign = (
90 ".html" => "text/html",
91 ".htm" => "text/html",
92 ".txt" => "text/plain",
93 ".jpg" => "image/jpeg",
94 ".png" => "image/png",
95 ".css" => "text/css"
96 )
97
98 static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
99 index-file.names = ( "index.html" )
100
101 ${if cfg.mod_userdir then ''
102 userdir.path = "public_html"
103 '' else ""}
104
105 ${if cfg.mod_status then ''
106 status.status-url = "/server-status"
107 status.statistics-url = "/server-statistics"
108 status.config-url = "/server-config"
109 '' else ""}
110
111 ${cfg.extraConfig}
112 '';
113
114in
115
116{
117
118 options = {
119
120 services.lighttpd = {
121
122 enable = mkOption {
123 default = false;
124 type = types.bool;
125 description = ''
126 Enable the lighttpd web server.
127 '';
128 };
129
130 port = mkOption {
131 default = 80;
132 type = types.int;
133 description = ''
134 TCP port number for lighttpd to bind to.
135 '';
136 };
137
138 document-root = mkOption {
139 default = "/srv/www";
140 type = types.path;
141 description = ''
142 Document-root of the web server. Must be readable by the "lighttpd" user.
143 '';
144 };
145
146 mod_userdir = mkOption {
147 default = false;
148 type = types.bool;
149 description = ''
150 If true, requests in the form /~user/page.html are rewritten to take
151 the file public_html/page.html from the home directory of the user.
152 '';
153 };
154
155 enableModules = mkOption {
156 type = types.listOf types.str;
157 default = [ ];
158 example = [ "mod_cgi" "mod_status" ];
159 description = ''
160 List of lighttpd modules to enable. Sub-services take care of
161 enabling modules as needed, so this option is mainly for when you
162 want to add custom stuff to
163 <option>services.lighttpd.extraConfig</option> that depends on a
164 certain module.
165 '';
166 };
167
168 mod_status = mkOption {
169 default = false;
170 type = types.bool;
171 description = ''
172 Show server status overview at /server-status, statistics at
173 /server-statistics and list of loaded modules at /server-config.
174 '';
175 };
176
177 configText = mkOption {
178 default = "";
179 type = types.lines;
180 example = ''...verbatim config file contents...'';
181 description = ''
182 Overridable config file contents to use for lighttpd. By default, use
183 the contents automatically generated by NixOS.
184 '';
185 };
186
187 extraConfig = mkOption {
188 default = "";
189 type = types.lines;
190 description = ''
191 These configuration lines will be appended to the generated lighttpd
192 config file. Note that this mechanism does not work when the manual
193 <option>configText</option> option is used.
194 '';
195 };
196
197 };
198
199 };
200
201 config = mkIf cfg.enable {
202
203 assertions = [
204 { assertion = all (x: elem x allKnownModules) cfg.enableModules;
205 message = ''
206 One (or more) modules in services.lighttpd.enableModules are
207 unrecognized.
208
209 Known modules: ${toString allKnownModules}
210
211 services.lighttpd.enableModules: ${toString cfg.enableModules}
212 '';
213 }
214 ];
215
216 services.lighttpd.enableModules = mkMerge
217 [ (mkIf cfg.mod_status [ "mod_status" ])
218 (mkIf cfg.mod_userdir [ "mod_userdir" ])
219 # always load mod_accesslog so that we can log to the journal
220 [ "mod_accesslog" ]
221 ];
222
223 systemd.services.lighttpd = {
224 description = "Lighttpd Web Server";
225 after = [ "network.target" ];
226 wantedBy = [ "multi-user.target" ];
227 serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
228 # SIGINT => graceful shutdown
229 serviceConfig.KillSignal = "SIGINT";
230 };
231
232 users.extraUsers.lighttpd = {
233 group = "lighttpd";
234 description = "lighttpd web server privilege separation user";
235 uid = config.ids.uids.lighttpd;
236 };
237
238 users.extraGroups.lighttpd.gid = config.ids.gids.lighttpd;
239 };
240}