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