summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/acme2.nix353
-rw-r--r--modules/default.nix1
-rw-r--r--modules/webapps/mastodon.nix2
-rw-r--r--modules/webapps/webstats/default.nix2
-rw-r--r--modules/websites/default.nix28
-rw-r--r--modules/websites/httpd-service-builder.nix68
-rw-r--r--modules/websites/php-application.nix31
7 files changed, 53 insertions, 432 deletions
diff --git a/modules/acme2.nix b/modules/acme2.nix
deleted file mode 100644
index b22e4ccc..00000000
--- a/modules/acme2.nix
+++ /dev/null
@@ -1,353 +0,0 @@
1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.security.acme2;
8
9 certOpts = { name, ... }: {
10 options = {
11 webroot = mkOption {
12 type = types.str;
13 example = "/var/lib/acme/acme-challenges";
14 description = ''
15 Where the webroot of the HTTP vhost is located.
16 <filename>.well-known/acme-challenge/</filename> directory
17 will be created below the webroot if it doesn't exist.
18 <literal>http://example.org/.well-known/acme-challenge/</literal> must also
19 be available (notice unencrypted HTTP).
20 '';
21 };
22
23 server = mkOption {
24 type = types.nullOr types.str;
25 default = null;
26 description = ''
27 ACME Directory Resource URI. Defaults to let's encrypt
28 production endpoint,
29 https://acme-v02.api.letsencrypt.org/directory, if unset.
30 '';
31 };
32
33 domain = mkOption {
34 type = types.str;
35 default = name;
36 description = "Domain to fetch certificate for (defaults to the entry name)";
37 };
38
39 email = mkOption {
40 type = types.nullOr types.str;
41 default = null;
42 description = "Contact email address for the CA to be able to reach you.";
43 };
44
45 user = mkOption {
46 type = types.str;
47 default = "root";
48 description = "User running the ACME client.";
49 };
50
51 group = mkOption {
52 type = types.str;
53 default = "root";
54 description = "Group running the ACME client.";
55 };
56
57 allowKeysForGroup = mkOption {
58 type = types.bool;
59 default = false;
60 description = ''
61 Give read permissions to the specified group
62 (<option>security.acme2.cert.&lt;name&gt;.group</option>) to read SSL private certificates.
63 '';
64 };
65
66 postRun = mkOption {
67 type = types.lines;
68 default = "";
69 example = "systemctl reload nginx.service";
70 description = ''
71 Commands to run after new certificates go live. Typically
72 the web server and other servers using certificates need to
73 be reloaded.
74
75 Executed in the same directory with the new certificate.
76 '';
77 };
78
79 plugins = mkOption {
80 type = types.listOf (types.enum [
81 "cert.der" "cert.pem" "chain.pem" "external.sh"
82 "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" "account_reg.json"
83 ]);
84 default = [ "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ];
85 description = ''
86 Plugins to enable. With default settings simp_le will
87 store public certificate bundle in <filename>fullchain.pem</filename>,
88 private key in <filename>key.pem</filename> and those two previous
89 files combined in <filename>full.pem</filename> in its state directory.
90 '';
91 };
92
93 directory = mkOption {
94 type = types.str;
95 readOnly = true;
96 default = "/var/lib/acme/${name}";
97 description = "Directory where certificate and other state is stored.";
98 };
99
100 extraDomains = mkOption {
101 type = types.attrsOf (types.nullOr types.str);
102 default = {};
103 example = literalExample ''
104 {
105 "example.org" = "/srv/http/nginx";
106 "mydomain.org" = null;
107 }
108 '';
109 description = ''
110 A list of extra domain names, which are included in the one certificate to be issued, with their
111 own server roots if needed.
112 '';
113 };
114 };
115 };
116
117in
118
119{
120
121 ###### interface
122 imports = [
123 (mkRemovedOptionModule [ "security" "acme2" "production" ] ''
124 Use security.acme2.server to define your staging ACME server URL instead.
125
126 To use the let's encrypt staging server, use security.acme2.server =
127 "https://acme-staging-v02.api.letsencrypt.org/directory".
128 ''
129 )
130 (mkRemovedOptionModule [ "security" "acme2" "directory"] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
131 (mkRemovedOptionModule [ "security" "acme" "preDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
132 (mkRemovedOptionModule [ "security" "acme" "activationDelay"] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
133 ];
134 options = {
135 security.acme2 = {
136
137 validMin = mkOption {
138 type = types.int;
139 default = 30 * 24 * 3600;
140 description = "Minimum remaining validity before renewal in seconds.";
141 };
142
143 renewInterval = mkOption {
144 type = types.str;
145 default = "weekly";
146 description = ''
147 Systemd calendar expression when to check for renewal. See
148 <citerefentry><refentrytitle>systemd.time</refentrytitle>
149 <manvolnum>7</manvolnum></citerefentry>.
150 '';
151 };
152
153 server = mkOption {
154 type = types.nullOr types.str;
155 default = null;
156 description = ''
157 ACME Directory Resource URI. Defaults to let's encrypt
158 production endpoint,
159 <literal>https://acme-v02.api.letsencrypt.org/directory</literal>, if unset.
160 '';
161 };
162
163 preliminarySelfsigned = mkOption {
164 type = types.bool;
165 default = true;
166 description = ''
167 Whether a preliminary self-signed certificate should be generated before
168 doing ACME requests. This can be useful when certificates are required in
169 a webserver, but ACME needs the webserver to make its requests.
170
171 With preliminary self-signed certificate the webserver can be started and
172 can later reload the correct ACME certificates.
173 '';
174 };
175
176 certs = mkOption {
177 default = { };
178 type = with types; attrsOf (submodule certOpts);
179 description = ''
180 Attribute set of certificates to get signed and renewed. Creates
181 <literal>acme-''${cert}.{service,timer}</literal> systemd units for
182 each certificate defined here. Other services can add dependencies
183 to those units if they rely on the certificates being present,
184 or trigger restarts of the service if certificates get renewed.
185 '';
186 example = literalExample ''
187 {
188 "example.com" = {
189 webroot = "/var/www/challenges/";
190 email = "foo@example.com";
191 extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; };
192 };
193 "bar.example.com" = {
194 webroot = "/var/www/challenges/";
195 email = "bar@example.com";
196 };
197 }
198 '';
199 };
200 };
201 };
202
203 ###### implementation
204 config = mkMerge [
205 (mkIf (cfg.certs != { }) {
206
207 systemd.services = let
208 services = concatLists servicesLists;
209 servicesLists = mapAttrsToList certToServices cfg.certs;
210 certToServices = cert: data:
211 let
212 lpath = "acme/${cert}";
213 rights = if data.allowKeysForGroup then "750" else "700";
214 cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ]
215 ++ optionals (data.email != null) [ "--email" data.email ]
216 ++ concatMap (p: [ "-f" p ]) data.plugins
217 ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains)
218 ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
219 acmeService = {
220 description = "Renew ACME Certificate for ${cert}";
221 after = [ "network.target" "network-online.target" ];
222 wants = [ "network-online.target" ];
223 # simp_le uses requests, which uses certifi under the hood,
224 # which doesn't respect the system trust store.
225 # At least in the acme test, we provision a fake CA, impersonating the LE endpoint.
226 # REQUESTS_CA_BUNDLE is a way to teach python requests to use something else
227 environment.REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
228 serviceConfig = {
229 Type = "oneshot";
230 # With RemainAfterExit the service is considered active even
231 # after the main process having exited, which means when it
232 # gets changed, the activation phase restarts it, meaning
233 # the permissions of the StateDirectory get adjusted
234 # according to the specified group
235 # Edit: Timers will never run because of this
236 # RemainAfterExit = true;
237 SuccessExitStatus = [ "0" "1" ];
238 User = data.user;
239 Group = data.group;
240 PrivateTmp = true;
241 StateDirectory = lpath;
242 StateDirectoryMode = rights;
243 ExecStartPre =
244 let
245 script = pkgs.writeScript "acme-pre-start" ''
246 #!${pkgs.runtimeShell} -e
247 mkdir -p '${data.webroot}/.well-known/acme-challenge'
248 chmod a+w '${data.webroot}/.well-known/acme-challenge'
249 #doesn't work for multiple concurrent runs
250 #chown -R '${data.user}:${data.group}' '${data.webroot}/.well-known/acme-challenge'
251 '';
252 in
253 "+${script}";
254 WorkingDirectory = "/var/lib/${lpath}";
255 ExecStart = "${pkgs.simp_le_0_17}/bin/simp_le ${escapeShellArgs cmdline}";
256 ExecStartPost =
257 let
258 script = pkgs.writeScript "acme-post-start" ''
259 #!${pkgs.runtimeShell} -e
260 ${data.postRun}
261 '';
262 in
263 "+${script}";
264 };
265
266 };
267 selfsignedService = {
268 description = "Create preliminary self-signed certificate for ${cert}";
269 path = [ pkgs.openssl ];
270 script =
271 ''
272 workdir="$(mktemp -d)"
273
274 # Create CA
275 openssl genrsa -des3 -passout pass:xxxx -out $workdir/ca.pass.key 2048
276 openssl rsa -passin pass:xxxx -in $workdir/ca.pass.key -out $workdir/ca.key
277 openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \
278 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com"
279 openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt
280
281 # Create key
282 openssl genrsa -des3 -passout pass:xxxx -out $workdir/server.pass.key 2048
283 openssl rsa -passin pass:xxxx -in $workdir/server.pass.key -out $workdir/server.key
284 openssl req -new -key $workdir/server.key -out $workdir/server.csr \
285 -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
286 openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \
287 -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \
288 -out $workdir/server.crt
289
290 # Copy key to destination
291 cp $workdir/server.key /var/lib/${lpath}/key.pem
292
293 # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates)
294 cat $workdir/{server.crt,ca.crt} > "/var/lib/${lpath}/fullchain.pem"
295
296 # Create full.pem for e.g. lighttpd
297 cat $workdir/{server.key,server.crt,ca.crt} > "/var/lib/${lpath}/full.pem"
298
299 # Give key acme permissions
300 chown '${data.user}:${data.group}' "/var/lib/${lpath}/"{key,fullchain,full}.pem
301 chmod ${rights} "/var/lib/${lpath}/"{key,fullchain,full}.pem
302 '';
303 serviceConfig = {
304 Type = "oneshot";
305 PrivateTmp = true;
306 StateDirectory = lpath;
307 User = data.user;
308 Group = data.group;
309 };
310 unitConfig = {
311 # Do not create self-signed key when key already exists
312 ConditionPathExists = "!/var/lib/${lpath}/key.pem";
313 };
314 };
315 in (
316 [ { name = "acme-${cert}"; value = acmeService; } ]
317 ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; }
318 );
319 servicesAttr = listToAttrs services;
320 in
321 servicesAttr;
322
323 # FIXME: this doesn't work for multiple users
324 systemd.tmpfiles.rules =
325 flip mapAttrsToList cfg.certs
326 (cert: data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}");
327
328 systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
329 ("acme-${cert}")
330 ({
331 description = "Renew ACME Certificate for ${cert}";
332 wantedBy = [ "timers.target" ];
333 timerConfig = {
334 OnCalendar = cfg.renewInterval;
335 Unit = "acme-${cert}.service";
336 Persistent = "yes";
337 AccuracySec = "5m";
338 RandomizedDelaySec = "1h";
339 };
340 })
341 );
342
343 systemd.targets.acme-selfsigned-certificates = mkIf cfg.preliminarySelfsigned {};
344 systemd.targets.acme-certificates = {};
345 })
346
347 ];
348
349 meta = {
350 maintainers = with lib.maintainers; [ abbradar fpletz globin ];
351 #doc = ./acme.xml;
352 };
353}
diff --git a/modules/default.nix b/modules/default.nix
index 98dc77d8..9ff6ea62 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -19,5 +19,4 @@
19 19
20 php-application = ./websites/php-application.nix; 20 php-application = ./websites/php-application.nix;
21 websites = ./websites; 21 websites = ./websites;
22 acme2 = ./acme2.nix;
23} // (if builtins.pathExists ./private then import ./private else {}) 22} // (if builtins.pathExists ./private then import ./private else {})
diff --git a/modules/webapps/mastodon.nix b/modules/webapps/mastodon.nix
index eed9e3f6..68531cf3 100644
--- a/modules/webapps/mastodon.nix
+++ b/modules/webapps/mastodon.nix
@@ -27,7 +27,7 @@ in
27 ''; 27 '';
28 }; 28 };
29 socketsPrefix = lib.mkOption { 29 socketsPrefix = lib.mkOption {
30 type = lib.types.string; 30 type = lib.types.str;
31 default = "live"; 31 default = "live";
32 description = '' 32 description = ''
33 The prefix to use for Mastodon sockets. 33 The prefix to use for Mastodon sockets.
diff --git a/modules/webapps/webstats/default.nix b/modules/webapps/webstats/default.nix
index e822645c..fe5f068d 100644
--- a/modules/webapps/webstats/default.nix
+++ b/modules/webapps/webstats/default.nix
@@ -23,7 +23,7 @@ in {
23 ''; 23 '';
24 }; 24 };
25 name = lib.mkOption { 25 name = lib.mkOption {
26 type = lib.types.string; 26 type = lib.types.str;
27 description = '' 27 description = ''
28 Domain name. Corresponds to the Apache file name and the 28 Domain name. Corresponds to the Apache file name and the
29 folder name in which the state will be saved. 29 folder name in which the state will be saved.
diff --git a/modules/websites/default.nix b/modules/websites/default.nix
index 767a7b23..3f46e65d 100644
--- a/modules/websites/default.nix
+++ b/modules/websites/default.nix
@@ -38,7 +38,7 @@ in
38 description = "Name of the httpd instance to assign this type to"; 38 description = "Name of the httpd instance to assign this type to";
39 }; 39 };
40 ips = mkOption { 40 ips = mkOption {
41 type = listOf string; 41 type = listOf str;
42 default = []; 42 default = [];
43 description = "ips to listen to"; 43 description = "ips to listen to";
44 }; 44 };
@@ -59,7 +59,7 @@ in
59 options = { 59 options = {
60 enable = mkEnableOption "Add default no-ssl vhost for this instance"; 60 enable = mkEnableOption "Add default no-ssl vhost for this instance";
61 host = mkOption { 61 host = mkOption {
62 type = string; 62 type = str;
63 description = "The hostname to use for this vhost"; 63 description = "The hostname to use for this vhost";
64 }; 64 };
65 root = mkOption { 65 root = mkOption {
@@ -68,7 +68,7 @@ in
68 description = "The root folder to serve"; 68 description = "The root folder to serve";
69 }; 69 };
70 indexFile = mkOption { 70 indexFile = mkOption {
71 type = string; 71 type = str;
72 default = "index.html"; 72 default = "index.html";
73 description = "The index file to show."; 73 description = "The index file to show.";
74 }; 74 };
@@ -79,8 +79,8 @@ in
79 description = "The fallback vhost that will be defined as first vhost in Apache"; 79 description = "The fallback vhost that will be defined as first vhost in Apache";
80 type = submodule { 80 type = submodule {
81 options = { 81 options = {
82 certName = mkOption { type = string; }; 82 certName = mkOption { type = str; };
83 hosts = mkOption { type = listOf string; }; 83 hosts = mkOption { type = listOf str; };
84 root = mkOption { type = nullOr path; }; 84 root = mkOption { type = nullOr path; };
85 extraConfig = mkOption { type = listOf lines; default = []; }; 85 extraConfig = mkOption { type = listOf lines; default = []; };
86 }; 86 };
@@ -91,7 +91,7 @@ in
91 description = "List of no ssl vhosts to define for Apache"; 91 description = "List of no ssl vhosts to define for Apache";
92 type = attrsOf (submodule { 92 type = attrsOf (submodule {
93 options = { 93 options = {
94 hosts = mkOption { type = listOf string; }; 94 hosts = mkOption { type = listOf str; };
95 root = mkOption { type = nullOr path; }; 95 root = mkOption { type = nullOr path; };
96 extraConfig = mkOption { type = listOf lines; default = []; }; 96 extraConfig = mkOption { type = listOf lines; default = []; };
97 }; 97 };
@@ -102,25 +102,25 @@ in
102 description = "List of vhosts to define for Apache"; 102 description = "List of vhosts to define for Apache";
103 type = attrsOf (submodule { 103 type = attrsOf (submodule {
104 options = { 104 options = {
105 certName = mkOption { type = string; }; 105 certName = mkOption { type = str; };
106 addToCerts = mkOption { 106 addToCerts = mkOption {
107 type = bool; 107 type = bool;
108 default = false; 108 default = false;
109 description = "Use these to certificates. Is ignored (considered true) if certMainHost is not null"; 109 description = "Use these to certificates. Is ignored (considered true) if certMainHost is not null";
110 }; 110 };
111 certMainHost = mkOption { 111 certMainHost = mkOption {
112 type = nullOr string; 112 type = nullOr str;
113 description = "Use that host as 'main host' for acme certs"; 113 description = "Use that host as 'main host' for acme certs";
114 default = null; 114 default = null;
115 }; 115 };
116 hosts = mkOption { type = listOf string; }; 116 hosts = mkOption { type = listOf str; };
117 root = mkOption { type = nullOr path; }; 117 root = mkOption { type = nullOr path; };
118 extraConfig = mkOption { type = listOf lines; default = []; }; 118 extraConfig = mkOption { type = listOf lines; default = []; };
119 }; 119 };
120 }); 120 });
121 }; 121 };
122 watchPaths = mkOption { 122 watchPaths = mkOption {
123 type = listOf string; 123 type = listOf str;
124 default = []; 124 default = [];
125 description = '' 125 description = ''
126 Paths to watch that should trigger a reload of httpd 126 Paths to watch that should trigger a reload of httpd
@@ -178,9 +178,9 @@ in
178 }; 178 };
179 toVhost = ips: vhostConf: { 179 toVhost = ips: vhostConf: {
180 enableSSL = true; 180 enableSSL = true;
181 sslServerCert = "${config.security.acme2.certs."${vhostConf.certName}".directory}/cert.pem"; 181 sslServerCert = "${config.security.acme.certs."${vhostConf.certName}".directory}/cert.pem";
182 sslServerKey = "${config.security.acme2.certs."${vhostConf.certName}".directory}/key.pem"; 182 sslServerKey = "${config.security.acme.certs."${vhostConf.certName}".directory}/key.pem";
183 sslServerChain = "${config.security.acme2.certs."${vhostConf.certName}".directory}/chain.pem"; 183 sslServerChain = "${config.security.acme.certs."${vhostConf.certName}".directory}/chain.pem";
184 logFormat = "combinedVhost"; 184 logFormat = "combinedVhost";
185 listen = map (ip: { inherit ip; port = 443; }) ips; 185 listen = map (ip: { inherit ip; port = 443; }) ips;
186 hostName = builtins.head vhostConf.hosts; 186 hostName = builtins.head vhostConf.hosts;
@@ -231,7 +231,7 @@ in
231 } 231 }
232 ) cfg.env; 232 ) cfg.env;
233 233
234 config.security.acme2.certs = let 234 config.security.acme.certs = let
235 typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env; 235 typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg.env;
236 flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v: 236 flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v:
237 attrValues v.vhostConfs 237 attrValues v.vhostConfs
diff --git a/modules/websites/httpd-service-builder.nix b/modules/websites/httpd-service-builder.nix
index d049202c..f0208ab5 100644
--- a/modules/websites/httpd-service-builder.nix
+++ b/modules/websites/httpd-service-builder.nix
@@ -11,8 +11,6 @@ let
11 11
12 httpd = mainCfg.package.out; 12 httpd = mainCfg.package.out;
13 13
14 version24 = !versionOlder httpd.version "2.4";
15
16 httpdConf = mainCfg.configFile; 14 httpdConf = mainCfg.configFile;
17 15
18 php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ }; 16 php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
@@ -26,10 +24,9 @@ let
26 else [{ip = "*"; port = 80;}]; 24 else [{ip = "*"; port = 80;}];
27 25
28 getListen = cfg: 26 getListen = cfg:
29 let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen; 27 if cfg.listen == []
30 in if list == [] 28 then defaultListen cfg
31 then defaultListen cfg 29 else cfg.listen;
32 else list;
33 30
34 listenToString = l: "${l.ip}:${toString l.port}"; 31 listenToString = l: "${l.ip}:${toString l.port}";
35 32
@@ -110,11 +107,10 @@ let
110 "auth_basic" "auth_digest" 107 "auth_basic" "auth_digest"
111 108
112 # Authentication: is the user who he claims to be? 109 # Authentication: is the user who he claims to be?
113 "authn_file" "authn_dbm" "authn_anon" 110 "authn_file" "authn_dbm" "authn_anon" "authn_core"
114 (if version24 then "authn_core" else "authn_alias")
115 111
116 # Authorization: is the user allowed access? 112 # Authorization: is the user allowed access?
117 "authz_user" "authz_groupfile" "authz_host" 113 "authz_user" "authz_groupfile" "authz_host" "authz_core"
118 114
119 # Other modules. 115 # Other modules.
120 "ext_filter" "include" "log_config" "env" "mime_magic" 116 "ext_filter" "include" "log_config" "env" "mime_magic"
@@ -122,14 +118,9 @@ let
122 "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs" 118 "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
123 "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling" 119 "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
124 "userdir" "alias" "rewrite" "proxy" "proxy_http" 120 "userdir" "alias" "rewrite" "proxy" "proxy_http"
125 ] 121 "unixd" "cache" "cache_disk" "slotmem_shm" "socache_shmcb"
126 ++ optionals version24 [
127 "mpm_${mainCfg.multiProcessingModule}" 122 "mpm_${mainCfg.multiProcessingModule}"
128 "authz_core" 123
129 "unixd"
130 "cache" "cache_disk"
131 "slotmem_shm"
132 "socache_shmcb"
133 # For compatibility with old configurations, the new module mod_access_compat is provided. 124 # For compatibility with old configurations, the new module mod_access_compat is provided.
134 "access_compat" 125 "access_compat"
135 ] 126 ]
@@ -138,19 +129,8 @@ let
138 ++ extraApacheModules; 129 ++ extraApacheModules;
139 130
140 131
141 allDenied = if version24 then '' 132 allDenied = "Require all denied";
142 Require all denied 133 allGranted = "Require all granted";
143 '' else ''
144 Order deny,allow
145 Deny from all
146 '';
147
148 allGranted = if version24 then ''
149 Require all granted
150 '' else ''
151 Order allow,deny
152 Allow from all
153 '';
154 134
155 135
156 loggingConf = (if mainCfg.logFormat != "none" then '' 136 loggingConf = (if mainCfg.logFormat != "none" then ''
@@ -183,9 +163,9 @@ let
183 163
184 164
185 sslConf = '' 165 sslConf = ''
186 SSLSessionCache ${if version24 then "shmcb" else "shm"}:${mainCfg.stateDir}/ssl_scache(512000) 166 SSLSessionCache shmcb:${mainCfg.stateDir}/ssl_scache(512000)
187 167
188 ${if version24 then "Mutex" else "SSLMutex"} posixsem 168 Mutex posixsem
189 169
190 SSLRandomSeed startup builtin 170 SSLRandomSeed startup builtin
191 SSLRandomSeed connect builtin 171 SSLRandomSeed connect builtin
@@ -325,9 +305,7 @@ let
325 305
326 ServerRoot ${httpd} 306 ServerRoot ${httpd}
327 307
328 ${optionalString version24 '' 308 DefaultRuntimeDir ${mainCfg.stateDir}/runtime
329 DefaultRuntimeDir ${mainCfg.stateDir}/runtime
330 ''}
331 309
332 PidFile ${mainCfg.stateDir}/httpd.pid 310 PidFile ${mainCfg.stateDir}/httpd.pid
333 311
@@ -361,7 +339,7 @@ let
361 ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; } 339 ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
362 ++ concatMap (svc: svc.extraModules) allSubservices 340 ++ concatMap (svc: svc.extraModules) allSubservices
363 ++ extraForeignModules; 341 ++ extraForeignModules;
364 in concatMapStrings load allModules 342 in concatMapStrings load (unique allModules)
365 } 343 }
366 344
367 AddHandler type-map var 345 AddHandler type-map var
@@ -393,14 +371,6 @@ let
393 # Generate directives for the main server. 371 # Generate directives for the main server.
394 ${perServerConf true mainCfg} 372 ${perServerConf true mainCfg}
395 373
396 # Always enable virtual hosts; it doesn't seem to hurt.
397 ${let
398 listen = concatMap getListen allHosts;
399 uniqueListen = uniqList {inputList = listen;};
400 directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen;
401 in optionalString (!version24) directives
402 }
403
404 ${let 374 ${let
405 makeVirtualHost = vhost: '' 375 makeVirtualHost = vhost: ''
406 <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}> 376 <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
@@ -663,7 +633,7 @@ in
663 message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; } 633 message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
664 ]; 634 ];
665 635
666 warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts); 636 warnings = map (cfg: "apache-httpd's extraSubservices option is deprecated. Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.") (lib.filter (cfg: cfg.extraSubservices != []) allHosts);
667 637
668 users.users = optionalAttrs (withUsers && mainCfg.user == "wwwrun") (singleton 638 users.users = optionalAttrs (withUsers && mainCfg.user == "wwwrun") (singleton
669 { name = "wwwrun"; 639 { name = "wwwrun";
@@ -686,7 +656,7 @@ in
686 656
687 ; Don't advertise PHP 657 ; Don't advertise PHP
688 expose_php = off 658 expose_php = off
689 '' + optionalString (!isNull config.time.timeZone) '' 659 '' + optionalString (config.time.timeZone != null) ''
690 660
691 ; Apparently PHP doesn't use $TZ. 661 ; Apparently PHP doesn't use $TZ.
692 date.timezone = "${config.time.timeZone}" 662 date.timezone = "${config.time.timeZone}"
@@ -713,10 +683,10 @@ in
713 '' 683 ''
714 mkdir -m 0750 -p ${mainCfg.stateDir} 684 mkdir -m 0750 -p ${mainCfg.stateDir}
715 [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir} 685 [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
716 ${optionalString version24 '' 686
717 mkdir -m 0750 -p "${mainCfg.stateDir}/runtime" 687 mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
718 [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime" 688 [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
719 ''} 689
720 mkdir -m 0700 -p ${mainCfg.logDir} 690 mkdir -m 0700 -p ${mainCfg.logDir}
721 691
722 # Get rid of old semaphores. These tend to accumulate across 692 # Get rid of old semaphores. These tend to accumulate across
diff --git a/modules/websites/php-application.nix b/modules/websites/php-application.nix
index 8ad7a0df..20e2a5dd 100644
--- a/modules/websites/php-application.nix
+++ b/modules/websites/php-application.nix
@@ -44,10 +44,15 @@ in
44 description = "Name of the socket to listen to. Defaults to app name if null"; 44 description = "Name of the socket to listen to. Defaults to app name if null";
45 }; 45 };
46 phpPool = mkOption { 46 phpPool = mkOption {
47 type = lines; 47 type = attrsOf str;
48 default = ""; 48 default = {};
49 description = "Pool configuration to append"; 49 description = "Pool configuration to append";
50 }; 50 };
51 phpEnv = mkOption {
52 type = attrsOf str;
53 default = {};
54 description = "Pool environment to append";
55 };
51 phpOptions = mkOption { 56 phpOptions = mkOption {
52 type = lines; 57 type = lines;
53 default = ""; 58 default = "";
@@ -135,7 +140,7 @@ in
135 services.phpApplication.phpListenPaths = mkOption { 140 services.phpApplication.phpListenPaths = mkOption {
136 type = attrsOf path; 141 type = attrsOf path;
137 default = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair 142 default = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
138 name "/run/phpfpm/${if icfg.phpListen == null then name else icfg.phpListen}.sock" 143 name config.services.phpfpm.pools."${name}".socket
139 ) cfg.apps; 144 ) cfg.apps;
140 readOnly = true; 145 readOnly = true;
141 description = '' 146 description = ''
@@ -162,17 +167,17 @@ in
162 167
163 services.phpfpm.pools = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair 168 services.phpfpm.pools = attrsets.mapAttrs' (name: icfg: attrsets.nameValuePair
164 name { 169 name {
165 listen = cfg.phpListenPaths."${name}"; 170 user = icfg.httpdUser;
166 extraConfig = '' 171 group = icfg.httpdUser;
167 user = ${icfg.httpdUser} 172 settings = {
168 group = ${icfg.httpdGroup} 173 "listen.owner" = icfg.httpdUser;
169 listen.owner = ${icfg.httpdUser} 174 "listen.group" = icfg.httpdGroup;
170 listen.group = ${icfg.httpdGroup} 175 "php_admin_value[open_basedir]" = builtins.concatStringsSep ":" ([icfg.app icfg.varDir] ++ icfg.phpWatchFiles ++ icfg.phpOpenbasedir);
171 ${optionalString (icfg.phpSession) '' 176 }
172 php_admin_value[session.save_path] = "${icfg.varDir}/phpSessions"''} 177 // optionalAttrs (icfg.phpSession) { "php_admin_value[session.save_path]" = "${icfg.varDir}/phpSessions"; }
173 php_admin_value[open_basedir] = "${builtins.concatStringsSep ":" ([icfg.app icfg.varDir] ++ icfg.phpWatchFiles ++ icfg.phpOpenbasedir)}" 178 // icfg.phpPool;
174 '' + icfg.phpPool;
175 phpOptions = config.services.phpfpm.phpOptions + icfg.phpOptions; 179 phpOptions = config.services.phpfpm.phpOptions + icfg.phpOptions;
180 inherit (icfg) phpEnv;
176 } 181 }
177 ) cfg.apps; 182 ) cfg.apps;
178 183